Merge "migrate sushy_oem_idrac to sushy"

This commit is contained in:
Zuul 2025-03-20 13:36:45 +00:00 committed by Gerrit Code Review
commit 880ab20930
61 changed files with 6550 additions and 0 deletions

View File

@ -0,0 +1,7 @@
---
prelude: >
sushy-oem-idrac has been merged into this package, removing the need
to install it separately.
upgrade:
- |
You must uninstall sushy-oem-idrac as a separate package when upgrading.

View File

@ -27,8 +27,15 @@ packages =
sushy
[entry_points]
sushy.resources.manager.oems =
dell = sushy.oem.dell.resources.manager.manager:get_extension
sushy.resources.storage_controller.oems =
dell = sushy.oem.dell.resources.system.storage.controller:get_extension
sushy.resources.system.oems =
contoso = sushy.resources.oem.fake:get_extension
dell = sushy.oem.dell.resources.system.system:get_extension
sushy.resources.task.oems =
dell = sushy.oem.dell.resources.taskservice.task:get_extension
[codespell]
quiet-level = 4

0
sushy/oem/__init__.py Normal file
View File

View File

@ -0,0 +1,18 @@
# Copyright (c) 2021-2022 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.
from sushy.oem.dell.resources.manager.constants import * # noqa
from sushy.oem.dell.resources.system.constants import * # noqa
from sushy.oem.dell.resources.system.storage.constants import * # noqa
from sushy.oem.dell.resources.taskservice.constants import * # noqa

View File

@ -0,0 +1,76 @@
# 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 datetime import datetime
from datetime import timedelta
import logging
import time
from dateutil import parser
import sushy
LOG = logging.getLogger(__name__)
TASK_POLL_PERIOD = 1
def _to_datetime(retry_after_str):
if retry_after_str.isdigit():
# Retry-After: 120
return datetime.now() + timedelta(seconds=int(retry_after_str))
else:
# Retry-After: Fri, 31 Dec 1999 23:59:59 GMT
return parser.parse(retry_after_str)
def http_call(conn, method, *args, **kwargs):
handle = getattr(conn, method.lower())
sleep_for = kwargs.pop('sushy_task_poll_period', TASK_POLL_PERIOD)
response = handle(*args, **kwargs)
LOG.debug('Finished HTTP %s with args %s %s, response is '
'%d', method, args or '', kwargs, response.status_code)
location = None
while response.status_code == 202:
location = response.headers.get('Location', location)
if not location:
raise sushy.exceptions.ExtensionError(
error='Response %d to HTTP %s with args %s, kwargs %s '
'does not include Location: in '
'header' % (response.status_code, method.upper(),
args, kwargs))
retry_after = response.headers.get('Retry-After')
if retry_after:
retry_after = _to_datetime(retry_after)
sleep_for = max(0, (retry_after - datetime.now()).total_seconds())
LOG.debug('Sleeping for %d secs before retrying HTTP GET '
'%s', sleep_for, location)
time.sleep(sleep_for)
response = conn.get(location)
LOG.debug('Finished HTTP GET %s, response is '
'%d', location, response.status_code)
if response.status_code >= 400:
raise sushy.exceptions.ExtensionError(
error=f'HTTP {method.upper()} with args {args}, '
f'kwargs {kwargs} failed '
f'with code {response.status_code}')
return response

View File

@ -0,0 +1,20 @@
# Copyright (c) 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.
IDRAC_CONFIG_PENDING = 'LC068'
IDRAC_JOB_RUNNING = 'RAC0679'
NO_FOREIGN_CONFIG = 'STOR018'
INCOMPLETE_JOB_STATES = ['Scheduled',
'Running',
'Paused']

View File

View File

@ -0,0 +1,76 @@
# Copyright (c) 2022 SAP SE 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.
from sushy.resources import base
class DellAttributes(base.ResourceBase):
identity = base.Field('Id', required=True)
"""The DellAttributes resource identity string"""
name = base.Field('Name')
"""The name of the resource"""
description = base.Field('Description')
"""Human-readable description of the DellAttributes resource"""
_attribute_registry = base.Field('AttributeRegistry')
"""The Resource ID of the AttributeRegistry for this resource"""
attributes = base.Field('Attributes')
"""Vendor-specific key-value dict of effective DellAttributes
They cannot be updated directly.
To update use :py:func:`~set_attribute` or :py:func:`~set_attributes`
"""
def set_attribute(self, key, value):
"""Update an attribute
Attribute update is not immediate but requires system restart.
Committed attributes can be checked at :py:attr:`~pending_attributes`
property
:param key: Attribute name
:param value: Attribute value
"""
self.set_attributes({key: value})
def set_attributes(self, value):
"""Update many attributes at once
Attribute update is not immediate but requires system restart.
Committed attributes can be checked at :py:attr:`~pending_attributes`
property
:param value: Key-value pairs for attribute name and value
:param apply_time: When to update the attributes. Optional.
An :py:class:`sushy.ApplyTime` value.
"""
payload = {'Attributes': value}
result = self._conn.patch(self.path, data=payload)
if result.status_code in (200, 202):
for k, v in value.items():
self.attributes[k] = v
def get_attribute_registry(self, language='en'):
"""Get the Attribute Registry associated with this resource
:param language: RFC 5646 language code for Message Registries.
Indicates language of registry to be used. Defaults to 'en'.
:returns: the DellAttributes Attribute Registry
"""
return self._get_registry(self._attribute_registry,
language=language,
description='Dell attribute registry')

View File

@ -0,0 +1,161 @@
# 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 enum
class ExportTarget(enum.Enum):
"""Export system config action constants"""
ALL = 'ALL'
"""Export entire system configuration"""
BIOS = 'BIOS'
"""Export BIOS related configuration"""
IDRAC = 'IDRAC'
"""Export iDRAC related configuration"""
NIC = 'NIC'
"""Export NIC related configuration"""
RAID = 'RAID'
"""Export RAID related configuration"""
# Backward compatibility
EXPORT_TARGET_ALL = ExportTarget.ALL
EXPORT_TARGET_BIOS = ExportTarget.BIOS
EXPORT_TARGET_IDRAC = ExportTarget.IDRAC
EXPORT_TARGET_NIC = ExportTarget.NIC
EXPORT_TARGET_RAID = ExportTarget.RAID
class ResetType(enum.Enum):
"""iDRAC Reset reset type constants"""
GRACEFUL = 'Graceful'
"""Perform a graceful shutdown followed by a restart of the system"""
FORCE = 'Force'
"""Perform an immediate (non-graceful) shutdown, followed by a restart"""
# Backward compatibility
RESET_IDRAC_GRACEFUL_RESTART = ResetType.GRACEFUL
RESET_IDRAC_FORCE_RESTART = ResetType.FORCE
class ShutdownType(enum.Enum):
"""ImportSystemConfiguration ShutdownType values"""
GRACEFUL = 'Graceful'
"""Graceful shutdown for Import System Configuration
Will wait for the host up to 5 minutes to shut down before timing out. The
operating system can potentially deny or ignore the graceful shutdown
request.
"""
FORCED = 'Forced'
"""Forced shutdown for Import System Configuration
The host server will be powered off immediately. Should be used when it is
safe to power down the host.
"""
NO_REBOOT = 'NoReboot'
"""No reboot for Import System Configuration
No shutdown performed. Explicit reboot is necessary to apply changes.
"""
# Backward compatibility
IMPORT_SHUTDOWN_GRACEFUL = ShutdownType.GRACEFUL
IMPORT_SHUTDOWN_FORCED = ShutdownType.FORCED
IMPORT_SHUTDOWN_NO_REBOOT = ShutdownType.NO_REBOOT
class ExportUse(enum.Enum):
"""ExportUse in ExportSystemConfiguration"""
DEFAULT = 'Default'
"""Default export type
Leaves some attributes commented out and requires user to enable them
before they can be applied during import.
"""
CLONE = 'Clone'
"""Clone export type suitable for cloning a 'golden' configuration.
Compared to Default export type, more attributes are enabled and
storage settings adjusted to aid in cloning process.
"""
REPLACE = 'Replace'
"""Replace export type suited for replacing complete configuration.
Compared to Clone export type, most attributes are enabled and storage
settings adjusted to aid in the replace process.
"""
# Backward compatibility
EXPORT_USE_DEFAULT = ExportUse.DEFAULT
EXPORT_USE_CLONE = ExportUse.CLONE
EXPORT_USE_REPLACE = ExportUse.REPLACE
class IncludeInExport(enum.Enum):
"""IncludeInExport in ExportSystemConfiguration"""
DEFAULT = 'Default'
"""Default for what to include in export.
Does not include read-only attributes, and depending on Export Use,
passwords are marked as ****** (for Default) or are set to default password
values (for Clone and Replace).
"""
READ_ONLY = 'IncludeReadOnly'
"""Includes read-only attributes.
In addition to values included by Default option, this also includes
read-only attributes that cannot be changed via Import and are provided for
informational purposes only.
"""
PASSWORD_HASHES = 'IncludePasswordHashValues' # noqa:S105
"""Include password hashes.
When using Clone or Replace, include password hashes, instead of default
password. Can be used to replicate passwords across systems.
"""
READ_ONLY_PASSWORD_HASHES =\
'IncludeReadOnly,IncludePasswordHashValues' # noqa:S105
"""Includes both read-only attributes and password hashes.
INCLUDE_EXPORT_READ_ONLY and INCLUDE_EXPORT_PASSWORD_HASHES combined
"""
# Backward compatibility
INCLUDE_EXPORT_DEFAULT = IncludeInExport.DEFAULT
INCLUDE_EXPORT_READ_ONLY = IncludeInExport.READ_ONLY
INCLUDE_EXPORT_PASSWORD_HASHES = IncludeInExport.PASSWORD_HASHES
INCLUDE_EXPORT_READ_ONLY_PASSWORD_HASHES =\
IncludeInExport.READ_ONLY_PASSWORD_HASHES

View File

@ -0,0 +1,109 @@
# 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 secrets
from sushy import exceptions
from sushy.resources import base
from sushy.resources import common
from sushy.oem.dell.resources.manager import constants as mgr_cons
LOG = logging.getLogger(__name__)
class ForceActionField(base.CompositeField):
target_uri = base.Field('target', required=True)
allowed_values = base.Field('Force@Redfish.AllowableValues',
adapter=list)
class ActionsField(base.CompositeField):
reset_idrac = ForceActionField('#DelliDRACCardService.iDRACReset')
get_kvm_session = common.ActionField('#DelliDRACCardService.GetKVMSession')
class DelliDRACCardService(base.ResourceBase):
_actions = ActionsField('Actions')
identity = base.Field('Id', required=True)
def __init__(self, connector, identity, redfish_version=None,
registries=None):
"""A class representing a DelliDRACCardService.
:param connector: A Connector instance
:param identity: The identity of the DelliDRACCardService resource
:param redfish_version: The version of Redfish. Used to construct
the object according to schema of the given version.
:param registries: Dict of Redfish Message Registry objects to be
used in any resource that needs registries to parse messages.
"""
super().__init__(
connector, identity, redfish_version, registries)
def get_allowed_reset_idrac_values(self):
"""Get the allowed values for resetting the idrac.
:returns: A set of allowed values.
"""
reset_action = self._actions.reset_idrac
if not reset_action.allowed_values:
LOG.warning('Could not figure out the allowed values for the '
'reset idrac action for %s', self.identity)
return set(mgr_cons.ResetType)
return {v for v in mgr_cons.ResetType
if v.value in reset_action.allowed_values}
def reset_idrac(self):
"""Reset the iDRAC.
"""
reset_type = mgr_cons.ResetType.GRACEFUL
valid_resets = self.get_allowed_reset_idrac_values()
if reset_type not in valid_resets:
raise exceptions.InvalidParameterValueError(
parameter='value', value=reset_type, valid_values=valid_resets)
target_uri = self._actions.reset_idrac.target_uri
payload = {"Force": reset_type.value}
LOG.debug('Resetting the iDRAC %s ...', self.identity)
self._conn.post(target_uri, data=payload)
LOG.info('The iDRAC %s is being reset', self.identity)
def get_kvm_session(self):
"""Get temporary credentials for KVM session
The TempUsername and TempPassword fields can be used in the following
url template:
https://{host}/console?username={}&tempUsername={}&tempPassword={}
The username is the user used to generate these session-credentials.
:returns: Dict with the fields TempUsername and TempPassword as strings
None, if the API did not return any credentials, but did not
raise an error. When and why that should happen is unclear,
but specified in the API doc.
"""
target_uri = self._actions.get_kvm_session.target_uri
LOG.debug('Getting KVM session from iDRAC %s ...', self.identity)
# SessionTypeName: A random string value upto 32 bytes.
name = secrets.token_urlsafe(32)
data = {"SessionTypeName": name}
result = self._conn.post(target_uri, data=data)
if result.status_code in (200, 201):
return result.json()
return None

View File

@ -0,0 +1,56 @@
# 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
from sushy.resources import base
from sushy.oem.dell import constants
LOG = logging.getLogger(__name__)
class DellJobCollection(base.ResourceBase):
_JOB_EXPAND = '?$expand=.($levels=1)'
def __init__(self, connector, identity, redfish_version=None,
registries=None):
"""A class representing a DellJobCollection.
:param connector: A Connector instance
:param identity: The identity of the DellJobCollection resource
:param redfish_version: The version of Redfish. Used to construct
the object according to schema of the given version.
:param registries: Dict of Redfish Message Registry objects to be
used in any resource that needs registries to parse messages
"""
super().__init__(
connector, identity, redfish_version, registries)
def get_unfinished_jobs(self):
"""Get the unfinished jobs.
:returns: A list of unfinished jobs.
"""
job_expand_uri = f'{self._path}{self._JOB_EXPAND}'
unfinished_jobs = []
LOG.debug('Getting unfinished jobs...')
job_response = self._conn.get(job_expand_uri)
data = job_response.json()
for job in data['Members']:
if job['JobState'] in constants.INCOMPLETE_JOB_STATES:
unfinished_jobs.append(job['Id'])
LOG.info('Got unfinished jobs')
return unfinished_jobs

View File

@ -0,0 +1,59 @@
# 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
from sushy.resources import base
from sushy.resources import common
LOG = logging.getLogger(__name__)
class ActionsField(base.CompositeField):
delete_job_queue = common.ActionField("#DellJobService.DeleteJobQueue")
class DellJobService(base.ResourceBase):
_actions = ActionsField('Actions')
identity = base.Field('Id', required=True)
def __init__(self, connector, identity, redfish_version=None,
registries=None):
"""A class representing a DellJobService.
:param connector: A Connector instance
:param identity: The identity of the DellJobService resource
:param redfish_version: The version of Redfish. Used to construct
the object according to schema of the given version.
:param registries: Dict of Redfish Message Registry objects to be
used in any resource that needs registries to parse messages.
"""
super().__init__(
connector, identity, redfish_version, registries)
def delete_jobs(self, job_ids=['JID_CLEARALL']):
"""Delete the given jobs, or all jobs.
:param job_ids: a list of job ids to delete. Clearing all the
jobs may be accomplished using the keyword JID_CLEARALL
as the job_id.
"""
target_uri = self._actions.delete_job_queue.target_uri
for job_id in job_ids:
LOG.debug('Deleting the job %s', job_id)
payload = {'JobID': job_id}
self._conn.post(target_uri,
data=payload)
LOG.info('Deleted the job %s', job_id)

View File

@ -0,0 +1,79 @@
# 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
from sushy.resources import base
from sushy.resources import common
LOG = logging.getLogger(__name__)
class ActionsField(base.CompositeField):
remote_service_api_status = common.ActionField(
"#DellLCService.GetRemoteServicesAPIStatus")
class DellLCService(base.ResourceBase):
_actions = ActionsField('Actions')
_OK_STATUS_CODE = 200
_READY_STATUS = 'Ready'
identity = base.Field('Id', required=True)
def __init__(self, connector, identity, redfish_version=None,
registries=None):
"""A class representing a DellLCService.
:param connector: A Connector instance
:param identity: The identity of the DellLCService resource
:param redfish_version: The version of Redfish. Used to construct
the object according to schema of the given version.
:param registries: Dict of Redfish Message Registry objects to be
used in any resource that needs registries to parse messages.
"""
super().__init__(
connector, identity, redfish_version, registries)
def _is_remote_service_api_status_ready(self, status_field):
"""Checks remote service status field
:param status_field: Status field to check, e.g., LCStatus, RTStatus
:returns: True if response returned and status field is Ready,
otherwise False.
"""
target_uri = self._actions.remote_service_api_status.target_uri
response = self._conn.post(target_uri, data={})
if response.status_code != self._OK_STATUS_CODE:
return False
data = response.json()
return data[status_field] == self._READY_STATUS
def is_idrac_ready(self):
"""Indicates if the iDRAC is ready to accept commands.
:returns: A boolean value True/False based on remote service api status
response.
"""
LOG.debug('Checking to see if the iDRAC is ready...')
return self._is_remote_service_api_status_ready('LCStatus')
def is_realtime_ready(self):
"""Indicates if real-time operations are ready to be accepted.
:returns: True if ready to accept real-time operations, otherwise
false.
"""
LOG.debug('Checking to see if the real-time operations are ready...')
return self._is_remote_service_api_status_ready('RTStatus')

View File

