Add `ChassisCollection` and `Chassis` classes

Add representation of Chassis and ChassisCollection resources.
The Chassis is used to represent a chassis or other physical enclosure
as a Redfish resource.

Also adds the methods get_chassis_collection and get_chassis in the
public API.

Implements: ChassisCollection and Chassis classes
Story: #2003853
Task: #26647

Change-Id: I59083562ff2ab3b18bfeebdabc0f4cfd663d01bb
This commit is contained in:
Gabriela Soria 2018-10-05 00:51:58 -07:00
parent 0480def823
commit c5d13e4385
18 changed files with 870 additions and 36 deletions

View File

@ -0,0 +1,5 @@
---
features:
- |
Adds support for the Chassis resource to the library.

View File

@ -20,6 +20,7 @@ from sushy.main import Sushy
from sushy.resources.constants import * # noqa
from sushy.resources.system.constants import * # noqa
from sushy.resources.manager.constants import * # noqa
from sushy.resources.chassis.constants import * # noqa
__all__ = ('Sushy',)
__version__ = pbr.version.VersionInfo(

View File

@ -18,6 +18,7 @@ from sushy import auth as sushy_auth
from sushy import connector as sushy_connector
from sushy import exceptions
from sushy.resources import base
from sushy.resources.chassis import chassis
from sushy.resources.manager import manager
from sushy.resources.registry import message_registry_file
from sushy.resources.sessionservice import session
@ -69,6 +70,9 @@ class Sushy(base.ResourceBase):
_managers_path = base.Field(['Managers', '@odata.id'])
"""ManagerCollection path"""
_chassis_path = base.Field(['Chassis', '@odata.id'])
"""ChassisCollection path"""
_session_service_path = base.Field(['SessionService', '@odata.id'])
"""SessionService path"""
@ -141,6 +145,29 @@ class Sushy(base.ResourceBase):
return system.System(self._conn, identity,
redfish_version=self.redfish_version)
def get_chassis_collection(self):
"""Get the ChassisCollection object
:raises: MissingAttributeError, if the collection attribute is
not found
:returns: a ChassisCollection object
"""
if not self._chassis_path:
raise exceptions.MissingAttributeError(
attribute='Chassis/@odata.id', resource=self._path)
return chassis.ChassisCollection(self._conn, self._chassis_path,
redfish_version=self.redfish_version)
def get_chassis(self, identity):
"""Given the identity return a Chassis object
:param identity: The identity of the Chassis resource
:returns: The Chassis object
"""
return chassis.Chassis(self._conn, identity,
redfish_version=self.redfish_version)
def get_manager_collection(self):
"""Get the ManagerCollection object

View File

View File

@ -0,0 +1,213 @@
# 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.
# This is referred from Redfish standard schema.
# http://redfish.dmtf.org/schemas/v1/Chassis.v1_8_0.json
from sushy import exceptions
from sushy.resources import base
from sushy.resources.chassis import mappings as cha_maps
from sushy.resources import common
from sushy.resources import mappings as res_maps
import logging
LOG = logging.getLogger(__name__)
class ActionsField(base.CompositeField):
reset = common.ResetActionField('#Chassis.Reset')
class PhysicalSecurity(base.CompositeField):
intrusion_sensor = base.MappedField('IntrusionSensor',
cha_maps.CHASSIS_INTRUSION_SENSOR_MAP)
"""IntrusionSensor
This indicates the known state of the physical security sensor, such as if
it is hardware intrusion detected.
"""
intrusion_sensor_number = base.Field('IntrusionSensorNumber')
"""A numerical identifier to represent the physical security sensor"""
intrusion_sensor_re_arm = (
base.MappedField('IntrusionSensorReArm',
cha_maps.CHASSIS_INTRUSION_SENSOR_RE_ARM_MAP))
"""This indicates how the Normal state to be restored"""
class Chassis(base.ResourceBase):
"""Chassis resource
The Chassis represents the physical components of a system. This
resource represents the sheet-metal confined spaces and logical zones
such as racks, enclosures, chassis and all other containers.
"""
chassis_type = base.MappedField('ChassisType',
cha_maps.CHASSIS_TYPE_VALUE_MAP,
required=True)
"""The type of physical form factor of the chassis"""
identity = base.Field('Id', required=True)
"""Identifier for the chassis"""
name = base.Field('Name', required=True)
"""The chassis name"""
asset_tag = base.Field('AssetTag')
"""The user assigned asset tag of this chassis"""
depth_mm = base.Field('DepthMm')
"""Depth in millimeters
The depth of the chassis. The value of this property shall represent
the depth (length) of the chassis (in millimeters) as specified by the
manufacturer.
"""
description = base.Field('Description')
"""The chassis description"""
height_mm = base.Field('HeightMm')
"""Height in millimeters
The height of the chassis. The value of this property shall represent
the height of the chassis (in millimeters) as specified by the
manufacturer.
"""
indicator_led = base.MappedField('IndicatorLED',
res_maps.INDICATOR_LED_VALUE_MAP)
"""The state of the indicator LED, used to identify the chassis"""
manufacturer = base.Field('Manufacturer')
"""The manufacturer of this chassis"""
model = base.Field('Model')
"""The model number of the chassis"""
part_number = base.Field('PartNumber')
"""The part number of the chassis"""
physical_security = PhysicalSecurity('PhysicalSecurity')
"""PhysicalSecurity
This value of this property shall contain the sensor state of the physical
security.
"""
power_state = base.MappedField('PowerState',
res_maps.POWER_STATE_VALUE_MAP)
"""The current power state of the chassis"""
serial_number = base.Field('SerialNumber')
"""The serial number of the chassis"""
sku = base.Field('SKU')
"""Stock-keeping unit number (SKU)
The value of this property shall be the stock-keeping unit number for
this chassis.
"""
status = common.StatusField('Status')
"""Status and Health
This property describes the status and health of the chassis and its
children.
"""
uuid = base.Field('UUID')
"""The Universal Unique Identifier (UUID) for this Chassis."""
weight_kg = base.Field('WeightKg')
"""Weight in kilograms
The value of this property shall represent the published mass (commonly
referred to as weight) of the chassis (in kilograms).
"""
width_mm = base.Field('WidthMm')
"""Width in millimeters
The value of this property shall represent the width of the chassis
(in millimeters) as specified by the manufacturer.
"""
_actions = ActionsField('Actions')
def __init__(self, connector, identity, redfish_version=None):
"""A class representing a Chassis
:param connector: A Connector instance
:param identity: The identity of the Chassis resource
:param redfish_version: The version of RedFish. Used to construct
the object according to schema of the given version.
"""
super(Chassis, self).__init__(connector, identity, redfish_version)
def _get_reset_action_element(self):
reset_action = self._actions.reset
if not reset_action:
raise exceptions.MissingActionError(action='#Chassis.Reset',
resource=self._path)
return reset_action
def get_allowed_reset_chassis_values(self):
"""Get the allowed values for resetting the chassis.
:returns: A set of allowed values.
:raises: MissingAttributeError, if Actions/#Chassis.Reset attribute
not present.
"""
reset_action = self._get_reset_action_element()
if not reset_action.allowed_values:
LOG.warning('Could not figure out the allowed values for the '
'reset chassis action for Chassis %s', self.identity)
return set(res_maps.RESET_TYPE_VALUE_MAP_REV)
return set([res_maps.RESET_TYPE_VALUE_MAP[v] for v in
set(res_maps.RESET_TYPE_VALUE_MAP).
intersection(reset_action.allowed_values)])
def reset_chassis(self, value):
"""Reset the chassis.
:param value: The target value.
:raises: InvalidParameterValueError, if the target value is not
allowed.
"""
valid_resets = self.get_allowed_reset_chassis_values()
if value not in valid_resets:
raise exceptions.InvalidParameterValueError(
parameter='value', value=value, valid_values=valid_resets)
value = res_maps.RESET_TYPE_VALUE_MAP_REV[value]
target_uri = self._get_reset_action_element().target_uri
LOG.debug('Resetting the Chassis %s ...', self.identity)
self._conn.post(target_uri, data={'ResetType': value})
LOG.info('The Chassis %s is being reset', self.identity)
class ChassisCollection(base.ResourceCollectionBase):
@property
def _resource_type(self):
return Chassis
def __init__(self, connector, path, redfish_version=None):
"""A class representing a ChassisCollection
:param connector: A Connector instance
:param path: The canonical path to the Chassis collection resource
:param redfish_version: The version of RedFish. Used to construct
the object according to schema of the given version.
"""
super(ChassisCollection, self).__init__(connector, path,
redfish_version)

View File

@ -0,0 +1,162 @@
# 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.
# Values comes from the Redfish Chassis json-schema 1.8.0:
# http://redfish.dmtf.org/schemas/v1/Chassis.v1_8_0.json#/definitions/Chassis
# Chassis Types constants
CHASSIS_TYPE_RACK = 'rack chassis type'
"""An equipment rack, typically a 19-inch wide freestanding unit"""
CHASSIS_TYPE_BLADE = 'blade chassis type'
"""Blade
An enclosed or semi-enclosed, typically vertically-oriented, system
chassis which must be plugged into a multi-system chassis to function
normally.
"""
CHASSIS_TYPE_ENCLOSURE = 'enclosure chassis type'
"""A generic term for a chassis that does not fit any other description"""
CHASSIS_TYPE_STAND_ALONE = 'stand alone chassis type'
"""StandAlone
A single, free-standing system, commonly called a tower or desktop
chassis.
"""
CHASSIS_TYPE_RACK_MOUNT = 'rack mount chassis type'
"""RackMount
A single system chassis designed specifically for mounting in an
equipment rack.
"""
CHASSIS_TYPE_CARD = 'card chassis type'
"""Card
A loose device or circuit board intended to be installed in a system or
other enclosure.
"""
CHASSIS_TYPE_CARTRIDGE = 'cartridge chassis type'
"""Cartridge
A small self-contained system intended to be plugged into a multi-system
chassis"""
CHASSIS_TYPE_ROW = 'row chassis type'
"""A collection of equipment rack"""
CHASSIS_TYPE_POD = 'pod chassis type'
"""Pod
A collection of equipment racks in a large, likely transportable,
container"""
CHASSIS_TYPE_EXPANSION = 'expansion chassis type'
"""A chassis which expands the capabilities or capacity of another chassis"""
CHASSIS_TYPE_SIDECAR = 'sidecar chassis type'
"""Sidecar
A chassis that mates mechanically with another chassis to expand its
capabilities or capacity.
"""
CHASSIS_TYPE_ZONE = 'zone chassis type'
"""Zone
A logical division or portion of a physical chassis that contains multiple
devices or systems that cannot be physically separated.
"""
CHASSIS_TYPE_SLED = 'sled chassis type'
"""Sled
An enclosed or semi-enclosed, system chassis which must be plugged into a
multi-system chassis to function normally similar to a blade type chassis.
"""
CHASSIS_TYPE_SHELF = 'shelf chassis type'
"""Shelf
An enclosed or semi-enclosed, typically horizontally-oriented, system chassis
which must be plugged into a multi-system chassis to function
normally.
"""
CHASSIS_TYPE_DRAWER = 'drawer chassis type'
"""Drawer
An enclosed or semi-enclosed, typically horizontally-oriented, system
chassis which may be slid into a multi-system chassis.
"""
CHASSIS_TYPE_MODULE = 'module chassis type'
"""Module
A small, typically removable, chassis or card which contains devices for
a particular subsystem or function.
"""
CHASSIS_TYPE_COMPONENT = 'component chassis type'
"""Component
A small chassis, card, or device which contains devices for a particular
subsystem or function.
"""
CHASSIS_TYPE_IP_BASED_DRIVE = 'IP based drive chassis type'
"""A chassis in a drive form factor with IP-based network connections"""
CHASSIS_TYPE_RACK_GROUP = 'rack group chassis type'
"""A group of racks which form a single entity or share infrastructure"""
CHASSIS_TYPE_STORAGE_ENCLOSURE = 'storage enclosure chassis type'
"""A chassis which encloses storage"""
CHASSIS_TYPE_OTHER = 'other chassis type'
"""A chassis that does not fit any of these definitions"""
# Chassis IntrusionSensor constants
CHASSIS_INTRUSION_SENSOR_NORMAL = 'normal chassis intrusion sensor'
"""No abnormal physical security conditions are detected at this time"""
CHASSIS_INTRUSION_SENSOR_HARDWARE_INTRUSION = 'hardware intrusion chassis ' \
'intrusion sensor'
"""HardwareIntrusion
A door, lock, or other mechanism protecting the internal system hardware from
being accessed is detected as being in an insecure state.
"""
CHASSIS_INTRUSION_SENSOR_TAMPERING_DETECTED = 'tampering detected chassis ' \
'intrusion sensor'
"""Physical tampering of the monitored entity is detected"""
# Chassis IntrusionSensorReArm constants
CHASSIS_INTRUSION_SENSOR_RE_ARM_MANUAL = 'manual re arm chassis intrusion ' \
'sensor'
"""This sensor would be restored to the Normal state by a manual re-arm"""
CHASSIS_INTRUSION_SENSOR_RE_ARM_AUTOMATIC = 'automatic re arm chassis ' \
'intrusion sensor'
"""Automatic
This sensor would be restored to the Normal state automatically as no abnormal
physical security conditions are detected.
"""

View File

@ -0,0 +1,48 @@
# 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.chassis import constants as cha_cons
CHASSIS_TYPE_VALUE_MAP = {
'Rack': cha_cons.CHASSIS_TYPE_RACK,
'Blade': cha_cons.CHASSIS_TYPE_BLADE,
'Enclosure': cha_cons.CHASSIS_TYPE_ENCLOSURE,
'StandAlone': cha_cons.CHASSIS_TYPE_STAND_ALONE,
'RackMount': cha_cons.CHASSIS_TYPE_RACK_MOUNT,
'Card': cha_cons.CHASSIS_TYPE_CARD,
'Cartridge': cha_cons.CHASSIS_TYPE_CARTRIDGE,
'Row': cha_cons.CHASSIS_TYPE_ROW,
'Pod': cha_cons.CHASSIS_TYPE_POD,
'Expansion': cha_cons.CHASSIS_TYPE_EXPANSION,
'Sidecar': cha_cons.CHASSIS_TYPE_SIDECAR,
'Zone': cha_cons.CHASSIS_TYPE_ZONE,
'Sled': cha_cons.CHASSIS_TYPE_SLED,
'Shelf': cha_cons.CHASSIS_TYPE_SHELF,
'Drawer': cha_cons.CHASSIS_TYPE_DRAWER,
'Module': cha_cons.CHASSIS_TYPE_MODULE,
'Component': cha_cons.CHASSIS_TYPE_COMPONENT,
'IPBasedDrive': cha_cons.CHASSIS_TYPE_IP_BASED_DRIVE,
'RackGroup': cha_cons.CHASSIS_TYPE_RACK_GROUP,
'StorageEnclosure': cha_cons.CHASSIS_TYPE_STORAGE_ENCLOSURE,
'Other': cha_cons.CHASSIS_TYPE_OTHER,
}
CHASSIS_INTRUSION_SENSOR_MAP = {
'Normal': cha_cons.CHASSIS_INTRUSION_SENSOR_NORMAL,
'HardwareIntrusion': cha_cons.CHASSIS_INTRUSION_SENSOR_HARDWARE_INTRUSION,
'TamperingDetected': cha_cons.CHASSIS_INTRUSION_SENSOR_TAMPERING_DETECTED,
}
CHASSIS_INTRUSION_SENSOR_RE_ARM_MAP = {
'Manual': cha_cons.CHASSIS_INTRUSION_SENSOR_RE_ARM_MANUAL,
'Automatic': cha_cons.CHASSIS_INTRUSION_SENSOR_RE_ARM_AUTOMATIC,
}

View File

@ -40,3 +40,64 @@ PARAMTYPE_NUMBER = 'number'
SEVERITY_OK = 'ok'
SEVERITY_WARNING = 'warning'
SEVERITY_CRITICAL = 'critical'
# Indicator LED Constants
INDICATOR_LED_LIT = 'indicator led lit'
"""The Indicator LED is lit"""
INDICATOR_LED_BLINKING = 'indicator led blinking'
"""The Indicator LED is blinking"""
INDICATOR_LED_OFF = 'indicator led off'
"""The Indicator LED is off"""
INDICATOR_LED_UNKNOWN = 'indicator led unknown'
"""The state of the Indicator LED cannot be determine"""
# System' PowerState constants
POWER_STATE_ON = 'on'
"""The resource is powered on"""
POWER_STATE_OFF = 'off'
"""The resource is powered off, although some components may continue to
have AUX power such as management controller"""
POWER_STATE_POWERING_ON = 'powering on'
"""A temporary state between Off and On. This temporary state can
be very short"""
POWER_STATE_POWERING_OFF = 'powering off'
"""A temporary state between On and Off. The power off action can take
time while the OS is in the shutdown process"""
# Reset action constants
RESET_TYPE_ON = 'on'
"""Turn the unit on"""
RESET_TYPE_FORCE_ON = 'force on'
"""Turn the unit on immediately"""
RESET_TYPE_FORCE_OFF = 'force off'
"""Turn the unit off immediately (non-graceful shutdown)"""
RESET_TYPE_GRACEFUL_SHUTDOWN = 'graceful shutdown'
"""Perform a graceful shutdown and power off"""
RESET_TYPE_GRACEFUL_RESTART = 'graceful restart'
"""Perform a graceful shutdown followed by a restart of the system"""
RESET_TYPE_FORCE_RESTART = 'force restart'
"""Perform an immediate (non-graceful) shutdown, followed by a restart"""
RESET_TYPE_NMI = 'nmi'
"""Generate a Diagnostic Interrupt (usually an NMI on x86 systems) to cease
normal operations, perform diagnostic actions and typically halt the system"""
RESET_TYPE_PUSH_POWER_BUTTON = 'push power button'
"""Simulate the pressing of the physical power button on this unit"""
RESET_TYPE_POWER_CYCLE = 'power cycle'
"""Perform a power cycle of the unit"""

View File

@ -13,10 +13,15 @@
# Values comes from the Redfish System json-schema 1.0.0:
# http://redfish.dmtf.org/schemas/v1/Manager.v1_0_0.json#/definitions/Manager # noqa
from sushy.resources import constants as res_cons
# Manager Reset action constants
RESET_MANAGER_GRACEFUL_RESTART = 'graceful restart'
RESET_MANAGER_FORCE_RESTART = 'force restart'
RESET_MANAGER_GRACEFUL_RESTART = res_cons.RESET_TYPE_GRACEFUL_RESTART
"""Perform a graceful shutdown followed by a restart of the system"""
RESET_MANAGER_FORCE_RESTART = res_cons.RESET_TYPE_FORCE_RESTART
"""Perform an immediate (non-graceful) shutdown, followed by a restart"""
# Manager Type constants

View File

@ -45,3 +45,33 @@ SEVERITY_VALUE_MAP = {
'Warning': res_cons.SEVERITY_WARNING,
'Critical': res_cons.SEVERITY_CRITICAL
}
INDICATOR_LED_VALUE_MAP = {
'Lit': res_cons.INDICATOR_LED_LIT,
'Blinking': res_cons.INDICATOR_LED_BLINKING,
'Off': res_cons.INDICATOR_LED_OFF,
'Unknown': res_cons.INDICATOR_LED_UNKNOWN,
}
POWER_STATE_VALUE_MAP = {
'On': res_cons.POWER_STATE_ON,
'Off': res_cons.POWER_STATE_OFF,
'PoweringOn': res_cons.POWER_STATE_POWERING_ON,
'PoweringOff': res_cons.POWER_STATE_POWERING_OFF,
}
POWER_STATE_MAP_REV = utils.revert_dictionary(POWER_STATE_VALUE_MAP)
RESET_TYPE_VALUE_MAP = {
'On': res_cons.RESET_TYPE_ON,
'ForceOff': res_cons.RESET_TYPE_FORCE_OFF,
'GracefulShutdown': res_cons.RESET_TYPE_GRACEFUL_SHUTDOWN,
'GracefulRestart': res_cons.RESET_TYPE_GRACEFUL_RESTART,
'ForceRestart': res_cons.RESET_TYPE_FORCE_RESTART,
'Nmi': res_cons.RESET_TYPE_NMI,
'ForceOn': res_cons.RESET_TYPE_FORCE_ON,
'PushPowerButton': res_cons.RESET_TYPE_PUSH_POWER_BUTTON,
'PowerCycle': res_cons.RESET_TYPE_POWER_CYCLE,
}
RESET_TYPE_VALUE_MAP_REV = utils.revert_dictionary(RESET_TYPE_VALUE_MAP)

View File

@ -16,46 +16,48 @@
# Values comes from the Redfish System json-schema 1.0.0:
# http://redfish.dmtf.org/schemas/v1/ComputerSystem.v1_0_0.json#/definitions/ComputerSystem # noqa
from sushy.resources import constants as res_cons
# Reset action constants
RESET_ON = 'on'
RESET_FORCE_OFF = 'force off'
RESET_GRACEFUL_SHUTDOWN = 'graceful shutdown'
RESET_GRACEFUL_RESTART = 'graceful restart'
RESET_FORCE_RESTART = 'force restart'
RESET_NMI = 'nmi'
RESET_FORCE_ON = 'force on'
RESET_PUSH_POWER_BUTTON = 'push power button'
RESET_ON = res_cons.RESET_TYPE_ON
RESET_FORCE_OFF = res_cons.RESET_TYPE_FORCE_OFF
RESET_GRACEFUL_SHUTDOWN = res_cons.RESET_TYPE_GRACEFUL_SHUTDOWN
RESET_GRACEFUL_RESTART = res_cons.RESET_TYPE_GRACEFUL_RESTART
RESET_FORCE_RESTART = res_cons.RESET_TYPE_FORCE_RESTART
RESET_NMI = res_cons.RESET_TYPE_NMI
RESET_FORCE_ON = res_cons.RESET_TYPE_FORCE_ON
RESET_PUSH_POWER_BUTTON = res_cons.RESET_TYPE_PUSH_POWER_BUTTON
# System' PowerState constants
SYSTEM_POWER_STATE_ON = 'on'
SYSTEM_POWER_STATE_ON = res_cons.POWER_STATE_ON
"""The system is powered on"""
SYSTEM_POWER_STATE_OFF = 'off'
SYSTEM_POWER_STATE_OFF = res_cons.POWER_STATE_OFF
"""The system is powered off, although some components may continue to
have AUX power such as management controller"""
SYSTEM_POWER_STATE_POWERING_ON = 'powering on'
SYSTEM_POWER_STATE_POWERING_ON = res_cons.POWER_STATE_POWERING_ON
"""A temporary state between Off and On. This temporary state can
be very short"""
SYSTEM_POWER_STATE_POWERING_OFF = 'powering off'
SYSTEM_POWER_STATE_POWERING_OFF = res_cons.POWER_STATE_POWERING_OFF
"""A temporary state between On and Off. The power off action can take
time while the OS is in the shutdown process"""
# Indicator LED Constants
SYSTEM_INDICATOR_LED_LIT = 'Lit'
SYSTEM_INDICATOR_LED_LIT = res_cons.INDICATOR_LED_LIT
"""The Indicator LED is lit"""
SYSTEM_INDICATOR_LED_BLINKING = 'Blinking'
SYSTEM_INDICATOR_LED_BLINKING = res_cons.INDICATOR_LED_BLINKING
"""The Indicator LED is blinking"""
SYSTEM_INDICATOR_LED_OFF = 'Off'
SYSTEM_INDICATOR_LED_OFF = res_cons.INDICATOR_LED_OFF
"""The Indicator LED is off"""
SYSTEM_INDICATOR_LED_UNKNOWN = 'Unknown'
SYSTEM_INDICATOR_LED_UNKNOWN = res_cons.INDICATOR_LED_UNKNOWN
"""The state of the Indicator LED cannot be determine"""
# Boot source target constants

View File

@ -30,22 +30,6 @@ RESET_SYSTEM_VALUE_MAP = {
RESET_SYSTEM_VALUE_MAP_REV = utils.revert_dictionary(RESET_SYSTEM_VALUE_MAP)
SYSTEM_POWER_STATE_MAP = {
'On': sys_cons.SYSTEM_POWER_STATE_ON,
'Off': sys_cons.SYSTEM_POWER_STATE_OFF,
'PoweringOn': sys_cons.SYSTEM_POWER_STATE_POWERING_ON,
'PoweringOff': sys_cons.SYSTEM_POWER_STATE_POWERING_OFF,
}
SYSTEM_POWER_STATE_MAP_REV = utils.revert_dictionary(SYSTEM_POWER_STATE_MAP)
SYSTEM_INDICATOR_LED_MAP = {
'Lit': sys_cons.SYSTEM_INDICATOR_LED_LIT,
'Blinking': sys_cons.SYSTEM_INDICATOR_LED_BLINKING,
'Off': sys_cons.SYSTEM_INDICATOR_LED_OFF,
'Unknown': sys_cons.SYSTEM_INDICATOR_LED_UNKNOWN,
}
BOOT_SOURCE_TARGET_MAP = {
'None': sys_cons.BOOT_SOURCE_TARGET_NONE,
'Pxe': sys_cons.BOOT_SOURCE_TARGET_PXE,

View File

@ -18,6 +18,7 @@ import logging
from sushy import exceptions
from sushy.resources import base
from sushy.resources import common
from sushy.resources import mappings as res_maps
from sushy.resources.system import bios
from sushy.resources.system import constants as sys_cons
from sushy.resources.system import ethernet_interface
@ -86,7 +87,7 @@ class System(base.ResourceBase):
"""The system identity string"""
indicator_led = base.MappedField('IndicatorLED',
sys_maps.SYSTEM_INDICATOR_LED_MAP)
res_maps.INDICATOR_LED_VALUE_MAP)
"""Whether the indicator LED is lit or off"""
manufacturer = base.Field('Manufacturer')
@ -99,7 +100,7 @@ class System(base.ResourceBase):
"""The system part number"""
power_state = base.MappedField('PowerState',
sys_maps.SYSTEM_POWER_STATE_MAP)
res_maps.POWER_STATE_VALUE_MAP)
"""The system power state"""
serial_number = base.Field('SerialNumber')

View File

@ -0,0 +1,98 @@
{
"@odata.type": "#Chassis.v1_8_0.Chassis",
"Id": "Blade1",
"Name": "Blade",
"Description": "Test description",
"ChassisType": "Blade",
"AssetTag": "45Z-2381",
"Manufacturer": "Contoso",
"Model": "SX1000",
"SKU": "6914260",
"SerialNumber": "529QB9450R6",
"PartNumber": "166480-S23",
"UUID": "FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF",
"PowerState": "On",
"IndicatorLED": "Off",
"Status": {
"State": "Enabled",
"Health": "OK"
},
"HeightMm": 44.45,
"WidthMm": 431.8,
"DepthMm": 711,
"WeightKg": 15.31,
"Location": {
"PartLocation": {
"ServiceLabel": "Blade 1",
"LocationType": "Slot",
"LocationOrdinalValue": 0,
"Reference": "Front",
"Orientation": "LeftToRight"
}
},
"PhysicalSecurity": {
"IntrusionSensor": "Normal",
"IntrusionSensorNumber": 123,
"IntrusionSensorReArm": "Manual"
},
"Thermal": {
"@odata.id": "/redfish/v1/Chassis/Blade1/Thermal"
},
"Links": {
"ComputerSystems": [
{
"@odata.id": "/redfish/v1/Systems/529QB9450R6"
}
],
"ManagedBy": [
{
"@odata.id": "/redfish/v1/Managers/Blade1BMC"
}
],
"ContainedBy": {
"@odata.id": "/redfish/v1/Chassis/MultiBladeEncl"
},
"CooledBy": [
{
"@odata.id": "/redfish/v1/Chassis/MultiBladeEncl/Thermal#/Fans/0"
},
{
"@odata.id": "/redfish/v1/Chassis/MultiBladeEncl/Thermal#/Fans/1"
},
{
"@odata.id": "/redfish/v1/Chassis/MultiBladeEncl/Thermal#/Fans/2"
},
{
"@odata.id": "/redfish/v1/Chassis/MultiBladeEncl/Thermal#/Fans/3"
}
],
"PoweredBy": [
{
"@odata.id": "/redfish/v1/Chassis/MultiBladeEncl/Power#/PowerSupplies/0"
},
{
"@odata.id": "/redfish/v1/Chassis/MultiBladeEncl/Power#/PowerSupplies/1"
}
]
},
"Actions": {
"#Chassis.Reset": {
"target": "/redfish/v1/Chassis/Blade1/Actions/Chassis.Reset",
"ResetType@Redfish.AllowableValues": [
"ForceRestart",
"GracefulRestart",
"On",
"ForceOff",
"GracefulShutdown",
"Nmi",
"ForceOn",
"PushPowerButton",
"PowerCycle"
]
},
"Oem": {}
},
"@odata.context": "/redfish/v1/$metadata#Chassis.Chassis",
"@odata.id": "/redfish/v1/Chassis/Blade1",
"@Redfish.Copyright": "Copyright 2014-2017 Distributed Management Task Force, Inc. (DMTF). For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright."
}

View File

@ -0,0 +1,25 @@
{
"@odata.type": "#ChassisCollection.ChassisCollection",
"Name": "Chassis Collection",
"Members@odata.count": 5,
"Members": [
{
"@odata.id": "/redfish/v1/Chassis/MultiBladeEncl"
},
{
"@odata.id": "/redfish/v1/Chassis/Blade1"
},
{
"@odata.id": "/redfish/v1/Chassis/Blade2"
},
{
"@odata.id": "/redfish/v1/Chassis/Blade3"
},
{
"@odata.id": "/redfish/v1/Chassis/Blade4"
}
],
"@odata.context": "/redfish/v1/$metadata#ChassisCollection.ChassisCollection",
"@odata.id": "/redfish/v1/Chassis",
"@Redfish.Copyright": "Copyright 2014-2017 Distributed Management Task Force, Inc. (DMTF). For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright."
}

View File

@ -0,0 +1,157 @@
# -*- coding: utf-8 -*-
# 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 mock
import sushy
from sushy import exceptions
from sushy.resources.chassis import chassis
from sushy.tests.unit import base
class ChassisTestCase(base.TestCase):
def setUp(self):
super(ChassisTestCase, self).setUp()
self.conn = mock.Mock()
with open('sushy/tests/unit/json_samples/chassis.json') as f:
self.conn.get.return_value.json.return_value = json.load(f)
self.chassis = chassis.Chassis(self.conn, '/redfish/v1/Chassis/Blade1',
redfish_version='1.8.0')
def test__parse_attributes(self):
# | WHEN |
self.chassis._parse_attributes()
# | THEN |
self.assertEqual('1.8.0', self.chassis.redfish_version)
self.assertEqual('Blade1', self.chassis.identity)
self.assertEqual('Blade', self.chassis.name)
self.assertEqual('Test description', self.chassis.description)
self.assertEqual('45Z-2381', self.chassis.asset_tag)
self.assertEqual(sushy.CHASSIS_TYPE_BLADE,
self.chassis.chassis_type)
self.assertEqual('Contoso', self.chassis.manufacturer)
self.assertEqual('SX1000', self.chassis.model)
self.assertEqual('529QB9450R6', self.chassis.serial_number)
self.assertEqual('6914260', self.chassis.sku)
self.assertEqual('166480-S23', self.chassis.part_number)
self.assertEqual('FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF',
self.chassis.uuid)
self.assertEqual(sushy.INDICATOR_LED_OFF,
self.chassis.indicator_led)
self.assertEqual(sushy.POWER_STATE_ON,
self.chassis.power_state)
self.assertEqual(sushy.STATE_ENABLED, self.chassis.status.state)
self.assertEqual(44.45, self.chassis.height_mm)
self.assertEqual(431.8, self.chassis.width_mm)
self.assertEqual(711, self.chassis.depth_mm)
self.assertEqual(15.31, self.chassis.weight_kg)
self.assertEqual(sushy.HEALTH_OK, self.chassis.status.health)
self.assertEqual(sushy.CHASSIS_INTRUSION_SENSOR_NORMAL,
self.chassis.physical_security.intrusion_sensor)
self.assertEqual(123,
self.chassis.physical_security.intrusion_sensor_number
)
self.assertEqual(sushy.CHASSIS_INTRUSION_SENSOR_RE_ARM_MANUAL,
self.chassis.physical_security.intrusion_sensor_re_arm
)
def test_get_allowed_reset_chasis_values(self):
# | GIVEN |
expected = {sushy.RESET_TYPE_POWER_CYCLE,
sushy.RESET_TYPE_PUSH_POWER_BUTTON,
sushy.RESET_TYPE_FORCE_ON, sushy.RESET_TYPE_NMI,
sushy.RESET_TYPE_FORCE_RESTART,
sushy.RESET_TYPE_GRACEFUL_RESTART, sushy.RESET_TYPE_ON,
sushy.RESET_TYPE_FORCE_OFF,
sushy.RESET_TYPE_GRACEFUL_SHUTDOWN}
# | WHEN |
values = self.chassis.get_allowed_reset_chassis_values()
# | THEN |
self.assertEqual(expected, values)
self.assertIsInstance(values, set)
def test_get_allowed_reset_chassis_values_for_no_values_set(self):
# | GIVEN |
self.chassis._actions.reset.allowed_values = []
expected = {sushy.RESET_TYPE_POWER_CYCLE,
sushy.RESET_TYPE_PUSH_POWER_BUTTON,
sushy.RESET_TYPE_FORCE_ON, sushy.RESET_TYPE_NMI,
sushy.RESET_TYPE_FORCE_RESTART,
sushy.RESET_TYPE_GRACEFUL_RESTART, sushy.RESET_TYPE_ON,
sushy.RESET_TYPE_FORCE_OFF,
sushy.RESET_TYPE_GRACEFUL_SHUTDOWN}
# | WHEN |
values = self.chassis.get_allowed_reset_chassis_values()
# | THEN |
self.assertEqual(expected, values)
self.assertIsInstance(values, set)
def test_get_allowed_reset_chassis_values_missing_action_reset_attr(self):
# | GIVEN |
self.chassis._actions.reset = None
# | WHEN & THEN |
self.assertRaisesRegex(
exceptions.MissingActionError, 'action #Chassis.Reset')
def test_reset_chassis(self):
self.chassis.reset_chassis(sushy.RESET_TYPE_GRACEFUL_RESTART)
self.chassis._conn.post.assert_called_once_with(
'/redfish/v1/Chassis/Blade1/Actions/Chassis.Reset',
data={'ResetType': 'GracefulRestart'})
def test_reset_chassis_with_invalid_value(self):
self.assertRaises(exceptions.InvalidParameterValueError,
self.chassis.reset_chassis, 'invalid-value')
class ChassisCollectionTestCase(base.TestCase):
def setUp(self):
super(ChassisCollectionTestCase, self).setUp()
self.conn = mock.Mock()
with open('sushy/tests/unit/json_samples/'
'chassis_collection.json') as f:
self.conn.get.return_value.json.return_value = json.load(f)
self.chassis = chassis.ChassisCollection(
self.conn, '/redfish/v1/Chassis', redfish_version='1.5.0')
@mock.patch.object(chassis, 'Chassis', autospec=True)
def test_get_member(self, chassis_mock):
self.chassis.get_member('/redfish/v1/Chassis/MultiBladeEncl')
chassis_mock.assert_called_once_with(
self.chassis._conn, '/redfish/v1/Chassis/MultiBladeEncl',
redfish_version=self.chassis.redfish_version)
@mock.patch.object(chassis, 'Chassis', autospec=True)
def test_get_members(self, chassis_mock):
members = self.chassis.get_members()
calls = [
mock.call(self.chassis._conn, '/redfish/v1/Chassis/MultiBladeEncl',
redfish_version=self.chassis.redfish_version),
mock.call(self.chassis._conn, '/redfish/v1/Chassis/Blade1',
redfish_version=self.chassis.redfish_version),
mock.call(self.chassis._conn, '/redfish/v1/Chassis/Blade2',
redfish_version=self.chassis.redfish_version),
mock.call(self.chassis._conn, '/redfish/v1/Chassis/Blade3',
redfish_version=self.chassis.redfish_version),
mock.call(self.chassis._conn, '/redfish/v1/Chassis/Blade4',
redfish_version=self.chassis.redfish_version)
]
chassis_mock.assert_has_calls(calls)
self.assertIsInstance(members, list)
self.assertEqual(5, len(members))

View File

@ -21,6 +21,7 @@ from sushy import auth
from sushy import connector
from sushy import exceptions
from sushy import main
from sushy.resources.chassis import chassis
from sushy.resources.manager import manager
from sushy.resources.registry import message_registry_file
from sushy.resources.sessionservice import session
@ -99,6 +100,20 @@ class MainTestCase(base.TestCase):
self.root._conn, 'fake-system-id',
redfish_version=self.root.redfish_version)
@mock.patch.object(chassis, 'Chassis', autospec=True)
def test_get_chassis(self, mock_chassis):
self.root.get_chassis('fake-chassis-id')
mock_chassis.assert_called_once_with(
self.root._conn, 'fake-chassis-id',
redfish_version=self.root.redfish_version)
@mock.patch.object(chassis, 'ChassisCollection', autospec=True)
def test_get_chassis_collection(self, chassis_collection_mock):
self.root.get_chassis_collection()
chassis_collection_mock.assert_called_once_with(
self.root._conn, '/redfish/v1/Chassis',
redfish_version=self.root.redfish_version)
@mock.patch.object(manager, 'ManagerCollection', autospec=True)
def test_get_manager_collection(self, ManagerCollection_mock):
self.root.get_manager_collection()