169 lines
5.2 KiB
Python
169 lines
5.2 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
|
|
}
|
|
|
|
@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 = 10
|
|
rebooted = False
|
|
|
|
while True:
|
|
try:
|
|
response = asynchronous.http_call(
|
|
self._conn, 'post',
|
|
self.import_system_configuration_uri,
|
|
data=action_data)
|
|
|
|
LOG.info("Set boot device to %(device)s via "
|
|
"Dell OEM magic spell", {'device': device})
|
|
|
|
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(60)
|
|
|
|
if not attempts:
|
|
LOG.error('Too many retries, bailing out.')
|
|
raise
|
|
|
|
attempts -= 1
|
|
|
|
|
|
def get_extension(*args, **kwargs):
|
|
return DellManagerExtension
|