@ -0,0 +1,681 @@
# 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 json
import logging
import subprocess
import time
from urllib.parse import urlparse
import sushy
from sushy.resources import base
from sushy.resources import common
from sushy.resources.oem import base as oem_base
from sushy.taskmonitor import TaskMonitor
from sushy import utils as sushy_utils
from sushy.oem.dell import asynchronous
from sushy.oem.dell import constants
from sushy.oem.dell.resources import attributes
from sushy.oem.dell.resources.manager import constants as mgr_cons
from sushy.oem.dell.resources.manager import idrac_card_service
from sushy.oem.dell.resources.manager import job_collection
from sushy.oem.dell.resources.manager import job_service
from sushy.oem.dell.resources.manager import lifecycle_service
from sushy.oem.dell import utils
LOG = logging.getLogger(__name__)
# System Configuration Tag Constant
_SYSTEM_CONFIG_TAG = "SystemConfiguration"
# Response Code Constant
_RESPONSE_OK_CODE = 200
# Structure {'Component FQDD': (tuple of beginning of Attribute keys)}
_DESTRUCTIVE_CONF_KEYS = {
'iDRAC.Embedded.1':
('IPv4Static', 'IPv6Static', 'IPv4.1#Enable', 'IPv4.1#DHCPEnable',
'IPv6.1#Enable', 'IPv6.1#AutoConfig')}
class SharedParameters(base.CompositeField):
allowed_target_values = base.Field('Target@Redfish.AllowableValues')
class ExportActionField(common.ActionField):
shared_parameters = SharedParameters('ShareParameters')
allowed_export_use_values = base.Field(
'ExportUse@Redfish.AllowableValues', adapter=list)
allowed_include_in_export_values = base.Field(
'IncludeInExport@Redfish.AllowableValues', adapter=list)
class ImportActionField(common.ActionField):
allowed_shutdown_type_values = base.Field(
'ShutdownType@Redfish.AllowableValues', adapter=list)
class DellManagerActionsField(base.CompositeField):
import_system_configuration = ImportActionField(
lambda key, **kwargs: key.endswith(
'#OemManager.ImportSystemConfiguration'))
export_system_configuration = 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 = 35
RETRY_DELAY = 15
_IDRAC_IS_READY_RETRIES = 96
_IDRAC_IS_READY_RETRY_DELAY_SEC = 10
@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
@property
@sushy_utils.cache_it
def idrac_card_service(self):
"""Reference to `DelliDRACCardService` instance of this manager.
"""
path = sushy_utils.get_sub_resource_path_by(
self, ["Links", "Oem", "Dell", "DelliDRACCardService"],
is_collection=False)
return idrac_card_service.DelliDRACCardService(
self._conn, path, self.redfish_version, self.registries)
@property
@sushy_utils.cache_it
def lifecycle_service(self):
"""Property to reference `DellLCService` instance of this manager.
"""
path = sushy_utils.get_sub_resource_path_by(
self, ["Links", "Oem", "Dell", "DellLCService"],
is_collection=False)
return lifecycle_service.DellLCService(
self._conn, path, self.redfish_version, self.registries)
@property
@sushy_utils.cache_it
def job_service(self):
"""Property to reference `DellJobService` instance of this manager.
"""
path = sushy_utils.get_sub_resource_path_by(
self, ["Links", "Oem", "Dell", "DellJobService"],
is_collection=False)
return job_service.DellJobService(
self._conn, path, self.redfish_version, self.registries)
@property
@sushy_utils.cache_it
def job_collection(self):
"""Property to reference `DellJobService` instance of this manager.
"""
path = sushy_utils.get_sub_resource_path_by(
self, ["Links", "Oem", "Dell", "Jobs"], is_collection=False)
return job_collection.DellJobCollection(
self._conn, path, self.redfish_version, self.registries)
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.
:param manager: Manager of OEM extension. Optional.
:param system: System of OEM extension. Optional.
: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=f'Unknown or unsupported device {device}')
idrac_media = idrac_media % (
manager.identity if manager else self._parent_resource.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.HTTPError 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 []
found = False
for error in errors:
message_id = error.get('MessageId')
LOG.warning('iDRAC error: %s',
error.get('Message', 'Unknown error'))
if constants.IDRAC_CONFIG_PENDING in message_id:
found = True
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 constants.IDRAC_JOB_RUNNING in message_id:
found = True
pass
else:
if found:
time.sleep(self.RETRY_DELAY)
else:
raise
if not attempts:
LOG.error('Too many (%d) retries, bailing '
'out.', self.RETRY_COUNT)
raise
attempts -= 1
def get_allowed_export_target_values(self):
"""Get the allowed targets of export system configuration.
:returns: A set of allowed values.
"""
export_action = self._actions.export_system_configuration
allowed_values = export_action.shared_parameters.allowed_target_values
if not allowed_values:
LOG.warning('Could not figure out the allowed values for the '
'target of export system configuration at %s',
self.path)
return set(mgr_cons.ExportTarget)
return {v for v in mgr_cons.ExportTarget if v.value in allowed_values}
def get_allowed_export_use_values(self):
"""Get allowed export use values of export system configuration.
:returns: A set of allowed export use values.
"""
export_action = self._actions.export_system_configuration
allowed_values = export_action.allowed_export_use_values
if not allowed_values:
LOG.warning('Could not figure out the allowed values for the '
'export use of export system configuration at %s',
self.path)
return set(mgr_cons.ExportUse)
return {v for v in mgr_cons.ExportUse if v.value in allowed_values}
def get_allowed_include_in_export_values(self):
"""Get allowed include in export values of export system configuration.
:returns: A set of allowed include in export values.
"""
export_action = self._actions.export_system_configuration
allowed_values = export_action.allowed_include_in_export_values
if not allowed_values:
LOG.warning('Could not figure out the allowed values for the '
'include in export of export system configuration at '
'%s', self.path)
return set(mgr_cons.IncludeInExport)
return {v for v in mgr_cons.IncludeInExport
if v.value in allowed_values}
def _export_system_configuration(
self, target, export_use=mgr_cons.ExportUse.DEFAULT,
include_in_export=mgr_cons.IncludeInExport.DEFAULT):
"""Export system configuration.
It exports system configuration for specified target like NIC, BIOS,
RAID and allows to configure purpose for export and what to include.
: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`.
:param export_use: Export use. Optional, defaults to "Default".
Valid values can be gotten from `get_allowed_export_use_values`.
:param include_in_export: What to include in export. Optional. Defaults
to "Default". Valid values can be gotten from
`get_allowed_include_in_export_values`.
:returns: Response object containing configuration details.
:raises: InvalidParameterValueError on invalid target.
:raises: ExtensionError on failure to perform requested
operation
"""
valid_allowed_targets = self.get_allowed_export_target_values()
if target not in valid_allowed_targets:
raise sushy.exceptions.InvalidParameterValueError(
parameter='target', value=target,
valid_values=valid_allowed_targets)
allowed_export_use = self.get_allowed_export_use_values()
if export_use not in allowed_export_use:
raise sushy.exceptions.InvalidParameterValueError(
parameter='export_use', value=export_use,
valid_values=allowed_export_use)
allowed_include_in_export = self.get_allowed_include_in_export_values()
if include_in_export not in allowed_include_in_export:
# Check if value contains comma and validate each item separately
# Older iDRACs used to include comma separated option in
# AllowableValues but got removed in newer versions violating
# AllowableValues validation logic.
all_items_valid = True
if not isinstance(include_in_export, mgr_cons.IncludeInExport):
all_items_valid = False
else:
items = include_in_export.value.split(',')
for item in items:
if (mgr_cons.IncludeInExport(item)
not in allowed_include_in_export):
all_items_valid = False
break
if not all_items_valid:
raise sushy.exceptions.InvalidParameterValueError(
parameter='include_in_export', value=include_in_export,
valid_values=allowed_include_in_export)
target = mgr_cons.ExportTarget(target).value
export_use = mgr_cons.ExportUse(export_use).value
include_in_export = mgr_cons.IncludeInExport(include_in_export).value
action_data = {
'ShareParameters': {
'Target': target
},
'ExportFormat': "JSON",
'ExportUse': export_use,
'IncludeInExport': include_in_export
}
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 export_system_configuration(self, include_destructive_fields=True):
"""Export system configuration.
Exports ALL targets for cloning and includes password hashes and
read-only attributes.
:param include_destructive_fields: Whether includes settings such as
iDRAC static IP address that could lead to losing access to iDRAC
if importing this configuration into another system. Default to
True for backward compatibility. False recommended if unsure.
:returns: Response object containing configuration details.
:raises: InvalidParameterValueError on invalid target.
:raises: ExtensionError on failure to perform requested
operation
"""
include_in_export = mgr_cons.IncludeInExport.READ_ONLY_PASSWORD_HASHES
response = self._export_system_configuration(
mgr_cons.ExportTarget.ALL,
export_use=mgr_cons.ExportUse.CLONE,
include_in_export=include_in_export)
if (response.status_code == _RESPONSE_OK_CODE
and not include_destructive_fields):
conf = response.json()
if _SYSTEM_CONFIG_TAG in conf.keys():
for fqdd, values in _DESTRUCTIVE_CONF_KEYS.items():
for comp in conf[_SYSTEM_CONFIG_TAG]['Components']:
if comp['FQDD'] == fqdd:
attributes_copy = comp['Attributes'].copy()
for child in comp['Attributes']:
if child.get('Name').startswith(values):
attributes_copy.remove(child)
comp['Attributes'] = attributes_copy
response._content = json.dumps(conf).encode()
return response
def get_pxe_port_macs_bios(self, ethernet_interfaces_mac):
"""Get a list of pxe port MAC addresses for BIOS.
:param ethernet_interfaces_mac: Dictionary of ethernet interfaces.
:returns: List of pxe port MAC addresses.
:raises: ExtensionError on failure to perform requested operation.
"""
pxe_port_macs = []
# Get NIC configuration
nic_settings = self._export_system_configuration(
target=mgr_cons.ExportTarget.NIC)
if nic_settings.status_code != _RESPONSE_OK_CODE:
error = (('An error occurred when attempting to export '
'the system configuration. Status code: %(code), '
'Error details: %(err)'),
{'code': nic_settings.status_code,
'err': nic_settings.__dict__})
LOG.error(error)
raise sushy.exceptions.ExtensionError(error=error)
# Parse the exported system configuration for the NIC
# ports that are set to PXE boot
json_data = nic_settings.json()
if _SYSTEM_CONFIG_TAG in json_data.keys():
for root in json_data[_SYSTEM_CONFIG_TAG]['Components']:
nic_id = root['FQDD']
for child in root['Attributes']:
if child.get('Name') == "LegacyBootProto":
if child['Value'] == "PXE":
mac_address = ethernet_interfaces_mac[nic_id]
pxe_port_macs.append(mac_address)
return pxe_port_macs
else:
error = ('Failed to get system configuration from response')
LOG.error(error)
raise sushy.exceptions.ExtensionError(error=error)
def get_allowed_import_shutdown_type_values(self):
"""Get the allowed shutdown types of import system configuration.
:returns: A set of allowed shutdown type values.
"""
import_action = self._actions.import_system_configuration
allowed_values = import_action.allowed_shutdown_type_values
if not allowed_values:
LOG.warning('Could not figure out the allowed values for the '
'shutdown type of import system configuration at %s',
self.path)
return set(mgr_cons.ShutdownType)
return {v for v in mgr_cons.ShutdownType
if v.value in allowed_values}
def import_system_configuration(self, import_buffer):
"""Imports system configuration.
Caller needs to handle system reboot separately.
:param import_buffer: Configuration data to be imported.
:returns: Task monitor instance to watch for task completion
"""
action_data = dict(self.ACTION_DATA, ImportBuffer=import_buffer)
# Caller needs to handle system reboot separately to preserve
# one-time boot settings.
shutdown_type = mgr_cons.ShutdownType.NO_REBOOT
allowed_shutdown_types = self.get_allowed_import_shutdown_type_values()
if shutdown_type not in allowed_shutdown_types:
raise sushy.exceptions.InvalidParameterValueError(
parameter='shutdown_type', value=shutdown_type,
valid_values=allowed_shutdown_types)
action_data['ShutdownType'] = shutdown_type.value
response = self._conn.post(self.import_system_configuration_uri,
data=action_data)
return TaskMonitor.from_response(
self._conn, response, self.import_system_configuration_uri)
def reset_idrac(self, wait=True, ready_wait_time=60):
"""Reset the iDRAC and wait for it to become ready.
:param wait: Whether to return immediately or wait for iDRAC to
become operational.
:param ready_wait_time: Amount of time in seconds to wait before
starting to check on the iDRAC's status.
"""
self.idrac_card_service.reset_idrac()
if not wait:
return
host = urlparse(self._conn._url).netloc
LOG.debug("iDRAC %(host)s was reset, "
"waiting for return to operational state", {'host': host})
self._wait_for_idrac(host, ready_wait_time)
self._wait_until_idrac_is_ready(host, self._IDRAC_IS_READY_RETRIES,
self._IDRAC_IS_READY_RETRY_DELAY_SEC)
@property
def attributes(self):
paths = sushy_utils.get_sub_resource_path_by(
self, ["Links", "Oem", "Dell", "DellAttributes"],
is_collection=True)
for path in paths:
yield attributes.DellAttributes(self._conn, path,
self.redfish_version,
self.registries)
def _wait_for_idrac_state(self, host, alive=True, ping_count=3,
retries=24):
"""Wait for iDRAC to become pingable or not pingable.
:param host: Hostname or IP of the iDRAC interface.
:param alive: True for pingable state and False for not pingable
state.
:param ping_count: Number of consecutive ping results, per
'alive', for success.
:param retries: Number of ping retries.
:returns: True on reaching specified host ping state; otherwise,
False.
"""
if alive:
ping_type = "pingable"
else:
ping_type = "not pingable"
LOG.debug("Waiting for iDRAC %(host)s to become %(ping_type)s",
{'host': host, 'ping_type': ping_type})
response_count = 0
while retries > 0:
response = self._ping_host(host)
retries -= 1
if response == alive:
response_count += 1
LOG.debug("iDRAC %(host)s is %(ping_type)s, "
"count=%(response_count)s",
{'host': host, 'ping_type': ping_type,
'response_count': response_count})
if response_count == ping_count:
LOG.debug("Reached specified %(alive)s count for iDRAC "
"%(host)s", {'alive': alive, 'host': host})
return True
else:
response_count = 0
if alive:
LOG.debug("iDRAC %(host)s is still not pingable",
{'host': host})
else:
LOG.debug("iDRAC %(host)s is still pingable",
{'host': host})
time.sleep(10)
return False
def _wait_for_idrac(self, host, post_pingable_wait_time):
"""Wait for iDRAC to transition from unpingable to pingable.
:param host: Hostname or IP of the iDRAC interface.
:param post_pingable_wait_time: Amount of time in seconds to
wait after the host becomes pingable.
:raises: ExtensionError on failure to perform requested
operation.
"""
state_reached = self._wait_for_idrac_state(
host, alive=False, ping_count=2, retries=24)
if not state_reached:
error_msg = ("Timed out waiting iDRAC %(host)s to become not "
"pingable", {'host': host})
LOG.error(error_msg)
raise sushy.exceptions.ExtensionError(error=error_msg)
LOG.debug("iDRAC %(host)s has become not pingable", {'host': host})
state_reached = self._wait_for_idrac_state(host, alive=True,
ping_count=3, retries=24)
if not state_reached:
error_msg = ("Timed out waiting iDRAC %(host)s to become pingable",
{'host': host})
LOG.error(error_msg)
raise sushy.exceptions.ExtensionError(error=error_msg)
LOG.debug("iDRAC %(host)s has become pingable", {'host': host})
time.sleep(post_pingable_wait_time)
def _wait_until_idrac_is_ready(self, host, retries, retry_delay):
"""Wait until the iDRAC is in a ready state.
:param host: Hostname or IP of the iDRAC interface.
:param retries: The number of times to check if the iDRAC is
ready.
:param retry_delay: The number of seconds to wait between
retries.
:raises: ExtensionError on failure to perform requested
operation.
"""
while retries > 0:
LOG.debug("Checking to see if iDRAC %(host)s is ready",
{'host': host})
if self.lifecycle_service.is_idrac_ready():
LOG.debug("iDRAC %(host)s is ready", {'host': host})
return
LOG.debug("iDRAC %(host)s is not ready", {'host': host})
retries -= 1
if retries > 0:
time.sleep(retry_delay)
if retries == 0:
error_msg = ("Timed out waiting iDRAC %(host)s to become "
"ready after reset", {'host': host})
LOG.error(error_msg)
raise sushy.exceptions.ExtensionError(error=error_msg)
def _ping_host(self, host):
"""Ping the hostname or IP of a host.
:param host: Hostname or IP.
:returns: True if host is alive; otherwise, False.
"""
response = subprocess.call(["ping", "-c", "1", host]) # noqa:S603,S607
return response == 0
def get_extension(*args, **kwargs):
return DellManagerExtension

View File

@ -0,0 +1,30 @@
# Copyright (c) 2021-2022 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 enum
class PhysicalDiskStateMode(enum.Enum):
"""Physical disk state mode constants"""
RAID = 'RAID'
"""RAID physical disk state mode"""
NONRAID = 'Non-RAID'
"""Non-RAID physical disk state mode"""
# For backward compatibility
PHYSICAL_DISK_STATE_MODE_RAID = PhysicalDiskStateMode.RAID
PHYSICAL_DISK_STATE_MODE_NONRAID = PhysicalDiskStateMode.NONRAID

View File

@ -0,0 +1,147 @@
# Copyright (c) 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
from sushy import exceptions
from sushy.resources import base
from sushy.resources import common
from sushy import taskmonitor
from sushy.oem.dell import constants
LOG = logging.getLogger(__name__)
class ActionsField(base.CompositeField):
convert_to_raid = common.ActionField("#DellRaidService.ConvertToRAID")
convert_to_nonraid = common.ActionField(
"#DellRaidService.ConvertToNonRAID")
clear_foreign_config = common.ActionField(
"#DellRaidService.ClearForeignConfig")
class DellRaidService(base.ResourceBase):
identity = base.Field('Id', required=True)
_actions = ActionsField('Actions')
def __init__(self, connector, identity, redfish_version=None,
registries=None, root=None):
"""A class representing a DellRaidService.
:param connector: A Connector instance
:param identity: The identity of the DellRaidService resource
:param redfish_version: The version of Redfish. Used to
construct the object according to schema of the given
version.
:param registries: Dict of Redfish Message Registry objects to
be used in any resource that needs registries to parse
messages.
:param root: Sushy root object. Empty for Sushy root itself.
"""
super().__init__(
connector, identity, redfish_version=redfish_version,
registries=registries, root=root)
def convert_to_raid(self, physical_disk_fqdds):
"""Converts physical disks to a state usable for RAID
:param physical_disk_fqdds: An array of FQDDs where each
identifies a physical drive.
:returns: Sushy's TaskMonitor instance for TaskService task
"""
target_uri = self._actions.convert_to_raid.target_uri
payload = {'PDArray': physical_disk_fqdds}
response = self._conn.post(target_uri, data=payload)
task_monitor = self._get_task_monitor_from_dell_job(response)
LOG.info('Converting to RAID mode: %s', physical_disk_fqdds)
return task_monitor
def convert_to_nonraid(self, physical_disk_fqdds):
"""Converts physical disks to non-RAID state.
:param physical_disk_fqdds: An array of FQDDs where each
identifies a physical drive.
:returns: Sushy's TaskMonitor instance for TaskService task
"""
target_uri = self._actions.convert_to_nonraid.target_uri
payload = {'PDArray': physical_disk_fqdds}
response = self._conn.post(target_uri, data=payload)
task_monitor = self._get_task_monitor_from_dell_job(response)
LOG.info('Converting to non-RAID mode: %s', physical_disk_fqdds)
return task_monitor
def clear_foreign_config(self, controller_fqdd):
"""Clears foreign configuration
Prepares any foreign physical disks for inclusion in the local
configuration
:param controller_fqdd: FQDD of controller to clear foreign
config
:returns: Sushy's TaskMonitor instance for TaskService task if
there are foreign drives to clear, otherwise None.
"""
target_uri = self._actions.clear_foreign_config.target_uri
payload = {'TargetFQDD': controller_fqdd}
try:
response = self._conn.post(target_uri, data=payload)
except exceptions.BadRequestError as ex:
# Check if failed for no foreign drives
errors = ex.body and ex.body.get('@Message.ExtendedInfo') or []
no_foreign_conf = [x for x in errors if constants.NO_FOREIGN_CONFIG
in x.get('MessageId')]
if len(no_foreign_conf) == 0:
raise ex
else:
LOG.debug('%s: %s', no_foreign_conf[0].get('Message'),
controller_fqdd)
return
task_mon = self._get_task_monitor_from_dell_job(response)
LOG.info('Clearing foreign config: %s', controller_fqdd)
return task_mon
def _get_task_monitor_from_dell_job(self, response):
"""From OEM job response returns generic Task monitor
:param response: Response from OEM job
:returns: Sushy's TaskMonitor instance for TaskService task
"""
location = response.headers.get('Location')
if not location:
raise exceptions.ExtensionError(
error=f'Response {response.url} does not include Location '
'in header')
task_id = location.split('/')[-1]
task = None
for t in self.root.get_task_service().tasks.get_members():
if t.identity == task_id:
task = t
break
if not task:
raise exceptions.ExtensionError(
error=f"Did not find task by id {task_id}")
return taskmonitor.TaskMonitor(
self._conn, task.path,
redfish_version=self.redfish_version,
registries=self.registries)

View File

@ -0,0 +1,28 @@
# Copyright (c) 2022 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 enum
class ControllerMode(enum.Enum):
"""RAID controller modes."""
RAID = "RAID"
"""RAID mode."""
HBA = "HBA"
"""HBA/Passthru mode. Does not support RAID. For PERC 9 controllers."""
EHBA = "EnhancedHBA"
"""Enhanced HBA mode. Limited RAID support. For PERC 10 controllers."""

View File

@ -0,0 +1,56 @@
# Copyright (c) 2022 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 sushy
from sushy.resources import base
from sushy.resources.oem import base as oem_base
from sushy.oem.dell.resources.system.storage import constants as s_cons
class DellStorageController(base.CompositeField):
controller_mode = base.MappedField('ControllerMode', s_cons.ControllerMode)
"""Mode of RAID controller"""
class DellStorageControllerExtension(oem_base.OEMResourceBase):
dell_storage_controller = DellStorageController('DellStorageController')
def convert_to_raid(self):
"""Converts to RAID mode if applicable
If PERC 9 or PERC 10 controller is in non-RAID mode, then convert
to RAID mode. No changes made for PERC 11 and above as they support
only RAID mode, and BOSS controller as it does not have controller
mode.
:returns: TaskMonitor if controller mode changes applied and need to
reboot, otherwise None
"""
controller_mode = self.dell_storage_controller.controller_mode
# BOSS will have this empty, PERC will have something assigned
if controller_mode and controller_mode != s_cons.ControllerMode.RAID:
patch = {
"Oem": {
"Dell": {
"DellStorageController": {
"ControllerMode":
s_cons.ControllerMode.RAID.value}}}}
return self._parent_resource.update(
patch, apply_time=sushy.ApplyTime.ON_RESET)
def get_extension(*args, **kwargs):
return DellStorageControllerExtension

View File

@ -0,0 +1,165 @@
# Copyright (c) 2021-2022 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 sushy
from sushy import exceptions
from sushy.resources.oem import base as oem_base
from sushy import utils as sushy_utils
from sushy.oem.dell.resources.system import constants as sys_cons
from sushy.oem.dell.resources.system import raid_service
def _filter_disks_not_in_mode(controller_to_disks, mode):
"""Filters disks that are not in requested mode
:param controller_to_disks: dictionary of controllers and their drives
:param mode: constants.PhysicalDiskStateMode
:returns: dictionary of controllers and their drives that need mode changed
"""
sushy_raw_device = sushy.VOLUME_TYPE_RAW_DEVICE
for controller, drives in controller_to_disks.items():
toprocess_drives = []
for drive in drives:
is_raw_device = False
volumes = None
try:
volumes = drive.volumes
except exceptions.MissingAttributeError:
pass
if (volumes
and (volumes[0].volume_type == sushy_raw_device
or volumes[0].raid_type is None)):
is_raw_device = True
if (mode == sys_cons.PhysicalDiskStateMode.RAID
and is_raw_device
or mode == sys_cons.PhysicalDiskStateMode.NONRAID
and not is_raw_device):
toprocess_drives.append(drive)
controller_to_disks[controller] = toprocess_drives
return controller_to_disks
class DellSystemExtension(oem_base.OEMResourceBase):
@property
@sushy_utils.cache_it
def raid_service(self):
"""`DellRaidService` of the system"""
path = sushy_utils.get_sub_resource_path_by(
self, ["Links", "Oem", "Dell", "DellRaidService"],
is_collection=False)
return raid_service.DellRaidService(
self._conn, path, redfish_version=self.redfish_version,
registries=self.registries, root=self.root)
def change_physical_disk_state(self, mode, controller_to_disks=None):
"""Converts physical disks RAID status
Converts only those disks that are not already in requested mode.
:param mode: constants.PhysicalDiskStateMode
:param controller_to_disks: dictionary of controllers and their drives.
Optional, if not provided, processes all RAID, except BOSS,
controller drives.
:returns: List of task monitors for each controller's disks if any
drives need changes
"""
if not controller_to_disks:
controller_to_disks = self._get_controller_to_disks()
# Do not process BOSS controllers as can't convert their disks
boss_controllers = [c for c in controller_to_disks
if 'BOSS' in c.name.upper()]
for c in boss_controllers:
controller_to_disks.pop(c)
controller_to_disks = _filter_disks_not_in_mode(
controller_to_disks, mode)
# Convert by each controller that have eligible disks
task_monitors = []
for controller, drives in controller_to_disks.items():
if drives:
drive_fqdds = [d.identity for d in drives]
if mode == sys_cons.PhysicalDiskStateMode.RAID:
task_monitors.append(
self.raid_service.convert_to_raid(drive_fqdds))
elif mode == sys_cons.PhysicalDiskStateMode.NONRAID:
task_monitors.append(
self.raid_service.convert_to_nonraid(drive_fqdds))
return task_monitors
def clear_foreign_config(self, storage_list=None):
"""Clears foreign config on given controllers
:param storage_list: List of storage objects, each of which
corresponds to a controller
:returns: List of task monitors, where each entry is for a
controller that has foreign config to clear
"""
if storage_list is None:
storage_list = self._get_storage_list()
# Do not process BOSS controllers as not supporting clearing
boss_storage = [s for s in storage_list
if any(c for c in s.storage_controllers
if 'BOSS' in c.name.upper())]
for s in boss_storage:
storage_list.remove(s)
task_monitors = []
for storage in storage_list:
task_mon = self.raid_service.clear_foreign_config(storage.identity)
if task_mon:
task_monitors.append(task_mon)
return task_monitors
def _get_controller_to_disks(self):
"""Gets all RAID controllers and their disks on system
:returns: dictionary of RAID controllers and their disks
"""
controller_to_disks = {}
for storage in self._parent_resource.storage.get_members():
controller = (storage.storage_controllers[0]
if storage.storage_controllers else None)
if not controller or controller and not controller.raid_types:
continue
controller_to_disks[controller] = storage.drives
return controller_to_disks
def _get_storage_list(self):
"""Gets all storage items corresponding to RAID controllers
:returns: list of storage items
"""
storage_list = []
for storage in self._parent_resource.storage.get_members():
controller = (storage.storage_controllers[0]
if storage.storage_controllers else None)
if not controller or controller and not controller.raid_types:
continue
storage_list.append(storage)
return storage_list
def get_extension(*args, **kwargs):
return DellSystemExtension

View File

@ -0,0 +1,245 @@
# Copyright (c) 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 enum
class JobState(enum.Enum):
"""Job state constants"""
COMPLETED = "Completed"
"""A job is in completed state"""
COMPLETED_ERRORS = "CompletedWithErrors"
"""A job is in completed state with errors"""
DOWNLOADED = "Downloaded"
"""A job is in downloaded state"""
DOWNLOADING = "Downloading"
"""A job is in downloading state"""
FAILED = "Failed"
"""A job is in failed state"""
NEW = "New"
"""A job is in newly created state"""
PAUSED = "Paused"
"""A job is in paused state"""
PENDING_ACTIVATION = "PendingActivation"
"""A job is in pending activation state"""
READY_EXECUTION = "ReadyForExecution"
"""A job is in ready for execution state"""
REBOOT_COMPLETED = "RebootCompleted"
"""A job is in reboot completed state"""
REBOOT_FAILED = "RebootFailed"
"""A job is in reboot failed state"""
REBOOT_PENDING = "RebootPending"
"""A job is in pending state for reboot"""
RUNNING = "Running"
"""A job is in running state"""
SCHEDULED = "Scheduled"
"""A job is in scheduled state"""
SCHEDULING = "Scheduling"
"""A job is in scheduling state"""
UNKNOWN = "Unknown"
"""A job is in unknown state"""
WAITING = "Waiting"
"""A job is in waiting state"""
# Backward compatibility
JOB_STATE_COMPLETED = JobState.COMPLETED
JOB_STATE_COMPLETED_ERRORS = JobState.COMPLETED_ERRORS
JOB_STATE_DOWNLOADED = JobState.DOWNLOADED
JOB_STATE_DOWNLOADING = JobState.DOWNLOADING
JOB_STATE_FAILED = JobState.FAILED
JOB_STATE_NEW = JobState.NEW
JOB_STATE_PAUSED = JobState.PAUSED
JOB_STATE_PENDING_ACTIVATION = JobState.PENDING_ACTIVATION
JOB_STATE_READY_EXECUTION = JobState.READY_EXECUTION
JOB_STATE_REBOOT_COMPLETED = JobState.REBOOT_COMPLETED
JOB_STATE_REBOOT_FAILED = JobState.REBOOT_FAILED
JOB_STATE_REBOOT_PENDING = JobState.REBOOT_PENDING
JOB_STATE_RUNNING = JobState.RUNNING
JOB_STATE_SCHEDULED = JobState.SCHEDULED
JOB_STATE_SCHEDULING = JobState.SCHEDULING
JOB_STATE_UNKNOWN = JobState.UNKNOWN
JOB_STATE_WAITING = JobState.WAITING
class JobType(enum.Enum):
"""Job type constants"""
BIOS_CONF = "BIOSConfiguration"
"""A BIOS configuration job"""
EXPORT_CONF = "ExportConfiguration"
"""A server configuration profile export job"""
FC_CONF = "FCConfiguration"
"""A Fibre Channel configuration job"""
FACTORY_CONF_EXPORT = "FactoryConfigurationExport"
"""A factory configuration export job"""
FIRMWARE_ROLLBACK = "FirmwareRollback"
"""A firmware rollback job"""
FIRMWARE_UPDATE = "FirmwareUpdate"
"""A firmware update job"""
HW_INVENTORY_EXPORT = "HardwareInventoryExport"
"""A hardware inventory export job"""
IMPORT_CONF = "ImportConfiguration"
"""A server configuration profile import job"""
INBAND_BIOS_CONF = "InbandBIOSConfiguration"
"""An inband BIOS configuration job"""
LC_CONF = "LCConfig"
"""A lifecycle controller attribute configuration job"""
LC_EXPORT = "LCExport"
"""A lifecycle controller export job"""
LC_LOG_EXPORT = "LCLogExport"
"""A lifecycle controller log export job"""
LICENSE_EXPORT = "LicenseExport"
"""A license export job"""
LICENSE_IMPORT = "LicenseImport"
"""A license import job"""
MSG_REG_EXPORT = "MessageRegistryExport"
"""Export message registry report job"""
NIC_CONF = "NICConfiguration"
"""A NIC configuration job"""
OS_DEPLOY = "OSDeploy"
"""Operating System deploy job"""
RAID_CONF = "RAIDConfiguration"
"""A RAID configuration job"""
RT_NO_REBOOT_CONF = "RealTimeNoRebootConfiguration"
"""A real time configuration job without reboot"""
REBOOT_FORCE = "RebootForce"
"""A reboot job with forced shutdown"""
REBOOT_NO_FORCE = "RebootNoForce"
"""A graceful reboot job without forced shutdown"""
REBOOT_POWER_CYCLE = "RebootPowerCycle"
"""A power cycle job"""
REMOTE_DIAG = "RemoteDiagnostics"
"""A remote diagnostics job"""
REPO_UPDATE = "RepositoryUpdate"
"""An update job from a repository"""
SA_COL_EXP_HEALTH_DATA = "SACollectExportHealthData"
"""Support Assist collect and export health data job"""
SA_COL_HEALTH_DATA = "SACollectHealthData"
"""Support Assist collect health data job"""
SA_EXP_HEALTH_DATA = "SAExportHealthData"
"""Support Assist export health data job"""
SA_ISM = "SAExposeISM"
"""Support Assist expose ISM installer package to host job"""
SA_REG = "SARegistration"
"""Support Assist register iDRAC to Dell backend server job"""
SEKM_REKEY = "SEKMRekey"
"""A Secure Enterprise Key Manager rekey job"""
SEKM_STATUS_SET = "SEKMStatusSet"
"""A Secure Enterprise Key Manager status set job"""
SHUTDOWN = "Shutdown"
"""A shutdown job"""
SYS_ERASE = "SystemErase"
"""A selective system erase job"""
SYS_INFO_CONF = "SystemInfoConfiguration"
"""A system info configuration job"""
THERMAL_HIST_EXP = "ThermalHistoryExport"
"""A thermal history export job"""
UNKNOWN = "Unknown"
"""An unknown job"""
IDRAC_CONF = "iDRACConfiguration"
"""An iDRAC configuration job"""
# Backward compatibility
JOB_TYPE_BIOS_CONF = JobType.BIOS_CONF
JOB_TYPE_EXPORT_CONF = JobType.EXPORT_CONF
JOB_TYPE_FC_CONF = JobType.FC_CONF
JOB_TYPE_FACTORY_CONF_EXPORT = JobType.FACTORY_CONF_EXPORT
JOB_TYPE_FIRMWARE_ROLLBACK = JobType.FIRMWARE_ROLLBACK
JOB_TYPE_FIRMWARE_UPDATE = JobType.FIRMWARE_UPDATE
JOB_TYPE_HW_INVENTORY_EXPORT = JobType.HW_INVENTORY_EXPORT
JOB_TYPE_IMPORT_CONF = JobType.IMPORT_CONF
JOB_TYPE_INBAND_BIOS_CONF = JobType.INBAND_BIOS_CONF
JOB_TYPE_LC_CONF = JobType.LC_CONF
JOB_TYPE_LC_EXPORT = JobType.LC_EXPORT
JOB_TYPE_LC_LOG_EXPORT = JobType.LC_LOG_EXPORT
JOB_TYPE_LICENSE_EXPORT = JobType.LICENSE_EXPORT
JOB_TYPE_LICENSE_IMPORT = JobType.LICENSE_IMPORT
JOB_TYPE_MSG_REG_EXPORT = JobType.MSG_REG_EXPORT
JOB_TYPE_NIC_CONF = JobType.NIC_CONF
JOB_TYPE_OS_DEPLOY = JobType.OS_DEPLOY
JOB_TYPE_RAID_CONF = JobType.RAID_CONF
JOB_TYPE_RT_NO_REBOOT_CONF = JobType.RT_NO_REBOOT_CONF
JOB_TYPE_REBOOT_FORCE = JobType.REBOOT_FORCE
JOB_TYPE_REBOOT_NO_FORCE = JobType.REBOOT_NO_FORCE
JOB_TYPE_REBOOT_POWER_CYCLE = JobType.REBOOT_POWER_CYCLE
JOB_TYPE_REMOTE_DIAG = JobType.REMOTE_DIAG
JOB_TYPE_REPO_UPDATE = JobType.REPO_UPDATE
JOB_TYPE_SA_COL_EXP_HEALTH_DATA = JobType.SA_COL_EXP_HEALTH_DATA
JOB_TYPE_SA_COL_HEALTH_DATA = JobType.SA_COL_HEALTH_DATA
JOB_TYPE_SA_EXP_HEALTH_DATA = JobType.SA_EXP_HEALTH_DATA
JOB_TYPE_SA_ISM = JobType.SA_ISM
JOB_TYPE_SA_REG = JobType.SA_REG
JOB_TYPE_SEKM_REKEY = JobType.SEKM_REKEY
JOB_TYPE_SEKM_STATUS_SET = JobType.SEKM_STATUS_SET
JOB_TYPE_SHUTDOWN = JobType.SHUTDOWN
JOB_TYPE_SYS_ERASE = JobType.SYS_ERASE
JOB_TYPE_SYS_INFO_CONF = JobType.SYS_INFO_CONF
JOB_TYPE_THERMAL_HIST_EXP = JobType.THERMAL_HIST_EXP
JOB_TYPE_UNKNOWN = JobType.UNKNOWN
JOB_TYPE_IDRAC_CONF = JobType.IDRAC_CONF

View File

@ -0,0 +1,73 @@
# Copyright (c) 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
from sushy.resources import base
from sushy.resources.oem import base as oem_base
from sushy.oem.dell.resources.taskservice import constants as ts_cons
LOG = logging.getLogger(__name__)
class DellTaskExtension(oem_base.OEMResourceBase):
"""Dell OEM extension for DellJob type"""
identity = base.Field('Id', required=True)
name = base.Field('Name', required=True)
description = base.Field('Description')
completion_time = base.Field('CompletionTime')
"""Job completion time"""
end_time = base.Field('EndTime')
"""End time of job
Timestamp until when the service will wait for a job to complete.
If a job does not complete within this time, it is killed and marked
as failed. TIME_NA is a default value and implies EndTime is not
applicable.
"""
job_state = base.MappedField('JobState', ts_cons.JobState)
"""Job state"""
job_type = base.MappedField('JobType', ts_cons.JobType)
"""Job type"""
message = base.Field('Message')
"""The status message for job"""
message_args = base.Field('MessageArgs')
"""Array of message arguments for message field"""
message_id = base.Field('MessageId')
"""Message id for job"""
percent_complete = base.Field('PercentComplete', adapter=int)
"""The percentage completion of job"""
start_time = base.Field('StartTime')
"""Scheduled start time of job
String that will contain a timestamp in Edm.DateTime format.
TIME_NOW is a default value and implies apply pending configuration
now.
"""
def get_extension(*args, **kwargs):
return DellTaskExtension

40
sushy/oem/dell/utils.py Normal file
View File

@ -0,0 +1,40 @@
# 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
LOG = logging.getLogger(__name__)
def reboot_system(system):
if system.power_state != sushy.POWER_STATE_OFF:
system.reset_system(sushy.RESET_FORCE_OFF)
LOG.info('Requested system power OFF')
while system.power_state != sushy.POWER_STATE_OFF:
time.sleep(30)
system.refresh()
LOG.info('System is powered OFF')
system.reset_system(sushy.RESET_ON)
LOG.info('Requested system power ON')
while system.power_state != sushy.POWER_STATE_ON:
time.sleep(30)
system.refresh()
LOG.info('System powered ON')

View File

View File

View File

@ -0,0 +1,109 @@
# 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 os
import sys
import sushy
from sushy.oem.dell import utils
USERNAME = 'root'
PASSWORD = 'calvin'
SERVICE_ROOT = 'http://demo.snmplabs.com:80/redfish/v1'
SYSTEM_ID = '437XR1138R2'
BOOT_DEVICE = sushy.VIRTUAL_MEDIA_CD
BOOT_MODE = sushy.BOOT_SOURCE_MODE_BIOS
BOOT_IMAGE = 'http://demo.snmplabs.com/mini.iso'
LOG = logging.getLogger(__name__)
def main():
"""Boot Dell node from virtual media device"""
LOG.setLevel(logging.INFO)
handler = logging.StreamHandler()
handler.setLevel(logging.INFO)
LOG.addHandler(handler)
authenticator = sushy.auth.BasicAuth(USERNAME, PASSWORD)
conn = sushy.Sushy(SERVICE_ROOT, verify=False, auth=authenticator)
LOG.info('connected to %s', SERVICE_ROOT)
system = conn.get_system(
os.path.join(SERVICE_ROOT, 'Systems', SYSTEM_ID))
LOG.info('read system resource %s', system.identity)
for manager in system.managers:
LOG.info('trying manager %s', manager.identity)
for v_media in manager.virtual_media.get_members():
if BOOT_DEVICE not in v_media.media_types:
continue
LOG.info(
'device %s is present at %s', BOOT_DEVICE, manager.identity)
try:
manager_oem = manager.get_oem_extension('Dell')
except sushy.exceptions.OEMExtensionNotFoundError:
LOG.info('Dell OEM not found')
continue
LOG.info('found Dell OEM extension at %s', manager.identity)
if v_media.inserted:
v_media.eject_media()
LOG.info('ejected virtual media')
v_media.insert_media(BOOT_IMAGE, inserted=True,
write_protected=True)
LOG.info('inserted boot image %s into virtual media', BOOT_IMAGE)
# the caller (e.g. ironic) sets boot mode first, boot device second
system.set_system_boot_source(
BOOT_DEVICE, enabled=sushy.BOOT_SOURCE_ENABLED_CONTINUOUS,
mode=BOOT_MODE)
# with Dell, patching System tree does not work as expected
# we need to reboot for the new boot mode to take effect
utils.reboot_system(system)
LOG.info('set boot mode to %s', BOOT_MODE)
manager_oem.set_virtual_boot_device(
BOOT_DEVICE, persistent=False, manager=manager, system=system)
LOG.info('set boot device to %s', BOOT_DEVICE)
# real caller should better not use our way to reboot
utils.reboot_system(system)
LOG.info('system rebooted')
return 0
if __name__ == '__main__':
sys.exit(main())

View File

View File

@ -0,0 +1,20 @@
# Copyright 2019 OpenStack Foundation
#
# 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 oslotest import base
class TestCase(base.BaseTestCase):
"""Test case base class for all unit tests"""

View File

@ -0,0 +1,18 @@
{
"error": {
"@Message.ExtendedInfo": [
{
"Message": "Unable to perform the import or export operation because there are pending attribute changes or a configuration job is in progress.",
"MessageArgs": "[]",
"MessageArgs@odata.count": 0,
"MessageId": "IDRAC.2.8.LC068",
"RelatedProperties": [],
"RelatedProperties@odata.count": 0,
"Resolution": "Apply or cancel any pending attribute changes. Changes can be applied by creating a targeted configuration job, or the changes can be cancelled by invoking the DeletePendingConfiguration method. If a configuration job is in progress, wait until it is completed before retrying the import or export system configuration operation.",
"Severity": "Warning"
}
],
"code": "IDRAC.2.8.LC068",
"message": "Unable to perform the import or export operation because there are pending attribute changes or a configuration job is in progress."
}
}

View File

@ -0,0 +1,18 @@
{
"error": {
"@Message.ExtendedInfo": [
{
"Message": "A job operation is already running. Retry the operation after the existing job is completed.",
"MessageArgs": "[]",
"MessageArgs@odata.count": 0,
"MessageId": "IDRAC.2.8.RAC0679",
"RelatedProperties": [],
"RelatedProperties@odata.count": 0,
"Resolution": "Wait until the running job is completed or delete the scheduled job and retry the operation",
"Severity": "Warning"
}
],
"code": "IDRAC.2.8.RAC0679",
"message": "Wait until the running job is completed or delete the scheduled job and retry the operation."
}
}

View File

@ -0,0 +1,174 @@
{ "SystemConfiguration": {
"Comments": [
{ "Comment": "Export type is Clone,RO,JSON,IncludeHash,Selective" },
{ "Comment": "Exported configuration may contain commented attributes. Attributes may be commented due to dependency, destructive nature, preserving server identity or for security reasons." }
],
"Model": "PowerEdge R640",
"ServiceTag": "ABC1234",
"TimeStamp": "Thu Nov 4 07:21:54 2021",
"Components": [
{ "FQDD": "iDRAC.Embedded.1",
"Attributes": [
{ "Name": "Info.1#Product",
"Value": "Integrated Dell Remote Access Controller",
"Set On Import": "False",
"Comment": "Always Read Only" },
{ "Name": "Info.1#Description",
"Value": "This system component provides a complete set of remote management functions for Dell PowerEdge Servers",
"Set On Import": "False",
"Comment": "Always Read Only" },
{ "Name": "Info.1#Version",
"Value": "5.00.10.20",
"Set On Import": "False",
"Comment": "Always Read Only" },
{ "Name": "Info.1#Build",
"Value": "01",
"Set On Import": "False",
"Comment": "Always Read Only" },
{ "Name": "Info.1#Name",
"Value": "iDRAC",
"Set On Import": "False",
"Comment": "Always Read Only" },
{ "Name": "Info.1#Type",
"Value": "14G Monolithic",
"Set On Import": "False",
"Comment": "Always Read Only" },
{ "Name": "Info.1#ServerGen",
"Value": "14G",
"Set On Import": "False",
"Comment": "Always Read Only" },
{ "Name": "IPv4.1#Enable",
"Value": "Enabled",
"Set On Import": "True",
"Comment": "Read and Write" },
{ "Name": "IPv4.1#DHCPEnable",
"Value": "Disabled",
"Set On Import": "True",
"Comment": "Read and Write" },
{ "Name": "IPv6.1#Enable",
"Value": "Disabled",
"Set On Import": "True",
"Comment": "Read and Write" },
{ "Name": "IPv6.1#AutoConfig",
"Value": "Enabled",
"Set On Import": "True",
"Comment": "Read and Write" },
{ "Name": "IPv6.1#LinkLocalAddress",
"Value": "2001:db8:3333:4444:5555:6666:7777:8888",
"Set On Import": "False",
"Comment": "Always Read Only" },
{ "Name": "IPv6.1#Address2",
"Value": "::",
"Set On Import": "False",
"Comment": "Always Read Only" },
{ "Name": "IPv6.1#Address3",
"Value": "::",
"Set On Import": "False",
"Comment": "Always Read Only" },
{ "Name": "IPv6.1#Address4",
"Value": "::",
"Set On Import": "False",
"Comment": "Always Read Only" },
{ "Name": "IPv6.1#Address5",
"Value": "::",
"Set On Import": "False",
"Comment": "Always Read Only" },
{ "Name": "IPv6.1#Address6",
"Value": "::",
"Set On Import": "False",
"Comment": "Always Read Only" },
{ "Name": "IPv6.1#Address7",
"Value": "::",
"Set On Import": "False",
"Comment": "Always Read Only" },
{ "Name": "IPv6.1#Address8",
"Value": "::",
"Set On Import": "False",
"Comment": "Always Read Only" },
{ "Name": "IPv6.1#Address9",
"Value": "::",
"Set On Import": "False",
"Comment": "Always Read Only" },
{ "Name": "IPv6.1#Address10",
"Value": "::",
"Set On Import": "False",
"Comment": "Always Read Only" },
{ "Name": "IPv6.1#Address11",
"Value": "::",
"Set On Import": "False",
"Comment": "Always Read Only" },
{ "Name": "IPv6.1#Address12",
"Value": "::",
"Set On Import": "False",
"Comment": "Always Read Only" },
{ "Name": "IPv6.1#Address13",
"Value": "::",
"Set On Import": "False",
"Comment": "Always Read Only" },
{ "Name": "IPv6.1#Address14",
"Value": "::",
"Set On Import": "False",
"Comment": "Always Read Only" },
{ "Name": "IPv6.1#Address15",
"Value": "::",
"Set On Import": "False",
"Comment": "Always Read Only" },
{ "Name": "IPv6.1#AddressState",
"Value": "Active",
"Set On Import": "False",
"Comment": "Always Read Only" },
{ "Name": "IPv6.1#DUID",
"Value": "00:01:00:01:25:67:39:1b:f4:02:70:db:6c:88",
"Set On Import": "False",
"Comment": "Always Read Only" },
{ "Name": "IPv4Static.1#Address",
"Value": "192.168.88.13",
"Set On Import": "True",
"Comment": "Read and Write" },
{ "Name": "IPv4Static.1#Netmask",
"Value": "255.255.255.0",
"Set On Import": "True",
"Comment": "Read and Write" },
{ "Name": "IPv4Static.1#Gateway",
"Value": "192.168.88.1",
"Set On Import": "True",
"Comment": "Read and Write" },
{ "Name": "IPv4Static.1#DNS1",
"Value": "0.0.0.0",
"Set On Import": "True",
"Comment": "Read and Write" },
{ "Name": "IPv4Static.1#DNS2",
"Value": "0.0.0.0",
"Set On Import": "True",
"Comment": "Read and Write" },
{ "Name": "IPv4Static.1#DNSFromDHCP",
"Value": "Disabled",
"Set On Import": "True",
"Comment": "Read and Write" },
{ "Name": "IPv6Static.1#Address1",
"Value": "::",
"Set On Import": "True",
"Comment": "Read and Write" },
{ "Name": "IPv6Static.1#Gateway",
"Value": "::",
"Set On Import": "True",
"Comment": "Read and Write" },
{ "Name": "IPv6Static.1#PrefixLength",
"Value": "64",
"Set On Import": "True",
"Comment": "Read and Write" },
{ "Name": "IPv6Static.1#DNS1",
"Value": "::",
"Set On Import": "True",
"Comment": "Read and Write" },
{ "Name": "IPv6Static.1#DNS2",
"Value": "::",
"Set On Import": "True",
"Comment": "Read and Write" },
{ "Name": "IPv6Static.1#DNSFromDHCP6",
"Value": "Disabled",
"Set On Import": "True",
"Comment": "Read and Write" }
]}
]}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,17 @@
{
"@odata.context": "/redfish/v1/$metadata#DelliDRACCard.DelliDRACCardService",
"@odata.id": "/redfish/v1/Dell/Managers/iDRAC.Embedded.1/DelliDRACCardService",
"@odata.type": "#DelliDRACCardService.v1_1_0.DelliDRACCardService",
"Actions": {
"#DelliDRACCardService.iDRACReset": {
"Force@Redfish.AllowableValues": [
"Graceful",
"Force"
],
"target": "/redfish/v1/Dell/Managers/iDRAC.Embedded.1/DelliDRACCardService/Actions/DelliDRACCardService.iDRACReset"
}
},
"Description": "The DelliDRACCardService resource provides some actions to support iDRAC configurations.",
"Id": "DelliDRACCardService",
"Name": "DelliDRACCardService"
}

View File

@ -0,0 +1,29 @@
{
"@odata.context": "/redfish/v1/$metadata#DellJobCollection.DellJobCollection",
"@odata.id": "/redfish/v1/Managers/iDRAC.Embedded.1/Jobs",
"@odata.type": "#DellJobCollection.DellJobCollection",
"Description": "Collection of Job Instances",
"Id": "JobQueue",
"Members": [
{
"@odata.context": "/redfish/v1/$metadata#DellJob.DellJob",
"@odata.id": "/redfish/v1/Managers/iDRAC.Embedded.1/Jobs/RID_878460711202",
"@odata.type": "#DellJob.v1_0_2.DellJob",
"CompletionTime": "2020-04-25T15:21:33",
"Description": "Job Instance",
"EndTime": "TIME_NA",
"Id": "RID_878460711202",
"JobState": "Running",
"JobType": "RebootForce",
"Message": "Reboot is complete.",
"MessageArgs": [ ],
"MessageArgs@odata.count": 0,
"MessageId": "RED030",
"Name": "Reboot3",
"PercentComplete": 100,
"StartTime": "TIME_NOW",
"TargetSettingsURI": null
}
],
"Name": "JobQueue"
}

View File

@ -0,0 +1,13 @@
{
"@odata.context": "/redfish/v1/$metadata#DellJobService.DellJobService",
"@odata.id": "/redfish/v1/Dell/Managers/iDRAC.Embedded.1/DellJobService",
"@odata.type": "#DellJobService.v1_1_0.DellJobService",
"Actions": {
"#DellJobService.DeleteJobQueue": {
"target": "/redfish/v1/Dell/Managers/iDRAC.Embedded.1/DellJobService/Actions/DellJobService.DeleteJobQueue"
}
},
"Description": "The DellJobService resource provides some actions to support Job management functionality.",
"Id": "Job Service",
"Name": "DellJobService"
}

View File

@ -0,0 +1,13 @@
{
"@odata.context": "/redfish/v1/$metadata#DellLCService.DellLCService",
"@odata.id": "/redfish/v1/Dell/Managers/iDRAC.Embedded.1/DellLCService",
"@odata.type": "#DellLCService.v1_1_0.DellLCService",
"Actions": {
"#DellLCService.GetRemoteServicesAPIStatus": {
"target": "/redfish/v1/Dell/Managers/iDRAC.Embedded.1/DellLCService/Actions/DellLCService.GetRemoteServicesAPIStatus"
}
},
"Description": "The DellLCService resource provides some actions to support Lifecycle Controller functionality.",
"Id": "DellLCService",
"Name": "DellLCService"
}

View File

@ -0,0 +1,274 @@
{
"@odata.context": "/redfish/v1/$metadata#Manager.Manager",
"@odata.id": "/redfish/v1/Managers/iDRAC.Embedded.1",
"@odata.type": "#Manager.v1_3_3.Manager",
"Actions": {
"#Manager.Reset": {
"ResetType@Redfish.AllowableValues": [
"GracefulRestart"
],
"target": "/redfish/v1/Managers/iDRAC.Embedded.1/Actions/Manager.Reset"
},
"Oem": {
"DellManager.v1_0_0#DellManager.ResetToDefaults": {
"ResetType@Redfish.AllowableValues": [
"All",
"ResetAllWithRootDefaults",
"Default"
],
"target": "/redfish/v1/Managers/iDRAC.Embedded.1/Actions/Oem/DellManager.ResetToDefaults"
},
"OemManager.v1_0_0#OemManager.ExportSystemConfiguration": {
"ExportFormat@Redfish.AllowableValues": [
"XML",
"JSON"
],
"ExportUse@Redfish.AllowableValues": [
"Default",
"Clone",
"Replace"
],
"IncludeInExport@Redfish.AllowableValues": [
"Default",
"IncludeReadOnly",
"IncludePasswordHashValues"
],
"ShareParameters": {
"IgnoreCertificateWarning@Redfish.AllowableValues": [
"Disabled",
"Enabled"
],
"ProxySupport@Redfish.AllowableValues": [
"Disabled",
"EnabledProxyDefault",
"Enabled"
],
"ProxyType@Redfish.AllowableValues": [
"HTTP",
"SOCKS4"
],
"ShareType@Redfish.AllowableValues": [
"LOCAL",
"NFS",
"CIFS",
"HTTP",
"HTTPS"
],
"Target@Redfish.AllowableValues": [
"ALL",
"IDRAC",
"BIOS",
"NIC",
"RAID"
]
},
"target": "/redfish/v1/Managers/iDRAC.Embedded.1/Actions/Oem/EID_674_Manager.ExportSystemConfiguration"
},
"OemManager.v1_0_0#OemManager.ImportSystemConfiguration": {
"HostPowerState@Redfish.AllowableValues": [
"On",
"Off"
],
"ImportSystemConfiguration@Redfish.AllowableValues": [
"TimeToWait",
"ImportBuffer"
],
"ShareParameters": {
"IgnoreCertificateWarning@Redfish.AllowableValues": [
"Disabled",
"Enabled"
],
"ProxySupport@Redfish.AllowableValues": [
"Disabled",
"EnabledProxyDefault",
"Enabled"
],
"ProxyType@Redfish.AllowableValues": [
"HTTP",
"SOCKS4"
],
"ShareType@Redfish.AllowableValues": [
"LOCAL",
"NFS",
"CIFS",
"HTTP",
"HTTPS"
],
"Target@Redfish.AllowableValues": [
"ALL",
"IDRAC",
"BIOS",
"NIC",
"RAID"
]
},
"ShutdownType@Redfish.AllowableValues": [
"Graceful",
"Forced",
"NoReboot"
],
"target": "/redfish/v1/Managers/iDRAC.Embedded.1/Actions/Oem/EID_674_Manager.ImportSystemConfiguration"
},
"OemManager.v1_0_0#OemManager.ImportSystemConfigurationPreview": {
"ImportSystemConfigurationPreview@Redfish.AllowableValues": [
"ImportBuffer"
],
"ShareParameters": {
"IgnoreCertificateWarning@Redfish.AllowableValues": [
"Disabled",
"Enabled"
],
"ProxySupport@Redfish.AllowableValues": [
"Disabled",
"EnabledProxyDefault",
"Enabled"
],
"ProxyType@Redfish.AllowableValues": [
"HTTP",
"SOCKS4"
],
"ShareType@Redfish.AllowableValues": [
"LOCAL",
"NFS",
"CIFS",
"HTTP",
"HTTPS"
],
"Target@Redfish.AllowableValues": [
"ALL"
]
},
"target": "/redfish/v1/Managers/iDRAC.Embedded.1/Actions/Oem/EID_674_Manager.ImportSystemConfigurationPreview"
}
}
},
"CommandShell": {
"ConnectTypesSupported": [
"SSH",
"Telnet",
"IPMI"
],
"ConnectTypesSupported@odata.count": 3,
"MaxConcurrentSessions": 5,
"ServiceEnabled": true
},
"DateTime": "2019-08-07T12:47:37-04:00",
"DateTimeLocalOffset": "-04:00",
"Description": "BMC",
"EthernetInterfaces": {
"@odata.id": "/redfish/v1/Managers/iDRAC.Embedded.1/EthernetInterfaces"
},
"FirmwareVersion": "3.34.34.34",
"GraphicalConsole": {
"ConnectTypesSupported": [
"KVMIP"
],
"ConnectTypesSupported@odata.count": 1,
"MaxConcurrentSessions": 6,
"ServiceEnabled": true
},
"HostInterfaces": {
"@odata.id": "/redfish/v1/Managers/iDRAC.Embedded.1/HostInterfaces"
},
"Id": "iDRAC.Embedded.1",
"Links": {
"ManagerForChassis": [
{
"@odata.id": "/redfish/v1/Chassis/System.Embedded.1"
}
],
"ManagerForChassis@odata.count": 1,
"ManagerForServers": [
{
"@odata.id": "/redfish/v1/Systems/System.Embedded.1"
}
],
"ManagerForServers@odata.count": 1,
"ManagerInChassis": {
"@odata.id": "/redfish/v1/Chassis/System.Embedded.1"
},
"Oem": {
"Dell": {
"DellAttributes": [
{
"@odata.id": "/redfish/v1/Managers/iDRAC.Embedded.1/Attributes"
},
{
"@odata.id": "/redfish/v1/Managers/System.Embedded.1/Attributes"
},
{
"@odata.id": "/redfish/v1/Managers/LifecycleController.Embedded.1/Attributes"
}
],
"DellAttributes@odata.count": 3,
"DellJobService": {
"@odata.id": "/redfish/v1/Dell/Managers/iDRAC.Embedded.1/DellJobService"
},
"DellLCService": {
"@odata.id": "/redfish/v1/Dell/Managers/iDRAC.Embedded.1/DellLCService"
},
"DellLicenseCollection": {
"@odata.id": "/redfish/v1/Dell/Managers/iDRAC.Embedded.1/DellLicenseCollection"
},
"DellLicenseManagementService": {
"@odata.id": "/redfish/v1/Dell/Managers/iDRAC.Embedded.1/DellLicenseManagementService"
},
"DellPersistentStorageService": {
"@odata.id": "/redfish/v1/Dell/Managers/iDRAC.Embedded.1/DellPersistentStorageService"
},
"DellSwitchConnectionCollection": {
"@odata.id": "/redfish/v1/Dell/Systems/System.Embedded.1/NetworkPorts/DellSwitchConnectionCollection"
},
"DelliDRACCardService": {
"@odata.id": "/redfish/v1/Dell/Managers/iDRAC.Embedded.1/DelliDRACCardService"
},
"DellvFlashCollection": {
"@odata.id": "/redfish/v1/Dell/Managers/iDRAC.Embedded.1/DellvFlashCollection"
},
"Jobs": {
"@odata.id": "/redfish/v1/Managers/iDRAC.Embedded.1/Jobs"
}
}
}
},
"LogServices": {
"@odata.id": "/redfish/v1/Managers/iDRAC.Embedded.1/LogServices"
},
"ManagerType": "BMC",
"Model": "14G Monolithic",
"Name": "Manager",
"NetworkProtocol": {
"@odata.id": "/redfish/v1/Managers/iDRAC.Embedded.1/NetworkProtocol"
},
"Oem": {
"Dell": {
"DelliDRACCard": {
"@odata.context": "/redfish/v1/$metadata#DelliDRACCard.DelliDRACCard",
"@odata.id": "/redfish/v1/Dell/Managers/iDRAC.Embedded.1/DelliDRACCard/iDRAC.Embedded.1-1%23IDRACinfo",
"@odata.type": "#DelliDRACCard.v1_0_0.DelliDRACCard",
"IPMIVersion": "2.0",
"URLString": "https://10.19.133.11:443"
}
}
},
"PowerState": "On",
"Redundancy": [],
"Redundancy@odata.count": 0,
"SerialConsole": {
"ConnectTypesSupported": [],
"ConnectTypesSupported@odata.count": 0,
"MaxConcurrentSessions": 0,
"ServiceEnabled": false
},
"SerialInterfaces": {
"@odata.id": "/redfish/v1/Managers/iDRAC.Embedded.1/SerialInterfaces"
},
"Status": {
"Health": "OK",
"State": "Enabled"
},
"UUID": "3256444f-c0c7-3780-5310-00544c4c4544",
"VirtualMedia": {
"@odata.id": "/redfish/v1/Managers/iDRAC.Embedded.1/VirtualMedia"
}
}

View File

@ -0,0 +1,13 @@
{
"@odata.context": "/redfish/v1/$metadata#ManagerCollection.ManagerCollection",
"@odata.id": "/redfish/v1/Managers",
"@odata.type": "#ManagerCollection.ManagerCollection",
"Description": "BMC",
"Members": [
{
"@odata.id": "/redfish/v1/Managers/iDRAC.Embedded.1"
}
],
"Members@odata.count": 1,
"Name": "Manager"
}

View File

@ -0,0 +1,213 @@
{
"@odata.context": "/redfish/v1/$metadata#DellRaidService.DellRaidService",
"@odata.id": "/redfish/v1/Systems/System.Embedded.1/Oem/Dell/DellRaidService",
"@odata.type": "#DellRaidService.v1_3_0.DellRaidService",
"Actions": {
"#DellRaidService.AssignSpare": {
"target": "/redfish/v1/Systems/System.Embedded.1/Oem/Dell/DellRaidService/Actions/DellRaidService.AssignSpare"
},
"#DellRaidService.BlinkTarget": {
"target": "/redfish/v1/Systems/System.Embedded.1/Oem/Dell/DellRaidService/Actions/DellRaidService.BlinkTarget"
},
"#DellRaidService.CancelBackgroundInitialization": {
"target": "/redfish/v1/Systems/System.Embedded.1/Oem/Dell/DellRaidService/Actions/DellRaidService.CancelBackgroundInitialization"
},
"#DellRaidService.CancelCheckConsistency": {
"target": "/redfish/v1/Systems/System.Embedded.1/Oem/Dell/DellRaidService/Actions/DellRaidService.CancelCheckConsistency"
},
"#DellRaidService.CancelRebuildPhysicalDisk": {
"target": "/redfish/v1/Systems/System.Embedded.1/Oem/Dell/DellRaidService/Actions/DellRaidService.CancelRebuildPhysicalDisk"
},
"#DellRaidService.ChangePDState": {
"State@Redfish.AllowableValues": [
"Offline",
"Online"
],
"target": "/redfish/v1/Systems/System.Embedded.1/Oem/Dell/DellRaidService/Actions/DellRaidService.ChangePDState"
},
"#DellRaidService.CheckVDValues": {
"VDPropNameArrayIn@Redfish.AllowableValues": [
"RAIDLevel",
"Size",
"SpanDepth",
"SpanLength",
"StartingLBA",
"T10PIStatus"
],
"target": "/redfish/v1/Systems/System.Embedded.1/Oem/Dell/DellRaidService/Actions/DellRaidService.CheckVDValues"
},
"#DellRaidService.ClearControllerPreservedCache": {
"target": "/redfish/v1/Systems/System.Embedded.1/Oem/Dell/DellRaidService/Actions/DellRaidService.ClearControllerPreservedCache"
},
"#DellRaidService.ClearForeignConfig": {
"target": "/redfish/v1/Systems/System.Embedded.1/Oem/Dell/DellRaidService/Actions/DellRaidService.ClearForeignConfig"
},
"#DellRaidService.ConvertToNonRAID": {
"target": "/redfish/v1/Systems/System.Embedded.1/Oem/Dell/DellRaidService/Actions/DellRaidService.ConvertToNonRAID"
},
"#DellRaidService.ConvertToRAID": {
"target": "/redfish/v1/Systems/System.Embedded.1/Oem/Dell/DellRaidService/Actions/DellRaidService.ConvertToRAID"
},
"#DellRaidService.EnableControllerEncryption": {
"Mode@Redfish.AllowableValues": [
"DKM",
"LKM",
"SEKM"
],
"target": "/redfish/v1/Systems/System.Embedded.1/Oem/Dell/DellRaidService/Actions/DellRaidService.EnableControllerEncryption"
},
"#DellRaidService.GetAvailableDisks": {
"BlockSizeInBytes@Redfish.AllowableValues": [
"4096",
"512",
"All"
],
"DiskEncrypt@Redfish.AllowableValues": [
"All",
"FDE",
"NonFDE"
],
"DiskType@Redfish.AllowableValues": [
"All",
"HDD",
"SSD"
],
"Diskprotocol@Redfish.AllowableValues": [
"AllProtocols",
"NVMe",
"SAS",
"SATA"
],
"FormFactor@Redfish.AllowableValues": [
"All",
"M.2"
],
"RaidLevel@Redfish.AllowableValues": [
"NoRAID",
"RAID0",
"RAID1",
"RAID10",
"RAID5",
"RAID50",
"RAID6",
"RAID60"
],
"T10PIStatus@Redfish.AllowableValues": [
"All",
"T10PICapable",
"T10PIIncapable"
],
"target": "/redfish/v1/Systems/System.Embedded.1/Oem/Dell/DellRaidService/Actions/DellRaidService.GetAvailableDisks"
},
"#DellRaidService.GetDHSDisks": {
"target": "/redfish/v1/Systems/System.Embedded.1/Oem/Dell/DellRaidService/Actions/DellRaidService.GetDHSDisks"
},
"#DellRaidService.GetRAIDLevels": {
"BlockSizeInBytes@Redfish.AllowableValues": [
"4096",
"512",
"All"
],
"DiskEncrypt@Redfish.AllowableValues": [
"All",
"FDE",
"NonFDE"
],
"DiskType@Redfish.AllowableValues": [
"All",
"HDD",
"SSD"
],
"Diskprotocol@Redfish.AllowableValues": [
"AllProtocols",
"NVMe",
"SAS",
"SATA"
],
"FormFactor@Redfish.AllowableValues": [
"All",
"M.2"
],
"T10PIStatus@Redfish.AllowableValues": [
"All",
"T10PICapable",
"T10PIIncapable"
],
"target": "/redfish/v1/Systems/System.Embedded.1/Oem/Dell/DellRaidService/Actions/DellRaidService.GetRAIDLevels"
},
"#DellRaidService.ImportForeignConfig": {
"target": "/redfish/v1/Systems/System.Embedded.1/Oem/Dell/DellRaidService/Actions/DellRaidService.ImportForeignConfig"
},
"#DellRaidService.LockVirtualDisk": {
"target": "/redfish/v1/Systems/System.Embedded.1/Oem/Dell/DellRaidService/Actions/DellRaidService.LockVirtualDisk"
},
"#DellRaidService.OnlineCapacityExpansion": {
"target": "/redfish/v1/Systems/System.Embedded.1/Oem/Dell/DellRaidService/Actions/DellRaidService.OnlineCapacityExpansion"
},
"#DellRaidService.PrepareToRemove": {
"target": "/redfish/v1/Systems/System.Embedded.1/Oem/Dell/DellRaidService/Actions/DellRaidService.PrepareToRemove"
},
"#DellRaidService.RAIDLevelMigration": {
"NewRaidLevel@Redfish.AllowableValues": [
"NoRAID",
"RAID0",
"RAID1",
"RAID10",
"RAID5",
"RAID50",
"RAID6",
"RAID60"
],
"target": "/redfish/v1/Systems/System.Embedded.1/Oem/Dell/DellRaidService/Actions/DellRaidService.RAIDLevelMigration"
},
"#DellRaidService.ReKey": {
"Mode@Redfish.AllowableValues": [
"LKM",
"SEKM"
],
"target": "/redfish/v1/Systems/System.Embedded.1/Oem/Dell/DellRaidService/Actions/DellRaidService.ReKey"
},
"#DellRaidService.RebuildPhysicalDisk": {
"target": "/redfish/v1/Systems/System.Embedded.1/Oem/Dell/DellRaidService/Actions/DellRaidService.RebuildPhysicalDisk"
},
"#DellRaidService.RemoveControllerKey": {
"target": "/redfish/v1/Systems/System.Embedded.1/Oem/Dell/DellRaidService/Actions/DellRaidService.RemoveControllerKey"
},
"#DellRaidService.RenameVD": {
"target": "/redfish/v1/Systems/System.Embedded.1/Oem/Dell/DellRaidService/Actions/DellRaidService.RenameVD"
},
"#DellRaidService.ReplacePhysicalDisk": {
"target": "/redfish/v1/Systems/System.Embedded.1/Oem/Dell/DellRaidService/Actions/DellRaidService.ReplacePhysicalDisk"
},
"#DellRaidService.ResetConfig": {
"target": "/redfish/v1/Systems/System.Embedded.1/Oem/Dell/DellRaidService/Actions/DellRaidService.ResetConfig"
},
"#DellRaidService.SetAssetName": {
"target": "/redfish/v1/Systems/System.Embedded.1/Oem/Dell/DellRaidService/Actions/DellRaidService.SetAssetName"
},
"#DellRaidService.SetBootVD": {
"target": "/redfish/v1/Systems/System.Embedded.1/Oem/Dell/DellRaidService/Actions/DellRaidService.SetBootVD"
},
"#DellRaidService.SetControllerKey": {
"target": "/redfish/v1/Systems/System.Embedded.1/Oem/Dell/DellRaidService/Actions/DellRaidService.SetControllerKey"
},
"#DellRaidService.StartPatrolRead": {
"target": "/redfish/v1/Systems/System.Embedded.1/Oem/Dell/DellRaidService/Actions/DellRaidService.StartPatrolRead"
},
"#DellRaidService.StopPatrolRead": {
"target": "/redfish/v1/Systems/System.Embedded.1/Oem/Dell/DellRaidService/Actions/DellRaidService.StopPatrolRead"
},
"#DellRaidService.UnBlinkTarget": {
"target": "/redfish/v1/Systems/System.Embedded.1/Oem/Dell/DellRaidService/Actions/DellRaidService.UnBlinkTarget"
},
"#DellRaidService.UnLockSecureForeignConfig": {
"target": "/redfish/v1/Systems/System.Embedded.1/Oem/Dell/DellRaidService/Actions/DellRaidService.UnLockSecureForeignConfig"
},
"#DellRaidService.UnassignSpare": {
"target": "/redfish/v1/Systems/System.Embedded.1/Oem/Dell/DellRaidService/Actions/DellRaidService.UnassignSpare"
}
},
"Description": "The DellRaidService resource provides some actions to support RAID functionality.",
"Id": "DellRaidService",
"Name": "DellRaidService"
}

View File

@ -0,0 +1,6 @@
---
fixes:
- |
Resolves the issue where wait for Lifecycle Controller job ends prematurely
due to iDRAC BMC raising a different exception type than the code
previously expected by using the correct exception class.

View File

@ -0,0 +1,68 @@
{
"@odata.context": "/redfish/v1/$metadata#ServiceRoot.ServiceRoot",
"@odata.id": "/redfish/v1",
"@odata.type": "#ServiceRoot.v1_3_0.ServiceRoot",
"AccountService": {
"@odata.id": "/redfish/v1/Managers/iDRAC.Embedded.1/AccountService"
},
"Chassis": {
"@odata.id": "/redfish/v1/Chassis"
},
"Description": "Root Service",
"EventService": {
"@odata.id": "/redfish/v1/EventService"
},
"Fabrics": {
"@odata.id": "/redfish/v1/Fabrics"
},
"Id": "RootService",
"JsonSchemas": {
"@odata.id": "/redfish/v1/JSONSchemas"
},
"Links": {
"Sessions": {
"@odata.id": "/redfish/v1/Sessions"
}
},
"Managers": {
"@odata.id": "/redfish/v1/Managers"
},
"Name": "Root Service",
"Oem": {
"Dell": {
"@odata.context": "/redfish/v1/$metadata#DellServiceRoot.DellServiceRoot",
"@odata.type": "#DellServiceRoot.v1_0_0.ServiceRootSummary",
"IsBranded": 0,
"ManagerMACAddress": "4c:d9:8f:20:73:84",
"ServiceTag": "GTS7DV2"
}
},
"Product": "Integrated Dell Remote Access Controller",
"ProtocolFeaturesSupported": {
"ExpandQuery": {
"ExpandAll": true,
"Levels": true,
"Links": true,
"MaxLevels": 1,
"NoLinks": true
},
"FilterQuery": true,
"SelectQuery": true
},
"RedfishVersion": "1.4.0",
"Registries": {
"@odata.id": "/redfish/v1/Registries"
},
"SessionService": {
"@odata.id": "/redfish/v1/SessionService"
},
"Systems": {
"@odata.id": "/redfish/v1/Systems"
},
"Tasks": {
"@odata.id": "/redfish/v1/TaskService"
},
"UpdateService": {
"@odata.id": "/redfish/v1/UpdateService"
}
}

View File

@ -0,0 +1,58 @@
{
"@Redfish.Settings": {
"@odata.context": "/redfish/v1/$metadata#Settings.Settings",
"@odata.type": "#Settings.v1_3_3.Settings",
"SettingsObject": {
"@odata.id": "/redfish/v1/Systems/437XR1138R2/Storage/1/Controllers/1/Settings"
},
"SupportedApplyTimes": [
"Immediate",
"OnReset"
]
},
"@odata.context": "/redfish/v1/$metadata#StorageController.StorageController",
"@odata.id": "/redfish/v1/Systems/437XR1138R2/Storage/1/Controllers/1",
"@odata.type": "#StorageController.v1_2_0.StorageController",
"Description": "Integrated RAID Controller",
"FirmwareVersion": "1.0.0.7",
"Id": "1",
"Identifiers": [
{
"@odata.type": "#Resource.v1_1_0.Identifier",
"DurableNameFormat": "NAA",
"DurableName": "345C59DBD970859C"
}
],
"Identifiers@odata.count": 1,
"Manufacturer": "Contoso",
"Model": "12Gbs Integrated RAID",
"Name": "Contoso Integrated RAID",
"Oem": {
"Dell": {
"@odata.type": "#DellOemStorageController.v1_0_0.DellOemStorageController",
"DellStorageController": {
"ControllerMode": "EnhancedHBA"
}
}
},
"SpeedGbps": 12,
"Status": {
"Health": "OK",
"HealthRollup": "OK",
"State": "Enabled"
},
"SupportedControllerProtocols": [
"PCIe"
],
"SupportedControllerProtocols@odata.count": 1,
"SupportedDeviceProtocols": [
"SAS",
"SATA"
],
"SupportedDeviceProtocols@odata.count": 2,
"SupportedRAIDTypes": [
"RAID0",
"RAID1"
],
"SupportedRAIDTypes@odata.count": 2
}

View File

@ -0,0 +1,398 @@
{
"@odata.context": "/redfish/v1/$metadata#ComputerSystem.ComputerSystem",
"@odata.id": "/redfish/v1/Systems/System.Embedded.1",
"@odata.type": "#ComputerSystem.v1_10_0.ComputerSystem",
"Actions": {
"#ComputerSystem.Reset": {
"target": "/redfish/v1/Systems/System.Embedded.1/Actions/ComputerSystem.Reset",
"ResetType@Redfish.AllowableValues": [
"On",
"ForceOff",
"ForceRestart",
"GracefulRestart",
"GracefulShutdown",
"PushPowerButton",
"Nmi",
"PowerCycle"
]
}
},
"AssetTag": "",
"Bios": {
"@odata.id": "/redfish/v1/Systems/System.Embedded.1/Bios"
},
"BiosVersion": "2.10.2",
"Boot": {
"BootOptions": {
"@odata.id": "/redfish/v1/Systems/System.Embedded.1/BootOptions"
},
"Certificates": {
"@odata.id": "/redfish/v1/Systems/System.Embedded.1/Boot/Certificates"
},
"BootOrder": [
"Boot0006",
"Boot0009",
"Boot0007",
"Boot0005",
"Boot0003",
"Boot0004",
"Boot0008",
"Boot0002"
],
"BootOrder@odata.count": 8,
"BootSourceOverrideEnabled": "Disabled",
"BootSourceOverrideMode": "UEFI",
"BootSourceOverrideTarget": "None",
"UefiTargetBootSourceOverride": null,
"BootSourceOverrideTarget@Redfish.AllowableValues": [
"None",
"Pxe",
"Floppy",
"Cd",
"Hdd",
"BiosSetup",
"Utilities",
"UefiTarget",
"SDCard",
"UefiHttp"
]
},
"Description": "Computer System which represents a machine (physical or virtual) and the local resources such as memory, cpu and other devices that can be accessed from that machine.",
"EthernetInterfaces": {
"@odata.id": "/redfish/v1/Systems/System.Embedded.1/EthernetInterfaces"
},
"HostName": "",
"HostWatchdogTimer": {
"FunctionEnabled": false,
"Status": {
"State": "Disabled"
},
"TimeoutAction": "None"
},
"HostingRoles": [],
"HostingRoles@odata.count": 0,
"Id": "System.Embedded.1",
"IndicatorLED": "Lit",
"Links": {
"Chassis": [
{
"@odata.id": "/redfish/v1/Chassis/System.Embedded.1"
}
],
"Chassis@odata.count": 1,
"CooledBy": [
{
"@odata.id": "/redfish/v1/Chassis/System.Embedded.1/Thermal#/Fans/0"
},
{
"@odata.id": "/redfish/v1/Chassis/System.Embedded.1/Thermal#/Fans/1"
},
{
"@odata.id": "/redfish/v1/Chassis/System.Embedded.1/Thermal#/Fans/2"
},
{
"@odata.id": "/redfish/v1/Chassis/System.Embedded.1/Thermal#/Fans/3"
},
{
"@odata.id": "/redfish/v1/Chassis/System.Embedded.1/Thermal#/Fans/4"
},
{
"@odata.id": "/redfish/v1/Chassis/System.Embedded.1/Thermal#/Fans/5"
},
{
"@odata.id": "/redfish/v1/Chassis/System.Embedded.1/Thermal#/Fans/6"
},
{
"@odata.id": "/redfish/v1/Chassis/System.Embedded.1/Thermal#/Fans/7"
},
{
"@odata.id": "/redfish/v1/Chassis/System.Embedded.1/Thermal#/Fans/8"
},
{
"@odata.id": "/redfish/v1/Chassis/System.Embedded.1/Thermal#/Fans/9"
},
{
"@odata.id": "/redfish/v1/Chassis/System.Embedded.1/Thermal#/Fans/10"
},
{
"@odata.id": "/redfish/v1/Chassis/System.Embedded.1/Thermal#/Fans/11"
},
{
"@odata.id": "/redfish/v1/Chassis/System.Embedded.1/Thermal#/Fans/12"
},
{
"@odata.id": "/redfish/v1/Chassis/System.Embedded.1/Thermal#/Fans/13"
},
{
"@odata.id": "/redfish/v1/Chassis/System.Embedded.1/Thermal#/Fans/14"
},
{
"@odata.id": "/redfish/v1/Chassis/System.Embedded.1/Thermal#/Fans/15"
}
],
"CooledBy@odata.count": 16,
"ManagedBy": [
{
"@odata.id": "/redfish/v1/Managers/iDRAC.Embedded.1"
}
],
"ManagedBy@odata.count": 1,
"Oem": {
"Dell": {
"@odata.type": "#DellOem.v1_1_0.DellOemLinks",
"BootOrder": {
"@odata.id": "/redfish/v1/Systems/System.Embedded.1/Oem/Dell/DellBootSources"
},
"DellBootSources": {
"@odata.id": "/redfish/v1/Systems/System.Embedded.1/Oem/Dell/DellBootSources"
},
"DellSoftwareInstallationService": {
"@odata.id": "/redfish/v1/Systems/System.Embedded.1/Oem/Dell/DellSoftwareInstallationService"
},
"DellVideoCollection": {
"@odata.id": "/redfish/v1/Systems/System.Embedded.1/Oem/Dell/DellVideo"
},
"DellChassisCollection": {
"@odata.id": "/redfish/v1/Chassis/System.Embedded.1/Oem/Dell/DellChassis"
},
"DellPresenceAndStatusSensorCollection": {
"@odata.id": "/redfish/v1/Systems/System.Embedded.1/Oem/Dell/DellPresenceAndStatusSensors"
},
"DellSensorCollection": {
"@odata.id": "/redfish/v1/Systems/System.Embedded.1/Oem/Dell/DellSensors"
},
"DellRollupStatusCollection": {
"@odata.id": "/redfish/v1/Systems/System.Embedded.1/Oem/Dell/DellRollupStatus"
},
"DellPSNumericSensorCollection": {
"@odata.id": "/redfish/v1/Systems/System.Embedded.1/Oem/Dell/DellPSNumericSensors"
},
"DellVideoNetworkCollection": {
"@odata.id": "/redfish/v1/Systems/System.Embedded.1/Oem/Dell/DellVideoNetwork"
},
"DellOSDeploymentService": {
"@odata.id": "/redfish/v1/Systems/System.Embedded.1/Oem/Dell/DellOSDeploymentService"
},
"DellMetricService": {
"@odata.id": "/redfish/v1/Systems/System.Embedded.1/Oem/Dell/DellMetricService"
},
"DellGPUSensorCollection": {
"@odata.id": "/redfish/v1/Systems/System.Embedded.1/Oem/Dell/DellGPUSensors"
},
"DellRaidService": {
"@odata.id": "/redfish/v1/Systems/System.Embedded.1/Oem/Dell/DellRaidService"
},
"DellNumericSensorCollection": {
"@odata.id": "/redfish/v1/Systems/System.Embedded.1/Oem/Dell/DellNumericSensors"
},
"DellBIOSService": {
"@odata.id": "/redfish/v1/Systems/System.Embedded.1/Oem/Dell/DellBIOSService"
},
"DellSlotCollection": {
"@odata.id": "/redfish/v1/Systems/System.Embedded.1/Oem/Dell/DellSlots"
}
}
},
"PoweredBy": [
{
"@odata.id": "/redfish/v1/Chassis/System.Embedded.1/Power#/PowerSupplies/0"
},
{
"@odata.id": "/redfish/v1/Chassis/System.Embedded.1/Power#/PowerSupplies/1"
}
],
"PoweredBy@odata.count": 2
},
"Manufacturer": "Dell Inc.",
"Memory": {
"@odata.id": "/redfish/v1/Systems/System.Embedded.1/Memory"
},
"MemorySummary": {
"MemoryMirroring": "System",
"Status": {
"Health": "OK",
"HealthRollup": "OK",
"State": "Enabled"
},
"TotalSystemMemoryGiB": 192
},
"Model": "PowerEdge R640",
"Name": "System",
"NetworkInterfaces": {
"@odata.id": "/redfish/v1/Systems/System.Embedded.1/NetworkInterfaces"
},
"Oem": {
"Dell": {
"@odata.type": "#DellOem.v1_1_0.DellOemResources",
"DellSystem": {
"BIOSReleaseDate": "02/24/2021",
"BaseBoardChassisSlot": "NA",
"BatteryRollupStatus": "OK",
"BladeGeometry": "NotApplicable",
"CMCIP": null,
"CPURollupStatus": "OK",
"ChassisModel": "",
"ChassisName": "Main System Chassis",
"ChassisServiceTag": "8CYCZ23",
"ChassisSystemHeightUnit": 1,
"CurrentRollupStatus": "OK",
"EstimatedExhaustTemperatureCelsius": 46,
"EstimatedSystemAirflowCFM": 26,
"ExpressServiceCode": "18197565051",
"FanRollupStatus": "OK",
"Id": "System.Embedded.1",
"IDSDMRollupStatus": null,
"IntrusionRollupStatus": "OK",
"IsOEMBranded": "False",
"LastSystemInventoryTime": "2021-06-16T09:46:25+00:00",
"LastUpdateTime": "2021-05-31T15:39:10+00:00",
"LicensingRollupStatus": "OK",
"MaxCPUSockets": 2,
"MaxDIMMSlots": 24,
"MaxPCIeSlots": 3,
"MemoryOperationMode": "OptimizerMode",
"Name": "DellSystem",
"NodeID": "8CYCZ23",
"PSRollupStatus": "OK",
"PlatformGUID": "33325a4f-c0b8-4380-5910-00434c4c4544",
"PopulatedDIMMSlots": 12,
"PopulatedPCIeSlots": 1,
"PowerCapEnabledState": "Disabled",
"SDCardRollupStatus": null,
"SELRollupStatus": "OK",
"ServerAllocationWatts": null,
"StorageRollupStatus": "OK",
"SysMemErrorMethodology": "Multi-bitECC",
"SysMemFailOverState": "NotInUse",
"SysMemLocation": "SystemBoardOrMotherboard",
"SysMemPrimaryStatus": "OK",
"SystemGeneration": "14G Monolithic",
"SystemID": 1814,
"SystemRevision": "I",
"TempRollupStatus": "OK",
"TempStatisticsRollupStatus": "OK",
"UUID": "4c4c4544-0043-5910-8043-b8c04f5a3233",
"VoltRollupStatus": "OK",
"smbiosGUID": "44454c4c-4300-1059-8043-b8c04f5a3233",
"@odata.context": "/redfish/v1/$metadata#DellSystem.DellSystem",
"@odata.type": "#DellSystem.v1_2_0.DellSystem",
"@odata.id": "/redfish/v1/Systems/System.Embedded.1/Oem/Dell/DellSystem/System.Embedded.1"
}
}
},
"PCIeDevices": [
{
"@odata.id": "/redfish/v1/Systems/System.Embedded.1/PCIeDevices/25-0"
},
{
"@odata.id": "/redfish/v1/Systems/System.Embedded.1/PCIeDevices/24-0"
},
{
"@odata.id": "/redfish/v1/Systems/System.Embedded.1/PCIeDevices/0-28"
},
{
"@odata.id": "/redfish/v1/Systems/System.Embedded.1/PCIeDevices/0-23"
},
{
"@odata.id": "/redfish/v1/Systems/System.Embedded.1/PCIeDevices/0-31"
},
{
"@odata.id": "/redfish/v1/Systems/System.Embedded.1/PCIeDevices/3-0"
},
{
"@odata.id": "/redfish/v1/Systems/System.Embedded.1/PCIeDevices/0-0"
},
{
"@odata.id": "/redfish/v1/Systems/System.Embedded.1/PCIeDevices/0-17"
},
{
"@odata.id": "/redfish/v1/Systems/System.Embedded.1/PCIeDevices/59-0"
}
],
"PCIeDevices@odata.count": 9,
"PCIeFunctions": [
{
"@odata.id": "/redfish/v1/Systems/System.Embedded.1/PCIeDevices/25-0/PCIeFunctions/25-0-1"
},
{
"@odata.id": "/redfish/v1/Systems/System.Embedded.1/PCIeDevices/24-0/PCIeFunctions/24-0-0"
},
{
"@odata.id": "/redfish/v1/Systems/System.Embedded.1/PCIeDevices/25-0/PCIeFunctions/25-0-0"
},
{
"@odata.id": "/redfish/v1/Systems/System.Embedded.1/PCIeDevices/0-28/PCIeFunctions/0-28-4"
},
{
"@odata.id": "/redfish/v1/Systems/System.Embedded.1/PCIeDevices/0-23/PCIeFunctions/0-23-0"
},
{
"@odata.id": "/redfish/v1/Systems/System.Embedded.1/PCIeDevices/0-31/PCIeFunctions/0-31-0"
},
{
"@odata.id": "/redfish/v1/Systems/System.Embedded.1/PCIeDevices/0-28/PCIeFunctions/0-28-0"
},
{
"@odata.id": "/redfish/v1/Systems/System.Embedded.1/PCIeDevices/3-0/PCIeFunctions/3-0-0"
},
{
"@odata.id": "/redfish/v1/Systems/System.Embedded.1/PCIeDevices/0-0/PCIeFunctions/0-0-0"
},
{
"@odata.id": "/redfish/v1/Systems/System.Embedded.1/PCIeDevices/0-17/PCIeFunctions/0-17-5"
},
{
"@odata.id": "/redfish/v1/Systems/System.Embedded.1/PCIeDevices/59-0/PCIeFunctions/59-0-1"
},
{
"@odata.id": "/redfish/v1/Systems/System.Embedded.1/PCIeDevices/59-0/PCIeFunctions/59-0-0"
},
{
"@odata.id": "/redfish/v1/Systems/System.Embedded.1/PCIeDevices/0-31/PCIeFunctions/0-31-4"
}
],
"PCIeFunctions@odata.count": 13,
"PartNumber": "06NR82A02",
"PowerState": "On",
"ProcessorSummary": {
"Count": 2,
"LogicalProcessorCount": 80,
"Model": "Intel(R) Xeon(R) Gold 6230 CPU @ 2.10GHz",
"Status": {
"Health": "OK",
"HealthRollup": "OK",
"State": "Enabled"
}
},
"Processors": {
"@odata.id": "/redfish/v1/Systems/System.Embedded.1/Processors"
},
"SKU": "8CYCZ23",
"SecureBoot": {
"@odata.id": "/redfish/v1/Systems/System.Embedded.1/SecureBoot"
},
"SerialNumber": "CNIVC000180873",
"SimpleStorage": {
"@odata.id": "/redfish/v1/Systems/System.Embedded.1/SimpleStorage"
},
"Status": {
"Health": "OK",
"HealthRollup": "OK",
"State": "Enabled"
},
"Storage": {
"@odata.id": "/redfish/v1/Systems/System.Embedded.1/Storage"
},
"SystemType": "Physical",
"TrustedModules": [
{
"FirmwareVersion": "Unknown",
"InterfaceType": null,
"Status": {
"State": "Disabled"
}
}
],
"TrustedModules@odata.count": 1,
"UUID": "4c4c4544-0043-5910-8043-b8c04f5a3233"
}

View File

@ -0,0 +1,38 @@
{
"@odata.context": "/redfish/v1/$metadata#Task.Task",
"@odata.id": "/redfish/v1/TaskService/Tasks/JID_257309938313",
"@odata.type": "#Task.v1_4_3.Task",
"Description": "Server Configuration and other Tasks running on iDRAC are listed here",
"Id": "JID_257309938313",
"Messages": [
{
"Message": "Task successfully scheduled.",
"MessageArgs": [],
"MessageArgs@odata.count": 0,
"MessageId": "IDRAC.2.5.JCP001"
}
],
"Messages@odata.count": 1,
"Name": "Configure: RAID.Integrated.1-1",
"Oem": {
"Dell": {
"@odata.type": "#DellJob.v1_1_0.DellJob",
"CompletionTime": null,
"Description": "Job Instance",
"EndTime": "TIME_NA",
"Id": "JID_257309938313",
"JobState": "Scheduled",
"JobType": "RAIDConfiguration",
"Message": "Task successfully scheduled.",
"MessageArgs": [],
"MessageId": "IDRAC.2.5.JCP001",
"Name": "Configure: RAID.Integrated.1-1",
"PercentComplete": 0,
"StartTime": "TIME_NOW",
"TargetSettingsURI": null
}
},
"PercentComplete": 0,
"TaskState": "Starting",
"TaskStatus": "OK"
}

View File

@ -0,0 +1,65 @@
# 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 json
from unittest import mock
from oslotest.base import BaseTestCase
from sushy.oem.dell.resources.manager import constants as mgr_cons
from sushy.oem.dell.resources.manager import idrac_card_service
class DelliDRACCardServiceTestCase(BaseTestCase):
def setUp(self):
super().setUp()
self.conn = mock.Mock()
with open('sushy/tests/oem/dell/unit/json_samples/'
'idrac_card_service.json') as f:
mock_response = self.conn.get.return_value
mock_response.json.return_value = json.load(f)
mock_response.status_code = 200
mock_response = self.conn.post.return_value
mock_response.status_code = 202
mock_response.headers.get.return_value = '1'
self.idrac_card_service = idrac_card_service.DelliDRACCardService(
self.conn, '/redfish/v1/Dell/Managers/iDRAC.Embedded.1')
def test_reset_idrac(self):
self.idrac_card_service.reset_idrac()
target_uri = ('/redfish/v1/Dell/Managers/iDRAC.Embedded.1'
'/DelliDRACCardService'
'/Actions/DelliDRACCardService.iDRACReset')
self.conn.post.assert_called_once_with(target_uri,
data={'Force': 'Graceful'})
def test_get_allowed_reset_idrac_values(self):
expected_values = {mgr_cons.ResetType.GRACEFUL,
mgr_cons.ResetType.FORCE}
allowed_values = \
self.idrac_card_service.get_allowed_reset_idrac_values()
self.assertEqual(expected_values, allowed_values)
def test_get_allowed_reset_idrac_values_not_provided_by_idrac(self):
idrac_service_json = self.idrac_card_service.json
base_property = '#DelliDRACCardService.iDRACReset'
remove_property = 'Force@Redfish.AllowableValues'
idrac_service_json['Actions'][base_property].pop(remove_property)
expected_values = {mgr_cons.ResetType.GRACEFUL,
mgr_cons.ResetType.FORCE}
allowed_values = \
self.idrac_card_service.get_allowed_reset_idrac_values()
self.assertEqual(expected_values, allowed_values)

View File

@ -0,0 +1,46 @@
# 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 json
from unittest import mock
from oslotest.base import BaseTestCase
from sushy.oem.dell.resources.manager import job_collection
class DellJobCollectionTestCase(BaseTestCase):
def setUp(self):
super().setUp()
self.conn = mock.Mock()
with open('sushy/tests/oem/dell/unit/json_samples/'
'job_collection_expanded.json') as f:
mock_response = self.conn.get.return_value
mock_response.json.return_value = json.load(f)
mock_response.status_code = 200
mock_response = self.conn.post.return_value
mock_response.status_code = 202
mock_response.headers.get.return_value = '1'
self.job_collection = job_collection.DellJobCollection(
self.conn, '/redfish/v1/Managers/iDRAC.Embedded.1/Jobs')
def test_get_unfinished_jobs(self):
expected_unfinished_jobs = ['RID_878460711202']
actual_unfinished_jobs = self.job_collection.get_unfinished_jobs()
target_uri = ('/redfish/v1/Managers/iDRAC.Embedded.1'
'/Jobs?$expand=.($levels=1)')
self.conn.get.assert_called_with(target_uri)
self.assertEqual(expected_unfinished_jobs, actual_unfinished_jobs)

View File

@ -0,0 +1,50 @@
# 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 json
from unittest import mock
from oslotest.base import BaseTestCase
from sushy.oem.dell.resources.manager import job_service
class DellJobServiceTestCase(BaseTestCase):
def setUp(self):
super().setUp()
self.conn = mock.Mock()
with open('sushy/tests/oem/dell/unit/json_samples/'
'job_service.json') as f:
mock_response = self.conn.get.return_value
mock_response.json.return_value = json.load(f)
mock_response.status_code = 200
mock_response = self.conn.post.return_value
mock_response.status_code = 202
mock_response.headers.get.return_value = '1'
self.job_service = job_service.DellJobService(
self.conn,
'/redfish/v1/Dell/Managers/iDRAC.Embedded.1/DellJobService')
def test_delete_jobs(self):
job_ids = ['JID_471269252011', 'JID_471269252012']
self.job_service.delete_jobs(job_ids=job_ids)
target_uri = ('/redfish/v1/Dell/Managers'
'/iDRAC.Embedded.1/DellJobService'
'/Actions/''DellJobService.DeleteJobQueue')
for job_id in job_ids:
self.conn.post.assert_any_call(target_uri,
data={'JobID': job_id})
self.assertEqual(2, self.conn.post.call_count)

View File

@ -0,0 +1,103 @@
# 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 json
from unittest import mock
from oslotest.base import BaseTestCase
from sushy.oem.dell.resources.manager import lifecycle_service
class DellLCServiceTestCase(BaseTestCase):
def setUp(self):
super().setUp()
self.conn = mock.Mock()
with open('sushy/tests/oem/dell/unit/json_samples/'
'lifecycle_service.json') as f:
mock_response = self.conn.get.return_value
mock_response.json.return_value = json.load(f)
mock_response.status_code = 200
mock_response = self.conn.post.return_value
mock_response.status_code = 202
mock_response.headers.get.return_value = '1'
self.lifecycle_service = lifecycle_service.DellLCService(
self.conn,
'/redfish/v1/Dell/Managers/iDRAC.Embedded.1/DellLCService')
def test_is_idrac_ready_true(self):
mock_response = self.conn.post.return_value
mock_response.status_code = 200
mock_response.json.return_value = {
"LCStatus": "Ready",
"RTStatus": "Ready",
"ServerStatus": "OutOfPOST",
"Status": "Ready"
}
target_uri = ('/redfish/v1/Dell/Managers/iDRAC.Embedded.1'
'/DellLCService'
'/Actions/DellLCService.GetRemoteServicesAPIStatus')
idrac_ready_response = self.lifecycle_service.is_idrac_ready()
self.conn.post.assert_called_once_with(target_uri, data={})
self.assertTrue(idrac_ready_response)
def test_is_idrac_ready_false(self):
mock_response = self.conn.post.return_value
mock_response.status_code = 202
mock_response.json.return_value = {
"LCStatus": "NotReady",
"RTStatus": "NotReady",
"ServerStatus": "OutOfPOST",
"Status": "NotReady"
}
target_uri = ('/redfish/v1/Dell/Managers/iDRAC.Embedded.1'
'/DellLCService'
'/Actions/DellLCService.GetRemoteServicesAPIStatus')
idrac_ready_response = self.lifecycle_service.is_idrac_ready()
self.conn.post.assert_called_once_with(target_uri, data={})
self.assertFalse(idrac_ready_response)
def test_is_realtime_ready_true(self):
mock_response = self.conn.post.return_value
mock_response.status_code = 200
mock_response.json.return_value = {
"LCStatus": "Ready",
"RTStatus": "Ready",
"ServerStatus": "OutOfPOST",
"Status": "Ready"
}
self.assertTrue(self.lifecycle_service.is_realtime_ready())
target_uri = ('/redfish/v1/Dell/Managers/iDRAC.Embedded.1'
'/DellLCService'
'/Actions/DellLCService.GetRemoteServicesAPIStatus')
self.conn.post.assert_called_once_with(target_uri, data={})
def test_is_realtime_ready_false(self):
mock_response = self.conn.post.return_value
mock_response.status_code = 202
mock_response.json.return_value = {
"LCStatus": "Ready",
"RTStatus": "NotReady",
"ServerStatus": "OutOfPOST",
"Status": "NotReady"
}
self.assertFalse(self.lifecycle_service.is_realtime_ready())
target_uri = ('/redfish/v1/Dell/Managers/iDRAC.Embedded.1'
'/DellLCService'
'/Actions/DellLCService.GetRemoteServicesAPIStatus')
self.conn.post.assert_called_once_with(target_uri, data={})

View File

@ -0,0 +1,668 @@
# Copyright 2017 Red Hat, Inc.
# All Rights Reserved.
# 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.
from http import client as http_client
import json
from unittest import mock
from oslotest.base import BaseTestCase
import requests
import sushy
from sushy.resources.manager import manager
from sushy.taskmonitor import TaskMonitor
from sushy.oem.dell.resources.manager import constants as mgr_cons
from sushy.oem.dell.resources.manager import idrac_card_service as idrac_card
from sushy.oem.dell.resources.manager import job_collection as jc
from sushy.oem.dell.resources.manager import job_service as job
from sushy.oem.dell.resources.manager import lifecycle_service as lifecycle
from sushy.oem.dell.resources.manager import manager as oem_manager
class ManagerTestCase(BaseTestCase):
def setUp(self):
super().setUp()
self.conn = mock.Mock()
with open('sushy/tests/oem/dell/unit/json_samples/'
'manager.json') as f:
mock_response = self.conn.get.return_value
mock_response.json.return_value = json.load(f)
mock_response.status_code = 200
mock_response = self.conn.post.return_value
mock_response.status_code = 202
mock_response.headers = {
'Location': '/redfish/v1/TaskService/Tasks/JID_905749031119'}
mock_response.content = None
self.manager = manager.Manager(self.conn, '/redfish/v1/Managers/BMC',
redfish_version='1.0.2')
@mock.patch('sushy.resources.oem.common._global_extn_mgrs_by_resource', {})
def test_import_system_configuration_uri(self):
oem = self.manager.get_oem_extension('Dell')
self.assertEqual(
'/redfish/v1/Managers/iDRAC.Embedded.1/Actions/Oem/EID_674_Manager'
'.ImportSystemConfiguration',
oem.import_system_configuration_uri)
@mock.patch('sushy.resources.oem.common._global_extn_mgrs_by_resource', {})
def test_set_virtual_boot_device_cd(self):
oem = self.manager.get_oem_extension('Dell')
oem.set_virtual_boot_device(
sushy.VIRTUAL_MEDIA_CD, manager=self.manager)
self.conn.post.assert_called_once_with(
'/redfish/v1/Managers/iDRAC.Embedded.1/Actions/Oem/EID_674_Manager'
'.ImportSystemConfiguration',
data={'ShareParameters': {'Target': 'ALL'},
'ImportBuffer':
'<SystemConfiguration><Component FQDD="iDRAC.Embedded.1">'
'<Attribute Name="ServerBoot.1#BootOnce">Enabled'
'</Attribute><Attribute Name="ServerBoot.1'
'#FirstBootDevice">VCD-DVD</Attribute></Component>'
'</SystemConfiguration>'})
@mock.patch('sushy.resources.oem.common._global_extn_mgrs_by_resource', {})
def test_set_virtual_boot_device_cd_no_manager_passed(self):
oem = self.manager.get_oem_extension('Dell')
oem.set_virtual_boot_device(sushy.VIRTUAL_MEDIA_CD)
self.conn.post.assert_called_once_with(
'/redfish/v1/Managers/iDRAC.Embedded.1/Actions/Oem/EID_674_Manager'
'.ImportSystemConfiguration',
data={'ShareParameters': {'Target': 'ALL'},
'ImportBuffer':
'<SystemConfiguration><Component FQDD="iDRAC.Embedded.1">'
'<Attribute Name="ServerBoot.1#BootOnce">Enabled'
'</Attribute><Attribute Name="ServerBoot.1'
'#FirstBootDevice">VCD-DVD</Attribute></Component>'
'</SystemConfiguration>'})
@mock.patch('time.sleep', autospec=True)
@mock.patch('sushy.resources.oem.common._global_extn_mgrs_by_resource', {})
def test_set_virtual_boot_device_cd_running_exc(self, mock_sleep):
oem = self.manager.get_oem_extension('Dell')
with open('sushy/tests/oem/dell/unit/json_samples/'
'error_running_job.json') as f:
response_obj = json.load(f)
response1 = mock.MagicMock(spec=requests.Response)
response1.status_code = http_client.BAD_REQUEST
response1.json.return_value = response_obj
response1.code = "IDRAC.2.8.LC068"
response2 = mock.MagicMock(spec=requests.Response)
response2.status_code = http_client.OK
self.conn.post.side_effect = [sushy.exceptions.HTTPError(
method='POST', url=self.manager.path, response=response1),
response2]
oem.set_virtual_boot_device(
sushy.VIRTUAL_MEDIA_CD, manager=self.manager)
self.conn.post.assert_called_with(
'/redfish/v1/Managers/iDRAC.Embedded.1/Actions/Oem/EID_674_Manager'
'.ImportSystemConfiguration',
data={'ShareParameters': {'Target': 'ALL'},
'ImportBuffer':
'<SystemConfiguration><Component FQDD="iDRAC.Embedded.1">'
'<Attribute Name="ServerBoot.1#BootOnce">Enabled'
'</Attribute><Attribute Name="ServerBoot.1'
'#FirstBootDevice">VCD-DVD</Attribute></Component>'
'</SystemConfiguration>'})
@mock.patch('sushy.oem.dell.utils.reboot_system', autospec=True)
@mock.patch('sushy.resources.oem.common._global_extn_mgrs_by_resource', {})
def test_set_virtual_boot_device_cd_pending_exc(self, mock_reboot):
oem = self.manager.get_oem_extension('Dell')
with open('sushy/tests/oem/dell/unit/json_samples/'
'error_pending_job.json') as f:
response_obj = json.load(f)
response1 = mock.MagicMock(spec=requests.Response)
response1.status_code = http_client.BAD_REQUEST
response1.json.return_value = response_obj
response1.code = "IDRAC.2.8.LC068"
response2 = mock.MagicMock(spec=requests.Response)
response2.status_code = http_client.OK
self.conn.post.side_effect = [sushy.exceptions.HTTPError(
method='POST', url=self.manager.path, response=response1),
response2]
oem.set_virtual_boot_device(
sushy.VIRTUAL_MEDIA_CD, manager=self.manager)
self.conn.post.assert_called_with(
'/redfish/v1/Managers/iDRAC.Embedded.1/Actions/Oem/EID_674_Manager'
'.ImportSystemConfiguration',
data={'ShareParameters': {'Target': 'ALL'},
'ImportBuffer':
'<SystemConfiguration><Component FQDD="iDRAC.Embedded.1">'
'<Attribute Name="ServerBoot.1#BootOnce">Enabled'
'</Attribute><Attribute Name="ServerBoot.1'
'#FirstBootDevice">VCD-DVD</Attribute></Component>'
'</SystemConfiguration>'})
@mock.patch('sushy.resources.oem.common._global_extn_mgrs_by_resource', {})
def test_set_virtual_boot_device_cd_other_exc(self):
oem = self.manager.get_oem_extension('Dell')
response = mock.MagicMock(spec=requests.Response)
response.status_code = http_client.FORBIDDEN
self.conn.post.side_effect = sushy.exceptions.HTTPError(
method='POST', url=self.manager.path, response=response)
self.assertRaises(sushy.exceptions.HTTPError,
oem.set_virtual_boot_device,
sushy.VIRTUAL_MEDIA_CD,
manager=self.manager)
@mock.patch('sushy.resources.oem.common._global_extn_mgrs_by_resource', {})
def test_get_allowed_export_target_values(self):
oem = self.manager.get_oem_extension('Dell')
expected_values = {mgr_cons.ExportTarget.IDRAC,
mgr_cons.ExportTarget.RAID,
mgr_cons.ExportTarget.ALL,
mgr_cons.ExportTarget.BIOS,
mgr_cons.ExportTarget.NIC}
allowed_values = oem.get_allowed_export_target_values()
self.assertEqual(expected_values, allowed_values)
@mock.patch('sushy.resources.oem.common._global_extn_mgrs_by_resource', {})
def test_get_allowed_export_target_values_missing(self):
oem = self.manager.get_oem_extension('Dell')
export_action = ('OemManager.v1_0_0'
'#OemManager.ExportSystemConfiguration')
oem.json['Actions']['Oem'][export_action]['ShareParameters'].pop(
'Target@Redfish.AllowableValues')
oem.refresh()
oem = self.manager.get_oem_extension('Dell')
expected_values = {mgr_cons.ExportTarget.IDRAC,
mgr_cons.ExportTarget.RAID,
mgr_cons.ExportTarget.ALL,
mgr_cons.ExportTarget.BIOS,
mgr_cons.ExportTarget.NIC}
allowed_values = oem.get_allowed_export_target_values()
self.assertEqual(expected_values, allowed_values)
@mock.patch('sushy.resources.oem.common._global_extn_mgrs_by_resource', {})
def test_export_system_configuration_uri(self):
oem = self.manager.get_oem_extension('Dell')
self.assertEqual(
'/redfish/v1/Managers/iDRAC.Embedded.1/Actions/Oem/EID_674_Manager'
'.ExportSystemConfiguration',
oem.export_system_configuration_uri)
@mock.patch('sushy.resources.oem.common._global_extn_mgrs_by_resource', {})
def test__export_system_configuration(self):
oem = self.manager.get_oem_extension('Dell')
oem._export_system_configuration(
target=mgr_cons.ExportTarget.ALL)
self.conn.post.assert_called_once_with(
'/redfish/v1/Managers/iDRAC.Embedded.1/Actions/Oem/EID_674_Manager'
'.ExportSystemConfiguration', data={'ShareParameters':
{'Target': 'ALL'},
'ExportFormat': 'JSON',
'ExportUse': 'Default',
'IncludeInExport': 'Default'})
@mock.patch('sushy.resources.oem.common._global_extn_mgrs_by_resource', {})
def test__export_system_configuration_nondefault(self):
oem = self.manager.get_oem_extension('Dell')
include_in_export = mgr_cons.IncludeInExport.READ_ONLY_PASSWORD_HASHES
oem._export_system_configuration(
target=mgr_cons.ExportTarget.RAID,
export_use=mgr_cons.ExportUse.CLONE,
include_in_export=include_in_export)
self.conn.post.assert_called_once_with(
'/redfish/v1/Managers/iDRAC.Embedded.1/Actions/Oem/EID_674_Manager'
'.ExportSystemConfiguration', data={'ShareParameters':
{'Target': 'RAID'},
'ExportFormat': 'JSON',
'ExportUse': 'Clone',
'IncludeInExport':
'IncludeReadOnly,Include'
'PasswordHashValues'})
@mock.patch('sushy.resources.oem.common._global_extn_mgrs_by_resource', {})
def test__export_system_configuration_invalid_target(self):
oem = self.manager.get_oem_extension('Dell')
target = "xyz"
self.assertRaises(sushy.exceptions.InvalidParameterValueError,
oem._export_system_configuration, target)
def test__export_system_configuration_invalid_export_use(self):
oem = self.manager.get_oem_extension('Dell')
self.assertRaises(sushy.exceptions.InvalidParameterValueError,
oem._export_system_configuration,
mgr_cons.ExportTarget.RAID,
export_use="ABC")
def test__export_system_configuration_invalid_include_in_export(self):
oem = self.manager.get_oem_extension('Dell')
self.assertRaises(sushy.exceptions.InvalidParameterValueError,
oem._export_system_configuration,
mgr_cons.ExportTarget.RAID,
include_in_export="ABC")
def test__export_system_configuration_invalid_include_in_export_part(self):
oem = self.manager.get_oem_extension('Dell')
export_action = ('OemManager.v1_0_0'
'#OemManager.ExportSystemConfiguration')
# Remove `IncludePasswordHashValues` from allowed values
oem.json['Actions']['Oem'][export_action]['IncludeInExport@Redfish.'
'AllowableValues'] =\
['Default', 'IncludeReadOnly']
oem.refresh()
oem = self.manager.get_oem_extension('Dell')
include_in_export = mgr_cons.IncludeInExport.READ_ONLY_PASSWORD_HASHES
self.assertRaises(sushy.exceptions.InvalidParameterValueError,
oem._export_system_configuration,
mgr_cons.ExportTarget.RAID,
include_in_export=include_in_export)
def test__export_system_configuration_include_in_export_legacy(
self):
oem = self.manager.get_oem_extension('Dell')
export_action = ('OemManager.v1_0_0'
'#OemManager.ExportSystemConfiguration')
# Add `IncludeReadOnly,IncludePasswordHashValues` to allowed values
oem.json['Actions']['Oem'][export_action]['IncludeInExport@Redfish.'
'AllowableValues'] =\
['Default', 'IncludeReadOnly', 'IncludePasswordHashValues',
'IncludeReadOnly,IncludePasswordHashValues']
oem.refresh()
include_in_export = mgr_cons.IncludeInExport.READ_ONLY_PASSWORD_HASHES
oem._export_system_configuration(
target=mgr_cons.ExportTarget.RAID,
export_use=mgr_cons.ExportUse.CLONE,
include_in_export=include_in_export)
self.conn.post.assert_called_once_with(
'/redfish/v1/Managers/iDRAC.Embedded.1/Actions/Oem/EID_674_Manager'
'.ExportSystemConfiguration', data={'ShareParameters':
{'Target': 'RAID'},
'ExportFormat': 'JSON',
'ExportUse': 'Clone',
'IncludeInExport':
'IncludeReadOnly,Include'
'PasswordHashValues'})
@mock.patch('sushy.resources.oem.common._global_extn_mgrs_by_resource', {})
def test_get_allowed_export_use_values(self):
oem = self.manager.get_oem_extension('Dell')
expected_values = {mgr_cons.ExportUse.DEFAULT,
mgr_cons.ExportUse.CLONE,
mgr_cons.ExportUse.REPLACE}
allowed_values = oem.get_allowed_export_use_values()
self.assertIsInstance(allowed_values, set)
self.assertEqual(expected_values, allowed_values)
@mock.patch.object(oem_manager, 'LOG', autospec=True)
def test_get_allowed_export_use_values_missing(self, mock_log):
oem = self.manager.get_oem_extension('Dell')
export_action = ('OemManager.v1_0_0'
'#OemManager.ExportSystemConfiguration')
oem.json['Actions']['Oem'][export_action].pop(
'ExportUse@Redfish.AllowableValues')
oem.refresh()
expected_values = {mgr_cons.ExportUse.DEFAULT,
mgr_cons.ExportUse.CLONE,
mgr_cons.ExportUse.REPLACE}
allowed_values = oem.get_allowed_export_use_values()
self.assertIsInstance(allowed_values, set)
self.assertEqual(expected_values, allowed_values)
mock_log.warning.assert_called_once()
@mock.patch('sushy.resources.oem.common._global_extn_mgrs_by_resource', {})
def test_get_allowed_include_in_export_values(self):
oem = self.manager.get_oem_extension('Dell')
expected_values = {mgr_cons.IncludeInExport.DEFAULT,
mgr_cons.IncludeInExport.READ_ONLY,
mgr_cons.IncludeInExport.PASSWORD_HASHES}
allowed_values = oem.get_allowed_include_in_export_values()
self.assertIsInstance(allowed_values, set)
self.assertEqual(expected_values, allowed_values)
@mock.patch.object(oem_manager, 'LOG', autospec=True)
def test_get_allowed_include_in_export_values_missing(self, mock_log):
oem = self.manager.get_oem_extension('Dell')
export_action = ('OemManager.v1_0_0'
'#OemManager.ExportSystemConfiguration')
oem.json['Actions']['Oem'][export_action].pop(
'IncludeInExport@Redfish.AllowableValues')
oem.refresh()
expected_values = {mgr_cons.IncludeInExport.DEFAULT,
mgr_cons.IncludeInExport.READ_ONLY,
mgr_cons.IncludeInExport.PASSWORD_HASHES,
mgr_cons.IncludeInExport.READ_ONLY_PASSWORD_HASHES}
allowed_values = oem.get_allowed_include_in_export_values()
self.assertIsInstance(allowed_values, set)
self.assertEqual(expected_values, allowed_values)
mock_log.warning.assert_called_once()
@mock.patch('sushy.resources.oem.common._global_extn_mgrs_by_resource', {})
def test_export_system_configuration(self):
oem = self.manager.get_oem_extension('Dell')
oem._export_system_configuration = mock.Mock()
mock_response = mock.Mock()
oem._export_system_configuration.return_value = mock_response
response = oem.export_system_configuration()
self.assertEqual(mock_response, response)
include_in_export = mgr_cons.IncludeInExport.READ_ONLY_PASSWORD_HASHES
oem._export_system_configuration.assert_called_once_with(
mgr_cons.ExportTarget.ALL,
export_use=mgr_cons.ExportUse.CLONE,
include_in_export=include_in_export)
@mock.patch('sushy.resources.oem.common._global_extn_mgrs_by_resource', {})
def test_export_system_configuration_destructive_fields(self):
oem = self.manager.get_oem_extension('Dell')
oem._export_system_configuration = mock.Mock()
with open('sushy/tests/oem/dell/unit/json_samples/'
'export_configuration_idrac.json') as f:
mock_response = oem._export_system_configuration.return_value
mock_response.json.return_value = json.load(f)
mock_response.status_code = 200
response = oem.export_system_configuration(
include_destructive_fields=False)
response_json = json.loads(response._content)
# From 40 items in test data 16 should be removed
self.assertEqual(24, len(response_json['SystemConfiguration']
['Components'][0]['Attributes']))
include_in_export = mgr_cons.INCLUDE_EXPORT_READ_ONLY_PASSWORD_HASHES
oem._export_system_configuration.assert_called_once_with(
mgr_cons.EXPORT_TARGET_ALL,
export_use=mgr_cons.EXPORT_USE_CLONE,
include_in_export=include_in_export)
@mock.patch('sushy.resources.oem.common._global_extn_mgrs_by_resource', {})
def test_get_pxe_port_macs_bios(self):
oem = self.manager.get_oem_extension('Dell')
oem._export_system_configuration = mock.Mock()
with open('sushy/tests/oem/dell/unit/json_samples/'
'export_configuration_nic_bios.json') as f:
mock_response = oem._export_system_configuration.return_value
mock_response.json.return_value = json.load(f)
mock_response.status_code = 200
ethernet_interfaces_mac = {'NIC.Integrated.1-4-1': '68:05:CA:AF:AA:C9',
'NIC.Slot.7-2-1': '3C:FD:FE:CD:67:31',
'NIC.Slot.7-1-1': '3C:FD:FE:CD:67:30',
'NIC.Integrated.1-2-1': '68:05:CA:AF:AA:C7',
'NIC.Integrated.1-3-1': '68:05:CA:AF:AA:C8',
'NIC.Integrated.1-1-1': '68:05:CA:AF:AA:C6'}
self.assertEqual(["68:05:CA:AF:AA:C8"],
oem.get_pxe_port_macs_bios(ethernet_interfaces_mac))
@mock.patch('sushy.resources.oem.common._global_extn_mgrs_by_resource', {})
def test_get_pxe_port_macs_bios_invalid_system_config_tag(self):
oem = self.manager.get_oem_extension('Dell')
oem._export_system_configuration = mock.Mock()
mock_response = oem._export_system_configuration.return_value
mock_response.json.return_value = {'Model': 'PowerEdge R7525'}
mock_response.status_code = 200
ethernet_interfaces_mac = {'NIC.Integrated.1-4-1': '68:05:CA:AF:AA:C9',
'NIC.Slot.7-2-1': '3C:FD:FE:CD:67:31',
'NIC.Slot.7-1-1': '3C:FD:FE:CD:67:30',
'NIC.Integrated.1-2-1': '68:05:CA:AF:AA:C7',
'NIC.Integrated.1-3-1': '68:05:CA:AF:AA:C8',
'NIC.Integrated.1-1-1': '68:05:CA:AF:AA:C6'}
self.assertRaises(sushy.exceptions.ExtensionError,
oem.get_pxe_port_macs_bios, ethernet_interfaces_mac)
@mock.patch('sushy.resources.oem.common._global_extn_mgrs_by_resource', {})
def test_get_pxe_port_macs_bios_invalid_response(self):
oem = self.manager.get_oem_extension('Dell')
oem._export_system_configuration = mock.Mock()
mock_response = oem._export_system_configuration.return_value
mock_response.status_code = 204
ethernet_interfaces_mac = {'NIC.Integrated.1-4-1': '68:05:CA:AF:AA:C9',
'NIC.Slot.7-2-1': '3C:FD:FE:CD:67:31',
'NIC.Slot.7-1-1': '3C:FD:FE:CD:67:30',
'NIC.Integrated.1-2-1': '68:05:CA:AF:AA:C7',
'NIC.Integrated.1-3-1': '68:05:CA:AF:AA:C8',
'NIC.Integrated.1-1-1': '68:05:CA:AF:AA:C6'}
self.assertRaises(sushy.exceptions.ExtensionError,
oem.get_pxe_port_macs_bios, ethernet_interfaces_mac)
def test_idrac_card_service(self):
oem = self.manager.get_oem_extension('Dell')
with open('sushy/tests/oem/dell/unit/json_samples/'
'idrac_card_service.json') as f:
mock_response = self.conn.get.return_value
mock_response.json.return_value = json.load(f)
mock_response.status_code = 200
idrac_card_service = oem.idrac_card_service
self.assertEqual(
'/redfish/v1/Dell/Managers/iDRAC.Embedded.1/DelliDRACCardService',
idrac_card_service.path)
self.assertIsInstance(idrac_card_service,
idrac_card.DelliDRACCardService)
@mock.patch('sushy.resources.oem.common._global_extn_mgrs_by_resource', {})
def test_lifecycle_service(self):
oem = self.manager.get_oem_extension('Dell')
with open('sushy/tests/oem/dell/unit/json_samples/'
'lifecycle_service.json') as f:
mock_response = self.conn.get.return_value
mock_response.json.return_value = json.load(f)
mock_response.status_code = 200
lifecycle_service = oem.lifecycle_service
self.assertEqual(
'/redfish/v1/Dell/Managers/iDRAC.Embedded.1/DellLCService',
lifecycle_service.path)
self.assertIsInstance(lifecycle_service,
lifecycle.DellLCService)
@mock.patch('sushy.resources.oem.common._global_extn_mgrs_by_resource', {})
def test_job_service(self):
oem = self.manager.get_oem_extension('Dell')
with open('sushy/tests/oem/dell/unit/json_samples/'
'job_service.json') as f:
mock_response = self.conn.get.return_value
mock_response.json.return_value = json.load(f)
mock_response.status_code = 200
job_service = oem.job_service
self.assertEqual(
'/redfish/v1/Dell/Managers/iDRAC.Embedded.1/DellJobService',
job_service.path)
self.assertIsInstance(job_service,
job.DellJobService)
@mock.patch('sushy.resources.oem.common._global_extn_mgrs_by_resource', {})
def test_job_collection(self):
oem = self.manager.get_oem_extension('Dell')
with open('sushy/tests/oem/dell/unit/json_samples/'
'job_collection_expanded.json') as f:
mock_response = self.conn.get.return_value
mock_response.json.return_value = json.load(f)
mock_response.status_code = 200
job_collection = oem.job_collection
self.assertEqual(
'/redfish/v1/Managers/iDRAC.Embedded.1/Jobs',
job_collection.path)
self.assertIsInstance(job_collection,
jc.DellJobCollection)
def test_get_allowed_import_shutdown_type_values(self):
oem = self.manager.get_oem_extension('Dell')
expected_values = {mgr_cons.ShutdownType.GRACEFUL,
mgr_cons.ShutdownType.FORCED,
mgr_cons.ShutdownType.NO_REBOOT}
allowed_values = oem.get_allowed_import_shutdown_type_values()
self.assertIsInstance(allowed_values, set)
self.assertEqual(expected_values, allowed_values)
@mock.patch.object(oem_manager, 'LOG', autospec=True)
def test_get_allowed_import_shutdown_type_values_missing(self, mock_log):
oem = self.manager.get_oem_extension('Dell')
import_action = ('OemManager.v1_0_0'
'#OemManager.ImportSystemConfiguration')
oem.json['Actions']['Oem'][import_action].pop(
'ShutdownType@Redfish.AllowableValues')
oem.refresh()
expected_values = {mgr_cons.ShutdownType.GRACEFUL,
mgr_cons.ShutdownType.FORCED,
mgr_cons.ShutdownType.NO_REBOOT}
allowed_values = oem.get_allowed_import_shutdown_type_values()
self.assertIsInstance(allowed_values, set)
self.assertEqual(expected_values, allowed_values)
mock_log.warning.assert_called_once()
def test_import_system_configuration(self):
oem = self.manager.get_oem_extension('Dell')
result = oem.import_system_configuration('{"key": "value"}')
self.conn.post.assert_called_once_with(
'/redfish/v1/Managers/iDRAC.Embedded.1/Actions/Oem/EID_674_Manager'
'.ImportSystemConfiguration', data={'ShareParameters':
{'Target': 'ALL'},
'ImportBuffer':
'{"key": "value"}',
'ShutdownType': 'NoReboot'})
self.assertIsInstance(result, TaskMonitor)
self.assertEqual('/redfish/v1/TaskService/Tasks/JID_905749031119',
result.task_monitor_uri)
def test_reset_idrac_with_wait_true(self):
oem_manager = self.manager.get_oem_extension('Dell')
oem_manager.idrac_card_service.reset_idrac = mock.Mock()
oem_manager._conn._url = "https://1.2.3.4"
oem_manager._wait_for_idrac = mock.Mock()
oem_manager._wait_until_idrac_is_ready = mock.Mock()
oem_manager.reset_idrac(wait=True)
oem_manager.idrac_card_service.reset_idrac.assert_called()
oem_manager._wait_for_idrac.assert_called_with('1.2.3.4', 60)
oem_manager._wait_until_idrac_is_ready.assert_called_with(
'1.2.3.4', 96, 10)
def test_reset_idrac_with_wait_false(self):
oem_manager = self.manager.get_oem_extension('Dell')
oem_manager.idrac_card_service.reset_idrac = mock.Mock()
oem_manager._wait_for_idrac = mock.Mock()
oem_manager._wait_until_idrac_is_ready = mock.Mock()
oem_manager.reset_idrac(wait=False)
oem_manager.idrac_card_service.reset_idrac.assert_called()
oem_manager._wait_for_idrac.assert_not_called()
oem_manager._wait_until_idrac_is_ready.assert_not_called()
def test__wait_until_idrac_is_ready(self):
oem_manager = self.manager.get_oem_extension('Dell')
oem_manager.lifecycle_service.is_idrac_ready = mock.Mock()
oem_manager.lifecycle_service.is_idrac_ready.side_effect = \
[False, True]
oem_manager._wait_until_idrac_is_ready('1.2.3.4', 96, 10)
oem_manager.lifecycle_service.is_idrac_ready.assert_called_with()
@mock.patch('time.sleep', autospec=True)
def test__wait_until_idrac_is_ready_with_timeout(self, mock_time_sleep):
oem_manager = self.manager.get_oem_extension('Dell')
oem_manager.lifecycle_service.is_idrac_ready = mock.Mock()
oem_manager.lifecycle_service.is_idrac_ready.return_value = False
self.assertRaises(sushy.exceptions.ExtensionError,
oem_manager._wait_until_idrac_is_ready,
'1.2.3.4', 96, 10)
@mock.patch('time.sleep', autospec=True)
def test__wait_for_idrac_with_state_reached(self, mock_time_sleep):
oem_manager = self.manager.get_oem_extension('Dell')
oem_manager._wait_for_idrac_state = mock.Mock()
oem_manager._wait_for_idrac_state.return_value = True
oem_manager._wait_for_idrac('1.2.3.4', 30)
oem_manager._wait_for_idrac_state.assert_called_with(
'1.2.3.4', alive=True, ping_count=3, retries=24)
oem_manager._wait_for_idrac_state.assert_any_call(
'1.2.3.4', alive=False, ping_count=2, retries=24)
self.assertEqual(2, oem_manager._wait_for_idrac_state.call_count)
@mock.patch('time.sleep', autospec=True)
def test__wait_for_idrac_with_first_state_not_reached(self,
mock_time_sleep):
oem_manager = self.manager.get_oem_extension('Dell')
oem_manager._wait_for_idrac_state = mock.Mock()
oem_manager._wait_for_idrac_state.return_value = False
self.assertRaises(sushy.exceptions.ExtensionError,
oem_manager._wait_for_idrac, '1.2.3.4', 30)
@mock.patch('time.sleep', autospec=True)
def test__wait_for_idrac_with_second_state_not_reached(self,
mock_time_sleep):
oem_manager = self.manager.get_oem_extension('Dell')
oem_manager._wait_for_idrac_state = mock.Mock()
oem_manager._wait_for_idrac_state.side_effect = [True, False]
self.assertRaises(sushy.exceptions.ExtensionError,
oem_manager._wait_for_idrac, '1.2.3.4', 30)
@mock.patch('time.sleep', autospec=True)
def test__wait_for_idrac_state_with_pingable(self, mock_time_sleep):
oem_manager = self.manager.get_oem_extension('Dell')
oem_manager._ping_host = mock.Mock()
oem_manager._ping_host.return_value = True
response = oem_manager._wait_for_idrac_state('1.2.3.4')
self.assertEqual(True, response)
self.assertEqual(3, oem_manager._ping_host.call_count)
@mock.patch('time.sleep', autospec=True)
def test__wait_for_idrac_state_without_pingable(self, mock_time_sleep):
oem_manager = self.manager.get_oem_extension('Dell')
oem_manager._ping_host = mock.Mock()
oem_manager._ping_host.return_value = False
response = oem_manager._wait_for_idrac_state('1.2.3.4')
self.assertEqual(False, response)
self.assertEqual(24, oem_manager._ping_host.call_count)
@mock.patch('subprocess.call', autospec=True)
def test__ping_host_alive(self, mock_call):
oem_manager = self.manager.get_oem_extension('Dell')
mock_call.return_value = 0
result = oem_manager._ping_host('1.2.3.4')
self.assertTrue(result)
mock_call.assert_called_with(["ping", "-c", "1", '1.2.3.4'])
@mock.patch('subprocess.call', autospec=True)
def test__ping_host_not_alive(self, mock_call):
oem_manager = self.manager.get_oem_extension('Dell')
mock_call.return_value = 1
result = oem_manager._ping_host('1.2.3.4')
self.assertFalse(result)
mock_call.assert_called_with(["ping", "-c", "1", '1.2.3.4'])

View File

@ -0,0 +1,76 @@
# Copyright (c) 2022 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 json
from unittest import mock
from oslotest.base import BaseTestCase
import sushy
from sushy.resources.system.storage import controller as sushy_constroller
from sushy.oem.dell.resources.system.storage import constants as ctrl_cons
from sushy.oem.dell.resources.system.storage import controller as oem_ctrl
class ControllerTestCase(BaseTestCase):
def setUp(self):
super().setUp()
self.conn = mock.Mock()
with open('sushy/tests/oem/dell/unit/json_samples/'
'storage_controller.json') as f:
mock_response = self.conn.get.return_value
mock_response.json.return_value = json.load(f)
mock_response.status_code = 200
self.controller = sushy_constroller.StorageController(
self.conn,
'/redfish/v1/Systems/437XR1138R2/Storage/1/Controllers/1')
self.oem_controller = oem_ctrl.DellStorageControllerExtension(
self.conn,
'/redfish/v1/Systems/437XR1138R2/Storage/1/Controllers/1')
self.oem_controller = self.oem_controller.set_parent_resource(
self.controller, 'Dell')
def test_parse_attributes(self):
self.assertEqual(
ctrl_cons.ControllerMode.EHBA,
self.oem_controller.dell_storage_controller.controller_mode)
def test_convert_to_raid(self):
mock_controller = mock.Mock()
mock_task_monitor = mock.Mock()
mock_controller.update.return_value = mock_task_monitor
self.oem_controller._parent_resource = mock_controller
res = self.oem_controller.convert_to_raid()
self.assertEqual(mock_task_monitor, res)
patch = {"Oem": {"Dell": {"DellStorageController": {
"ControllerMode": "RAID"}}}}
mock_controller.update.assert_called_once_with(
patch, apply_time=sushy.ApplyTime.ON_RESET)
def test_convert_to_raid_already_raid(self):
mock_controller = mock.Mock()
self.oem_controller._parent_resource = mock_controller
json = self.oem_controller.json
json['Oem']['Dell']['DellStorageController']['ControllerMode'] = 'RAID'
self.oem_controller.refresh()
res = self.oem_controller.convert_to_raid()
self.assertIsNone(res)
mock_controller.update.assert_not_called()

View File

@ -0,0 +1,209 @@
# Copyright (c) 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 json
from unittest import mock
from oslotest.base import BaseTestCase
from sushy import exceptions
from sushy import taskmonitor
from sushy.oem.dell.resources.system import raid_service
class DellRaidService(BaseTestCase):
def setUp(self):
super().setUp()
self.conn = mock.Mock()
with open('sushy/tests/oem/dell/unit/json_samples/'
'raid_service.json') as f:
mock_response = self.conn.get.return_value
mock_response.json.return_value = json.load(f)
mock_response.status_code = 200
self.mock_response = mock.Mock()
self.mock_response.status_code = 202
self.mock_response.headers = {
'Location': '/redfish/v1/Managers/iDRAC.Embedded.1/Oem/Dell/Jobs/'
'JID_999888777666'
}
self.root = mock.Mock()
self.raid_service = raid_service.DellRaidService(
self.conn,
'/redfish/v1/Systems/System.Embedded.1/Oem/Dell/'
'DellRaidService', root=self.root
)
@mock.patch.object(raid_service.DellRaidService,
'_get_task_monitor_from_dell_job', autospec=True)
def test_convert_to_raid(self, mock_get_task_mon):
mock_task_mon = mock.Mock()
mock_get_task_mon.return_value = mock_task_mon
fqdds = ["Disk.Bay.0:Enclosure.Internal.0-1:RAID.Integrated.1-1",
"Disk.Bay.1:Enclosure.Internal.0-1:RAID.Integrated.1-1"]
task_mon = self.raid_service.convert_to_raid(fqdds)
self.conn.post.assert_called_once_with(
'/redfish/v1/Systems/System.Embedded.1/Oem/Dell/DellRaidService/'
'Actions/DellRaidService.ConvertToRAID',
data={'PDArray': fqdds})
self.assertEqual(mock_task_mon, task_mon)
@mock.patch.object(raid_service.DellRaidService,
'_get_task_monitor_from_dell_job', autospec=True)
def test_convert_to_nonraid(self, mock_get_task_mon):
mock_task_mon = mock.Mock()
mock_get_task_mon.return_value = mock_task_mon
fqdds = ["Disk.Bay.0:Enclosure.Internal.0-1:RAID.Integrated.1-1",
"Disk.Bay.1:Enclosure.Internal.0-1:RAID.Integrated.1-1"]
task_mon = self.raid_service.convert_to_nonraid(fqdds)
self.conn.post.assert_called_once_with(
'/redfish/v1/Systems/System.Embedded.1/Oem/Dell/DellRaidService/'
'Actions/DellRaidService.ConvertToNonRAID',
data={'PDArray': fqdds})
self.assertEqual(mock_task_mon, task_mon)
@mock.patch.object(raid_service.DellRaidService,
'_get_task_monitor_from_dell_job', autospec=True)
def test_clear_foreign_config(self, mock_get_task_mon):
mock_task_mon = mock.Mock()
mock_get_task_mon.return_value = mock_task_mon
result = self.raid_service.clear_foreign_config('RAID.Integrated.1-1')
self.conn.post.assert_called_once_with(
'/redfish/v1/Systems/System.Embedded.1/Oem/Dell/DellRaidService/'
'Actions/DellRaidService.ClearForeignConfig',
data={'TargetFQDD': 'RAID.Integrated.1-1'})
self.assertEqual(mock_task_mon, result)
def test_clear_foreign_config_no_config(self):
mock_response = mock.Mock()
mock_response.status_code = 400
mock_response.json.return_value = {
"error": {
"@Message.ExtendedInfo": [
{
"Message": "No foreign configurations detected.",
"MessageArgs": [],
"MessageArgs@odata.count": 0,
"MessageId": "IDRAC.2.5.STOR018",
"RelatedProperties": [],
"RelatedProperties@odata.count": 0,
"Resolution": "If the only foreign drives present are "
"in a secured locked state, run a "
"secure erase operation on the drives "
"to securely erase data or unlock these "
"drives and retry the operation. "
"Otherwise the operation was not "
"successful because there are no "
"foreign drives.",
"Severity": "Warning"
}
],
"code": "Base.1.8.GeneralError",
"message": "A general error has occurred. See ExtendedInfo "
"for more information"
}
}
no_config_error = exceptions.BadRequestError(
'POST', '/redfish/v1/Dell/Systems/System.Embedded.1/'
'DellRaidService/Actions/DellRaidService.ClearForeignConfig',
mock_response)
self.conn.post.side_effect = no_config_error
result = self.raid_service.clear_foreign_config('RAID.Integrated.1-1')
self.assertIsNone(result)
def test_clear_foreign_config_bad_request(self):
mock_response = mock.Mock()
mock_response.status_code = 400
mock_response.json.return_value = {
"error": {
"@Message.ExtendedInfo": [
{
"Message": "Controller not found.",
"MessageArgs": [],
"MessageArgs@odata.count": 0,
"MessageId": "IDRAC.2.4.STOR030",
"RelatedProperties": [],
"RelatedProperties@odata.count": 0,
"Resolution": "Provide a valid controller FQDD (Fully "
"Qualified Device Descriptor) and retry "
"the operation.",
"Severity": "Warning"
}
],
"code": "Base.1.7.GeneralError",
"message": "A general error has occurred. See ExtendedInfo "
"for more information"
}
}
no_config_error = exceptions.BadRequestError(
'POST', '/redfish/v1/Dell/Systems/System.Embedded.1/'
'DellRaidService/Actions/DellRaidService.ClearForeignConfig',
mock_response)
self.conn.post.side_effect = no_config_error
self.assertRaises(exceptions.BadRequestError,
self.raid_service.clear_foreign_config,
'RAID.Integrated.999')
def test__get_task_monitor_from_dell_job(self):
mock_task1 = mock.Mock(identity='JID_111222333444',
path='/TaskService/Task/JID_111222333444')
mock_task2 = mock.Mock(identity='JID_999888777666',
path='/TaskService/Task/JID_999888777666')
mock_tasks = mock.Mock()
mock_tasks.get_members.return_value = [mock_task1, mock_task2]
self.root.get_task_service.return_value.tasks = mock_tasks
task_mon = self.raid_service._get_task_monitor_from_dell_job(
self.mock_response)
self.assertIsInstance(task_mon, taskmonitor.TaskMonitor)
self.assertEqual('/TaskService/Task/JID_999888777666',
task_mon.task_monitor_uri)
def test__get_task_monitor_from_dell_job_location_missing(self):
mock_response = mock.Mock()
mock_response.status_code = 202
mock_response.headers = {
'Connection': 'Keep-Alive'
}
self.assertRaisesRegex(
exceptions.ExtensionError,
'does not include Location',
self.raid_service._get_task_monitor_from_dell_job, mock_response)
def test__get_task_monitor_from_dell_job_task_not_found(self):
mock_task1 = mock.Mock(identity='JID_000000000000',
path='/TaskService/Task/JID_000000000000')
mock_tasks = mock.Mock()
mock_tasks.get_members.return_value = [mock_task1]
self.root.get_task_service.return_value.tasks = mock_tasks
self.assertRaisesRegex(
exceptions.ExtensionError,
'not find task by id',
self.raid_service._get_task_monitor_from_dell_job,
self.mock_response)

View File

@ -0,0 +1,123 @@
# Copyright (c) 2021-2022 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 json
from unittest import mock
from oslotest.base import BaseTestCase
from sushy import exceptions
from sushy.oem.dell.resources.system import constants as sys_cons
from sushy.oem.dell.resources.system import raid_service
from sushy.oem.dell.resources.system import system as oem_system
class SystemTestCase(BaseTestCase):
def setUp(self):
super().setUp()
self.conn = mock.Mock()
with open('sushy/tests/oem/dell/unit/json_samples/'
'system.json') as f:
mock_response = self.conn.get.return_value
mock_response.json.return_value = json.load(f)
mock_response.status_code = 200
self.oem_system = oem_system.DellSystemExtension(
self.conn, '/redfish/v1/Systems/System.Embedded.1')
mock_perc_raid = mock.Mock(
identity='Disk.Bay.0:Enclosure.Internal.0-1:RAID.Integrated.1-1')
type(mock_perc_raid).volumes = mock.PropertyMock(
side_effect=exceptions.MissingAttributeError)
mock_perc_nonraid = mock.Mock(
identity='Disk.Bay.1:Enclosure.Internal.0-1:RAID.Integrated.1-1',
volumes=[mock.Mock(volume_type='rawdevice', raid_type=None)])
mock_boss_controller = mock.MagicMock(raid_types=['RAID1'])
mock_boss_controller.name = 'BOSS-S1'
mock_boss = mock.Mock(storage_controllers=[mock_boss_controller],
drives=mock.Mock())
mock_perc_controller = mock.MagicMock(raid_types=['RAID1'])
mock_perc_controller.name = 'PERC'
mock_perc = mock.Mock(storage_controllers=[mock_perc_controller],
drives=[mock_perc_raid, mock_perc_nonraid])
mock_system = mock.Mock()
mock_storage_nocontroller = mock.Mock(storage_controllers=[])
mock_storage_nonraid = mock.Mock(
storage_controllers=[mock.Mock(raid_types=[])])
mock_storage_boss = mock_boss
mock_storage_raid = mock_perc
mock_system.storage.get_members.return_value = [
mock_storage_nocontroller, mock_storage_nonraid, mock_storage_boss,
mock_storage_raid]
self.oem_system._parent_resource = mock_system
def test_raid_service(self):
with open('sushy/tests/oem/dell/unit/json_samples/'
'raid_service.json') as f:
mock_response = self.conn.get.return_value
mock_response.json.return_value = json.load(f)
mock_response.status_code = 200
result = self.oem_system.raid_service
self.assertEqual(
'/redfish/v1/Systems/System.Embedded.1/Oem/Dell/DellRaidService',
result.path)
self.assertIsInstance(result,
raid_service.DellRaidService)
def test_change_physical_disk_state_raid(self):
mock_taskmon = mock.Mock()
mock_raid = mock.Mock()
mock_raid.return_value = mock_taskmon
mock_nonraid = mock.Mock()
self.oem_system.raid_service.convert_to_raid = mock_raid
self.oem_system.raid_service.convert_to_nonraid = mock_nonraid
task_mons = self.oem_system.change_physical_disk_state(
sys_cons.PhysicalDiskStateMode.RAID)
self.assertEqual([mock_taskmon], task_mons)
mock_raid.assert_called_once_with(
['Disk.Bay.1:Enclosure.Internal.0-1:RAID.Integrated.1-1'])
mock_nonraid.assert_not_called()
def test_change_physical_disk_state_nonraid(self):
mock_taskmon = mock.Mock()
mock_raid = mock.Mock()
mock_nonraid = mock.Mock()
mock_nonraid.return_value = mock_taskmon
self.oem_system.raid_service.convert_to_raid = mock_raid
self.oem_system.raid_service.convert_to_nonraid = mock_nonraid
task_mons = self.oem_system.change_physical_disk_state(
sys_cons.PhysicalDiskStateMode.NONRAID)
self.assertEqual([mock_taskmon], task_mons)
mock_raid.assert_not_called()
mock_nonraid.assert_called_once_with(
['Disk.Bay.0:Enclosure.Internal.0-1:RAID.Integrated.1-1'])
def test_clear_foreign_config(self):
mock_taskmon = mock.Mock()
mock_clear_foreign_config = mock.Mock()
mock_clear_foreign_config.side_effect = [mock_taskmon]
self.oem_system.raid_service.clear_foreign_config =\
mock_clear_foreign_config
task_mons = self.oem_system.clear_foreign_config()
self.assertEqual([mock_taskmon], task_mons)

View File

@ -0,0 +1,66 @@
# Copyright (c) 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 json
from unittest import mock
from oslotest.base import BaseTestCase
from sushy.resources.taskservice import task as sushy_task
from sushy.oem.dell.resources.taskservice import constants as ts_cons
from sushy.oem.dell.resources.taskservice import task
class TaskTestCase(BaseTestCase):
def setUp(self):
super().setUp()
self.conn = mock.Mock()
with open('sushy/tests/oem/dell/unit/json_samples/'
'task.json') as f:
mock_response = self.conn.get.return_value
mock_response.json.return_value = json.load(f)
mock_response.status_code = 200
self.task = sushy_task.Task(
self.conn,
'/redfish/v1/TaskService/Tasks/JID_257309938313')
self.oem_task = task.DellTaskExtension(
self.conn,
'/redfish/v1/TaskService/Tasks/JID_257309938313')
self.oem_task = self.oem_task.set_parent_resource(
self.task, 'Dell')
def test_parse_attributes(self):
self.assertEqual('JID_257309938313', self.oem_task.identity)
self.assertEqual('Configure: RAID.Integrated.1-1',
self.oem_task.name)
self.assertEqual('Job Instance', self.oem_task.description)
self.assertIsNone(self.oem_task.completion_time)
self.assertEqual('TIME_NA', self.oem_task.end_time)
self.assertEqual(ts_cons.JobState.SCHEDULED,
self.oem_task.job_state)
self.assertEqual(ts_cons.JobType.RAID_CONF,
self.oem_task.job_type)
# For backward compatibility
self.assertEqual(ts_cons.JOB_STATE_SCHEDULED,
self.oem_task.job_state)
self.assertEqual(ts_cons.JOB_TYPE_RAID_CONF,
self.oem_task.job_type)
self.assertEqual('Task successfully scheduled.',
self.oem_task.message)
self.assertEqual([], self.oem_task.message_args)
self.assertEqual('IDRAC.2.5.JCP001', self.oem_task.message_id)
self.assertEqual(0, self.oem_task.percent_complete)
self.assertEqual('TIME_NOW', self.oem_task.start_time)

View File

@ -0,0 +1,55 @@
# Copyright (c) 2020 Dell Inc. or its subsidiaries.
#
# All Rights Reserved.
#
# 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 unittest import mock
from oslotest.base import BaseTestCase
import sushy
from sushy.oem.dell.asynchronous import http_call
class AsychronousTestCase(BaseTestCase):
def setUp(self):
super().setUp()
self.conn = mock.Mock()
def test_http_call_post_accepted(self):
mock_post_response = self.conn.post.return_value
mock_post_response.status_code = 202
mock_post_response.headers.get.return_value = '1'
mock_get_202_response = mock.Mock()
mock_get_202_response.status_code = 202
mock_get_202_response.headers.get.return_value = '1'
mock_get_200_response = mock.Mock()
mock_get_200_response.status_code = 200
self.conn.get.side_effect = [
mock_get_202_response, mock_get_200_response]
resp = http_call(self.conn, 'POST')
self.assertIs(resp, mock_get_200_response)
def test_http_call_post_accepted_no_location(self):
mock_response = self.conn.post.return_value
mock_response.status_code = 202
mock_response.headers.get.return_value = None
self.assertRaises(sushy.exceptions.ExtensionError,
http_call, self.conn, 'POST')

View File

@ -0,0 +1,42 @@
# Copyright 2017 Red Hat, Inc.
# All Rights Reserved.
#
# 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 json
from unittest import mock
from oslotest.base import BaseTestCase
from sushy import main
class RootTestCase(BaseTestCase):
@mock.patch('sushy.auth.SessionOrBasicAuth', autospec=True)
@mock.patch('sushy.connector.Connector', autospec=True)
@mock.patch('sushy.resources.sessionservice.sessionservice.'
'SessionService', autospec=True)
def setUp(self, mock_session_service, mock_connector, mock_auth):
super().setUp()
self.conn = mock.Mock()
self.sess_serv = mock.Mock()
self.sess_serv.create_session.return_value = (None, None)
mock_session_service.return_value = self.sess_serv
mock_connector.return_value = self.conn
with open('sushy/tests/oem/dell/unit/json_samples/root.json') as f:
self.conn.get.return_value.json.return_value = json.load(f)
self.root = main.Sushy('http://foo.bar:1234',
verify=True, auth=mock_auth)
def test_oem_vendors(self):
self.assertEqual(['Dell'], self.root.oem_vendors)