Mahendra Kamble b9d65b5ef2 Add export system configuration
This change adds support for exporting system configuration. The
configuration of the entire system or individual subsystems, such as
iDRAC, BIOS, NIC, and RAID, can be exported.

Change-Id: Ia7db0223d72566cf4a4d013a35ef2782f273695f
Co-Authored-By: Sonali Borkar <sonaliborkar85@gmail.com>
2021-03-12 14:11:16 +00:00

249 lines
8.1 KiB
Python

# Copyright (c) 2020-2021 Dell Inc. or its subsidiaries.
#
# 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.resources import common as res_common
from sushy_oem_idrac.resources.manager import mappings as mgr_maps
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'))
export_system_configuration = res_common.ExportActionField(
lambda key, **kwargs: key.endswith(
'#OemManager.ExportSystemConfiguration'))
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
@property
def export_system_configuration_uri(self):
return self._actions.export_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_allowed_export_system_config_values(self):
"""Get the allowed values of export system configuration.
:returns: A set of allowed values.
"""
export_action = self._actions.export_system_configuration
allowed_values = export_action.allowed_values[
'Target@Redfish.AllowableValues']
return set([mgr_maps.EXPORT_CONFIG_VALUE_MAP[value] for value in
set(mgr_maps.EXPORT_CONFIG_VALUE_MAP).
intersection(allowed_values)])
def _export_system_configuration(self, target):
"""Export system configuration.
It exports system configuration for specified target
like NIC, BIOS, RAID.
:param target: Component of the system to export the
configuration from. Can be the entire system.
Valid values can be gotten from
`get_allowed_export_system_config_values`.
:returns: a response object containing configuration details for
the specified target.
:raises: InvalidParameterValueError on invalid target.
:raises: ExtensionError on failure to perform requested
operation
"""
valid_allowed_targets = self.get_allowed_export_system_config_values()
if target not in valid_allowed_targets:
raise sushy.exceptions.InvalidParameterValueError(
parameter='target', value=target,
valid_values=valid_allowed_targets)
target = mgr_maps.EXPORT_CONFIG_VALUE_MAP_REV[target]
action_data = {
'ShareParameters': {
'Target': target
},
'ExportFormat': "JSON"
}
try:
response = asynchronous.http_call(
self._conn,
'post',
self.export_system_configuration_uri,
data=action_data)
LOG.info("Successfully exported system configuration "
"for %(target)s", {'target': target})
return response
except (sushy.exceptions.ExtensionError,
sushy.exceptions.InvalidParameterValueError) as exc:
LOG.error('Dell OEM export system configuration failed : %s', exc)
raise
def get_extension(*args, **kwargs):
return DellManagerExtension