Merge "migrate sushy_oem_idrac to sushy"
This commit is contained in:
commit
880ab20930
7
releasenotes/notes/sushy-oem-idrac-f4a1f2202b7955d7.yaml
Normal file
7
releasenotes/notes/sushy-oem-idrac-f4a1f2202b7955d7.yaml
Normal 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.
|
@ -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
0
sushy/oem/__init__.py
Normal file
18
sushy/oem/dell/__init__.py
Normal file
18
sushy/oem/dell/__init__.py
Normal 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
|
76
sushy/oem/dell/asynchronous.py
Normal file
76
sushy/oem/dell/asynchronous.py
Normal 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
|
20
sushy/oem/dell/constants.py
Normal file
20
sushy/oem/dell/constants.py
Normal 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']
|
0
sushy/oem/dell/resources/__init__.py
Normal file
0
sushy/oem/dell/resources/__init__.py
Normal file
76
sushy/oem/dell/resources/attributes.py
Normal file
76
sushy/oem/dell/resources/attributes.py
Normal 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')
|
0
sushy/oem/dell/resources/manager/__init__.py
Normal file
0
sushy/oem/dell/resources/manager/__init__.py
Normal file
161
sushy/oem/dell/resources/manager/constants.py
Normal file
161
sushy/oem/dell/resources/manager/constants.py
Normal 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
|
109
sushy/oem/dell/resources/manager/idrac_card_service.py
Normal file
109
sushy/oem/dell/resources/manager/idrac_card_service.py
Normal 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
|
56
sushy/oem/dell/resources/manager/job_collection.py
Normal file
56
sushy/oem/dell/resources/manager/job_collection.py
Normal 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
|
59
sushy/oem/dell/resources/manager/job_service.py
Normal file
59
sushy/oem/dell/resources/manager/job_service.py
Normal 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)
|
79
sushy/oem/dell/resources/manager/lifecycle_service.py
Normal file
79
sushy/oem/dell/resources/manager/lifecycle_service.py
Normal 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')
|
681
sushy/oem/dell/resources/manager/manager.py
Normal file
681
sushy/oem/dell/resources/manager/manager.py
Normal 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
|
30
sushy/oem/dell/resources/system/constants.py
Normal file
30
sushy/oem/dell/resources/system/constants.py
Normal 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
|
147
sushy/oem/dell/resources/system/raid_service.py
Normal file
147
sushy/oem/dell/resources/system/raid_service.py
Normal 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)
|
28
sushy/oem/dell/resources/system/storage/constants.py
Normal file
28
sushy/oem/dell/resources/system/storage/constants.py
Normal 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."""
|
56
sushy/oem/dell/resources/system/storage/controller.py
Normal file
56
sushy/oem/dell/resources/system/storage/controller.py
Normal 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
|
165
sushy/oem/dell/resources/system/system.py
Normal file
165
sushy/oem/dell/resources/system/system.py
Normal 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
|
245
sushy/oem/dell/resources/taskservice/constants.py
Normal file
245
sushy/oem/dell/resources/taskservice/constants.py
Normal 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
|
73
sushy/oem/dell/resources/taskservice/task.py
Normal file
73
sushy/oem/dell/resources/taskservice/task.py
Normal 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
40
sushy/oem/dell/utils.py
Normal 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')
|
0
sushy/tests/oem/__init__.py
Normal file
0
sushy/tests/oem/__init__.py
Normal file
0
sushy/tests/oem/dell/__init__.py
Normal file
0
sushy/tests/oem/dell/__init__.py
Normal file
0
sushy/tests/oem/dell/functional/__init__.py
Normal file
0
sushy/tests/oem/dell/functional/__init__.py
Normal file
109
sushy/tests/oem/dell/functional/vmedia_boot.py
Normal file
109
sushy/tests/oem/dell/functional/vmedia_boot.py
Normal 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())
|
0
sushy/tests/oem/dell/unit/__init__.py
Normal file
0
sushy/tests/oem/dell/unit/__init__.py
Normal file
20
sushy/tests/oem/dell/unit/base.py
Normal file
20
sushy/tests/oem/dell/unit/base.py
Normal 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"""
|
@ -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."
|
||||
}
|
||||
}
|
@ -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."
|
||||
}
|
||||
}
|
@ -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
@ -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"
|
||||
}
|
@ -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"
|
||||
}
|
13
sushy/tests/oem/dell/unit/json_samples/job_service.json
Normal file
13
sushy/tests/oem/dell/unit/json_samples/job_service.json
Normal 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"
|
||||
}
|
@ -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"
|
||||
}
|
274
sushy/tests/oem/dell/unit/json_samples/manager.json
Normal file
274
sushy/tests/oem/dell/unit/json_samples/manager.json
Normal 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"
|
||||
}
|
||||
}
|
@ -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"
|
||||
}
|
213
sushy/tests/oem/dell/unit/json_samples/raid_service.json
Normal file
213
sushy/tests/oem/dell/unit/json_samples/raid_service.json
Normal 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"
|
||||
}
|
@ -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.
|
68
sushy/tests/oem/dell/unit/json_samples/root.json
Normal file
68
sushy/tests/oem/dell/unit/json_samples/root.json
Normal 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"
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
398
sushy/tests/oem/dell/unit/json_samples/system.json
Normal file
398
sushy/tests/oem/dell/unit/json_samples/system.json
Normal 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"
|
||||
}
|
38
sushy/tests/oem/dell/unit/json_samples/task.json
Normal file
38
sushy/tests/oem/dell/unit/json_samples/task.json
Normal 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"
|
||||
}
|
0
sushy/tests/oem/dell/unit/resources/__init__.py
Normal file
0
sushy/tests/oem/dell/unit/resources/__init__.py
Normal 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)
|
@ -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)
|
@ -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)
|
@ -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={})
|
668
sushy/tests/oem/dell/unit/resources/manager/test_manager.py
Normal file
668
sushy/tests/oem/dell/unit/resources/manager/test_manager.py
Normal 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'])
|
@ -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()
|
209
sushy/tests/oem/dell/unit/resources/system/test_raid_service.py
Normal file
209
sushy/tests/oem/dell/unit/resources/system/test_raid_service.py
Normal 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)
|
123
sushy/tests/oem/dell/unit/resources/system/test_system.py
Normal file
123
sushy/tests/oem/dell/unit/resources/system/test_system.py
Normal 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)
|
66
sushy/tests/oem/dell/unit/resources/taskservice/test_task.py
Normal file
66
sushy/tests/oem/dell/unit/resources/taskservice/test_task.py
Normal 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)
|
55
sushy/tests/oem/dell/unit/test_asynchronous.py
Normal file
55
sushy/tests/oem/dell/unit/test_asynchronous.py
Normal 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')
|
42
sushy/tests/oem/dell/unit/test_root.py
Normal file
42
sushy/tests/oem/dell/unit/test_root.py
Normal 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)
|
Loading…
x
Reference in New Issue
Block a user