Add all missing attributes of Node in RSD 2.1
Change-Id: If1cb3e716d6a8997629435e5400cd4d1aa43ce23
This commit is contained in:
parent
9adb95c051
commit
b585c03852
@ -1,100 +0,0 @@
|
||||
# Copyright 2017 Intel, 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.
|
||||
|
||||
# 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
|
||||
|
||||
# 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'
|
||||
|
||||
# Node PowerState constants
|
||||
|
||||
NODE_POWER_STATE_ON = 'on'
|
||||
"""The system is powered on"""
|
||||
|
||||
NODE_POWER_STATE_OFF = 'off'
|
||||
"""The system is powered off, although some components may continue to
|
||||
have AUX power such as management controller"""
|
||||
|
||||
NODE_POWER_STATE_POWERING_ON = 'powering on'
|
||||
"""A temporary state between Off and On. This temporary state can
|
||||
be very short"""
|
||||
|
||||
NODE_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"""
|
||||
|
||||
# Composed Node State constants
|
||||
|
||||
COMPOSED_NODE_STATE_ALLOCATING = 'allocating'
|
||||
"""Allocating resources for node is in progress. Next state can be
|
||||
Allocated or Failed"""
|
||||
|
||||
COMPOSED_NODE_STATE_ALLOCATED = 'allocated'
|
||||
"""Node resources have been allocated, but assembly not started yet.
|
||||
After ComposedNode.Assemble action state will progress to Assembling"""
|
||||
|
||||
COMPOSED_NODE_STATE_ASSEMBLING = 'assembling'
|
||||
"""Assembly process initiated, but not finished yet. When assembly
|
||||
is done it will change into Assembled"""
|
||||
|
||||
COMPOSED_NODE_STATE_ASSEMBLED = 'assembled'
|
||||
"""Node successfully assembled"""
|
||||
|
||||
COMPOSED_NODE_STATE_FAILED = 'failed'
|
||||
"""Allocation or assembly process failed, or in runtime one of composing
|
||||
components was removed or transitioned in error state"""
|
||||
|
||||
# Boot source target constants
|
||||
|
||||
BOOT_SOURCE_TARGET_NONE = 'none'
|
||||
"""Boot from the normal boot device"""
|
||||
|
||||
BOOT_SOURCE_TARGET_PXE = 'pxe'
|
||||
"""Boot from the Pre-Boot EXecution (PXE) environment"""
|
||||
|
||||
BOOT_SOURCE_TARGET_HDD = 'hdd'
|
||||
"""Boot from a hard drive"""
|
||||
|
||||
# Boot source mode constants
|
||||
|
||||
BOOT_SOURCE_MODE_LEGACY = 'legacy'
|
||||
BOOT_SOURCE_MODE_UEFI = 'uefi'
|
||||
|
||||
# Boot source enabled constants
|
||||
|
||||
BOOT_SOURCE_ENABLED_ONCE = 'once'
|
||||
BOOT_SOURCE_ENABLED_CONTINUOUS = 'continuous'
|
||||
BOOT_SOURCE_ENABLED_DISABLED = 'disabled'
|
||||
|
||||
# Processor related constants
|
||||
# Values comes from the Redfish Processor json-schema 1.0.0:
|
||||
# http://redfish.dmtf.org/schemas/v1/Processor.v1_0_0.json
|
||||
|
||||
# Processor Architecture constants
|
||||
|
||||
PROCESSOR_ARCH_x86 = 'x86 or x86-64'
|
||||
PROCESSOR_ARCH_IA_64 = 'Intel Itanium'
|
||||
PROCESSOR_ARCH_ARM = 'ARM'
|
||||
PROCESSOR_ARCH_MIPS = 'MIPS'
|
||||
PROCESSOR_ARCH_OEM = 'OEM-defined'
|
@ -1,84 +0,0 @@
|
||||
# Copyright 2017 Intel, 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.
|
||||
|
||||
from sushy import utils
|
||||
|
||||
from rsd_lib.resources.v2_1.node import constants as node_cons
|
||||
|
||||
RESET_NODE_VALUE_MAP = {
|
||||
'On': node_cons.RESET_ON,
|
||||
'ForceOff': node_cons.RESET_FORCE_OFF,
|
||||
'GracefulShutdown': node_cons.RESET_GRACEFUL_SHUTDOWN,
|
||||
'GracefulRestart': node_cons.RESET_GRACEFUL_RESTART,
|
||||
'ForceRestart': node_cons.RESET_FORCE_RESTART,
|
||||
'Nmi': node_cons.RESET_NMI,
|
||||
'ForceOn': node_cons.RESET_FORCE_ON,
|
||||
'PushPowerButton': node_cons.RESET_PUSH_POWER_BUTTON,
|
||||
}
|
||||
|
||||
RESET_NODE_VALUE_MAP_REV = utils.revert_dictionary(RESET_NODE_VALUE_MAP)
|
||||
|
||||
NODE_POWER_STATE_MAP = {
|
||||
'On': node_cons.NODE_POWER_STATE_ON,
|
||||
'Off': node_cons.NODE_POWER_STATE_OFF,
|
||||
'PoweringOn': node_cons.NODE_POWER_STATE_POWERING_ON,
|
||||
'PoweringOff': node_cons.NODE_POWER_STATE_POWERING_OFF,
|
||||
}
|
||||
|
||||
NODE_POWER_STATE_MAP_REV = utils.revert_dictionary(NODE_POWER_STATE_MAP)
|
||||
|
||||
COMPOSED_NODE_STATE_MAP = {
|
||||
'Allocating': node_cons.COMPOSED_NODE_STATE_ALLOCATING,
|
||||
'Allocated': node_cons.COMPOSED_NODE_STATE_ALLOCATED,
|
||||
'Assembling': node_cons.COMPOSED_NODE_STATE_ASSEMBLING,
|
||||
'Assembled': node_cons.COMPOSED_NODE_STATE_ASSEMBLED,
|
||||
'Failed': node_cons.COMPOSED_NODE_STATE_FAILED,
|
||||
}
|
||||
|
||||
COMPOSED_NODE_STATE_MAP_REV = utils.revert_dictionary(COMPOSED_NODE_STATE_MAP)
|
||||
|
||||
BOOT_SOURCE_TARGET_MAP = {
|
||||
'None': node_cons.BOOT_SOURCE_TARGET_NONE,
|
||||
'Pxe': node_cons.BOOT_SOURCE_TARGET_PXE,
|
||||
'Hdd': node_cons.BOOT_SOURCE_TARGET_HDD,
|
||||
}
|
||||
|
||||
BOOT_SOURCE_TARGET_MAP_REV = utils.revert_dictionary(BOOT_SOURCE_TARGET_MAP)
|
||||
|
||||
BOOT_SOURCE_MODE_MAP = {
|
||||
'Legacy': node_cons.BOOT_SOURCE_MODE_LEGACY,
|
||||
'UEFI': node_cons.BOOT_SOURCE_MODE_UEFI,
|
||||
}
|
||||
|
||||
BOOT_SOURCE_MODE_MAP_REV = utils.revert_dictionary(BOOT_SOURCE_MODE_MAP)
|
||||
|
||||
BOOT_SOURCE_ENABLED_MAP = {
|
||||
'Once': node_cons.BOOT_SOURCE_ENABLED_ONCE,
|
||||
'Continuous': node_cons.BOOT_SOURCE_ENABLED_CONTINUOUS,
|
||||
'Disabled': node_cons.BOOT_SOURCE_ENABLED_DISABLED,
|
||||
}
|
||||
|
||||
BOOT_SOURCE_ENABLED_MAP_REV = utils.revert_dictionary(BOOT_SOURCE_ENABLED_MAP)
|
||||
|
||||
PROCESSOR_ARCH_VALUE_MAP = {
|
||||
'x86': node_cons.PROCESSOR_ARCH_x86,
|
||||
'IA-64': node_cons.PROCESSOR_ARCH_IA_64,
|
||||
'ARM': node_cons.PROCESSOR_ARCH_ARM,
|
||||
'MIPS': node_cons.PROCESSOR_ARCH_MIPS,
|
||||
'OEM': node_cons.PROCESSOR_ARCH_OEM,
|
||||
}
|
||||
|
||||
PROCESSOR_ARCH_VALUE_MAP_REV = (
|
||||
utils.revert_dictionary(PROCESSOR_ARCH_VALUE_MAP))
|
@ -19,13 +19,12 @@ import logging
|
||||
from sushy import exceptions
|
||||
from sushy.resources import base
|
||||
from sushy.resources import common
|
||||
from sushy.resources.system import system
|
||||
from sushy import utils
|
||||
|
||||
from rsd_lib import common as rsd_lib_common
|
||||
from rsd_lib.resources.v2_1.node import constants as node_cons
|
||||
from rsd_lib.resources.v2_1.node import mappings as node_maps
|
||||
from rsd_lib import base as rsd_lib_base
|
||||
from rsd_lib import constants
|
||||
from rsd_lib.resources.v2_1.node import schemas as node_schemas
|
||||
from rsd_lib.resources.v2_1.system import system
|
||||
from rsd_lib import utils as rsd_lib_utils
|
||||
|
||||
|
||||
@ -33,166 +32,129 @@ LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class AssembleActionField(base.CompositeField):
|
||||
target_uri = base.Field('target', required=True)
|
||||
|
||||
target_uri = base.Field("target", required=True)
|
||||
|
||||
|
||||
class AttachEndpointActionField(base.CompositeField):
|
||||
allowed_values = base.Field('Resource@Redfish.AllowableValues',
|
||||
default=(),
|
||||
adapter=utils.get_members_identities)
|
||||
class AttachEndpointActionField(common.ActionField):
|
||||
|
||||
target_uri = base.Field('target', required=True)
|
||||
allowed_values = base.Field(
|
||||
"Resource@Redfish.AllowableValues",
|
||||
default=(),
|
||||
adapter=utils.get_members_identities,
|
||||
)
|
||||
|
||||
|
||||
class DetachEndpointActionField(base.CompositeField):
|
||||
allowed_values = base.Field('Resource@Redfish.AllowableValues',
|
||||
default=(),
|
||||
adapter=utils.get_members_identities)
|
||||
class DetachEndpointActionField(common.ActionField):
|
||||
|
||||
target_uri = base.Field('target', required=True)
|
||||
|
||||
|
||||
class ComposeNodeActionField(base.CompositeField):
|
||||
target_uri = base.Field('target', required=True)
|
||||
allowed_values = base.Field(
|
||||
"Resource@Redfish.AllowableValues",
|
||||
default=(),
|
||||
adapter=utils.get_members_identities,
|
||||
)
|
||||
|
||||
|
||||
class NodeActionsField(base.CompositeField):
|
||||
reset = common.ResetActionField('#ComposedNode.Reset')
|
||||
assemble = AssembleActionField('#ComposedNode.Assemble')
|
||||
attach_endpoint = AttachEndpointActionField('#ComposedNode.AttachEndpoint')
|
||||
detach_endpoint = DetachEndpointActionField('#ComposedNode.DetachEndpoint')
|
||||
|
||||
reset = common.ResetActionField("#ComposedNode.Reset")
|
||||
assemble = common.ActionField("#ComposedNode.Assemble")
|
||||
attach_endpoint = AttachEndpointActionField("#ComposedNode.AttachEndpoint")
|
||||
detach_endpoint = DetachEndpointActionField("#ComposedNode.DetachEndpoint")
|
||||
|
||||
|
||||
class NodeCollectionActionsField(base.CompositeField):
|
||||
compose = ComposeNodeActionField('#ComposedNodeCollection.Allocate')
|
||||
|
||||
|
||||
class BootField(base.CompositeField):
|
||||
allowed_values = base.Field(
|
||||
'BootSourceOverrideTarget@Redfish.AllowableValues')
|
||||
|
||||
enabled = base.MappedField('BootSourceOverrideEnabled',
|
||||
node_maps.BOOT_SOURCE_ENABLED_MAP)
|
||||
|
||||
mode = base.MappedField('BootSourceOverrideMode',
|
||||
node_maps.BOOT_SOURCE_MODE_MAP)
|
||||
|
||||
target = base.MappedField('BootSourceOverrideTarget',
|
||||
node_maps.BOOT_SOURCE_TARGET_MAP)
|
||||
|
||||
|
||||
class MemorySummaryField(base.CompositeField):
|
||||
status = rsd_lib_common.StatusField('Status')
|
||||
"""The memory status"""
|
||||
|
||||
size_gib = base.Field('TotalSystemMemoryGiB',
|
||||
adapter=rsd_lib_utils.num_or_none)
|
||||
"""The size of memory of the node in GiB.
|
||||
|
||||
This signifies the total installed, operating system-accessible memory
|
||||
(RAM), measured in GiB.
|
||||
"""
|
||||
|
||||
|
||||
class ProcessorSummaryField(base.CompositeField):
|
||||
status = rsd_lib_common.StatusField('Status')
|
||||
"""The processor status"""
|
||||
|
||||
count = base.Field('Count', adapter=rsd_lib_utils.num_or_none)
|
||||
"""The number of CPUs in the node."""
|
||||
|
||||
model = base.Field('Model')
|
||||
"""Basic information about processor model."""
|
||||
compose = common.ActionField("#ComposedNodeCollection.Allocate")
|
||||
|
||||
|
||||
class LinksField(base.CompositeField):
|
||||
system = base.Field('ComputerSystem',
|
||||
adapter=rsd_lib_utils.get_resource_identity)
|
||||
|
||||
computer_system = base.Field(
|
||||
"ComputerSystem", adapter=rsd_lib_utils.get_resource_identity
|
||||
)
|
||||
"""Link to base computer system of this node"""
|
||||
|
||||
processors = base.Field('Processors', default=(),
|
||||
adapter=utils.get_members_identities)
|
||||
processors = base.Field("Processors", adapter=utils.get_members_identities)
|
||||
"""Link to processors of this node"""
|
||||
|
||||
memory = base.Field('Memory', default=(),
|
||||
adapter=utils.get_members_identities)
|
||||
memory = base.Field("Memory", adapter=utils.get_members_identities)
|
||||
"""Link to memory of this node"""
|
||||
|
||||
ethernet_interfaces = base.Field('EthernetInterfaces', default=(),
|
||||
adapter=utils.get_members_identities)
|
||||
ethernet_interfaces = base.Field(
|
||||
"EthernetInterfaces", adapter=utils.get_members_identities
|
||||
)
|
||||
"""Link to ethernet interfaces of this node"""
|
||||
|
||||
local_drives = base.Field('LocalDrives', default=(),
|
||||
adapter=utils.get_members_identities)
|
||||
local_drives = base.Field(
|
||||
"LocalDrives", adapter=utils.get_members_identities
|
||||
)
|
||||
"""Link to local driver of this node"""
|
||||
|
||||
remote_drives = base.Field('RemoteDrives', default=(),
|
||||
adapter=utils.get_members_identities)
|
||||
remote_drives = base.Field(
|
||||
"RemoteDrives", adapter=utils.get_members_identities
|
||||
)
|
||||
"""Link to remote drives of this node"""
|
||||
|
||||
managed_by = base.Field("ManagedBy", adapter=utils.get_members_identities)
|
||||
|
||||
class Node(base.ResourceBase):
|
||||
|
||||
boot = BootField('Boot', required=True)
|
||||
"""A dictionary containg the current boot device, frequency and mode"""
|
||||
class Node(rsd_lib_base.ResourceBase):
|
||||
"""ComposedNode resource class
|
||||
|
||||
composed_node_state = base.MappedField('ComposedNodeState',
|
||||
node_maps.COMPOSED_NODE_STATE_MAP)
|
||||
"""Current state of assembly process for this node"""
|
||||
This schema defines a node and its respective properties.
|
||||
"""
|
||||
|
||||
description = base.Field('Description')
|
||||
"""The node description"""
|
||||
links = LinksField("Links")
|
||||
"""Contains links to other resources that are related to this resource."""
|
||||
|
||||
identity = base.Field('Id', required=True)
|
||||
"""The node identity string"""
|
||||
status = rsd_lib_base.StatusField("Status")
|
||||
"""This indicates the known state of the resource, such as if it is
|
||||
enabled.
|
||||
"""
|
||||
|
||||
name = base.Field('Name')
|
||||
"""The node name"""
|
||||
composed_node_state = base.Field("ComposedNodeState")
|
||||
|
||||
power_state = base.MappedField('PowerState',
|
||||
node_maps.NODE_POWER_STATE_MAP)
|
||||
"""The node power state"""
|
||||
asset_tag = base.Field("AssetTag")
|
||||
"""The user definable tag that can be used to track this computer system
|
||||
for inventory or other client purposes
|
||||
"""
|
||||
|
||||
status = rsd_lib_common.StatusField('Status')
|
||||
"""The node status"""
|
||||
uuid = base.Field("UUID")
|
||||
"""The universal unique identifier (UUID) for this system"""
|
||||
|
||||
uuid = base.Field('UUID')
|
||||
"""The node UUID"""
|
||||
power_state = base.Field("PowerState")
|
||||
"""This is the current power state of the system"""
|
||||
|
||||
memory_summary = MemorySummaryField('Memory')
|
||||
"""The summary info of memory of the node in general detail"""
|
||||
boot = system.BootField("Boot")
|
||||
"""Information about the boot settings for this system"""
|
||||
|
||||
processor_summary = ProcessorSummaryField('Processors')
|
||||
"""The summary info for the node processors in general detail"""
|
||||
processors = system.ProcessorSummaryField("Processors")
|
||||
"""This object describes the central processors of the system in general
|
||||
detail.
|
||||
"""
|
||||
|
||||
links = LinksField('Links')
|
||||
"""These links to related components of this composed node"""
|
||||
memory = system.MemorySummaryField("Memory")
|
||||
"""This object describes the central memory of the system in general
|
||||
detail.
|
||||
"""
|
||||
|
||||
_actions = NodeActionsField('Actions', required=True)
|
||||
|
||||
def __init__(self, connector, identity, redfish_version=None):
|
||||
"""A class representing a ComposedNode
|
||||
|
||||
:param connector: A Connector instance
|
||||
:param identity: The identity of the Node resource
|
||||
:param redfish_version: The version of RedFish. Used to construct
|
||||
the object according to schema of the given version.
|
||||
"""
|
||||
super(Node, self).__init__(connector, identity, redfish_version)
|
||||
_actions = NodeActionsField("Actions", required=True)
|
||||
|
||||
def _get_reset_action_element(self):
|
||||
reset_action = self._actions.reset
|
||||
|
||||
if not reset_action:
|
||||
raise exceptions.MissingActionError(action='#ComposedNode.Reset',
|
||||
resource=self._path)
|
||||
raise exceptions.MissingActionError(
|
||||
action="#ComposedNode.Reset", resource=self._path
|
||||
)
|
||||
return reset_action
|
||||
|
||||
def _get_assemble_action_element(self):
|
||||
assemble_action = self._actions.assemble
|
||||
if not assemble_action:
|
||||
raise exceptions.MissingActionError(
|
||||
action='#ComposedNode.Assemble',
|
||||
resource=self._path)
|
||||
action="#ComposedNode.Assemble", resource=self._path
|
||||
)
|
||||
return assemble_action
|
||||
|
||||
def get_allowed_reset_node_values(self):
|
||||
@ -203,13 +165,16 @@ class Node(base.ResourceBase):
|
||||
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 node action for Node %s', self.identity)
|
||||
return set(node_maps.RESET_NODE_VALUE_MAP_REV)
|
||||
LOG.warning(
|
||||
"Could not figure out the allowed values for the "
|
||||
"reset node action for Node %s",
|
||||
self.identity,
|
||||
)
|
||||
return set(constants.RESET_TYPE_VALUE)
|
||||
|
||||
return set([node_maps.RESET_NODE_VALUE_MAP[v] for v in
|
||||
set(node_maps.RESET_NODE_VALUE_MAP).
|
||||
intersection(reset_action.allowed_values)])
|
||||
return set(constants.RESET_TYPE_VALUE).intersection(
|
||||
reset_action.allowed_values
|
||||
)
|
||||
|
||||
def reset_node(self, value):
|
||||
"""Reset the node.
|
||||
@ -221,12 +186,12 @@ class Node(base.ResourceBase):
|
||||
valid_resets = self.get_allowed_reset_node_values()
|
||||
if value not in valid_resets:
|
||||
raise exceptions.InvalidParameterValueError(
|
||||
parameter='value', value=value, valid_values=valid_resets)
|
||||
parameter="value", value=value, valid_values=valid_resets
|
||||
)
|
||||
|
||||
value = node_maps.RESET_NODE_VALUE_MAP_REV[value]
|
||||
target_uri = self._get_reset_action_element().target_uri
|
||||
|
||||
self._conn.post(target_uri, data={'ResetType': value})
|
||||
self._conn.post(target_uri, data={"ResetType": value})
|
||||
|
||||
def assemble_node(self):
|
||||
"""Assemble the composed node."""
|
||||
@ -239,86 +204,85 @@ class Node(base.ResourceBase):
|
||||
|
||||
:returns: A set with the allowed values.
|
||||
"""
|
||||
if not self.boot.allowed_values:
|
||||
LOG.warning('Could not figure out the allowed values for '
|
||||
'configuring the boot source for Node %s',
|
||||
self.identity)
|
||||
return set(node_maps.BOOT_SOURCE_TARGET_MAP_REV)
|
||||
if not self.boot.boot_source_override_target_allowed_values:
|
||||
LOG.warning(
|
||||
"Could not figure out the allowed values for "
|
||||
"configuring the boot source for Node %s",
|
||||
self.identity,
|
||||
)
|
||||
return set(constants.BOOT_SOURCE_TARGET_VALUE)
|
||||
|
||||
return set([node_maps.BOOT_SOURCE_TARGET_MAP[v] for v in
|
||||
set(node_maps.BOOT_SOURCE_TARGET_MAP).
|
||||
intersection(self.boot.allowed_values)])
|
||||
return set(constants.BOOT_SOURCE_TARGET_VALUE).intersection(
|
||||
self.boot.boot_source_override_target_allowed_values
|
||||
)
|
||||
|
||||
def set_node_boot_source(self, target,
|
||||
enabled=node_cons.BOOT_SOURCE_ENABLED_ONCE,
|
||||
mode=None):
|
||||
def get_allowed_node_boot_mode_values(self):
|
||||
"""Get the allowed values for the boot source mode.
|
||||
|
||||
:returns: A set with the allowed values.
|
||||
"""
|
||||
if not self.boot.boot_source_override_mode_allowed_values:
|
||||
LOG.warning(
|
||||
"Could not figure out the allowed values for "
|
||||
"configuring the boot mode for Node %s",
|
||||
self.identity,
|
||||
)
|
||||
return set(constants.BOOT_SOURCE_MODE_VALUE)
|
||||
|
||||
return set(constants.BOOT_SOURCE_MODE_VALUE).intersection(
|
||||
self.boot.boot_source_override_mode_allowed_values
|
||||
)
|
||||
|
||||
def set_node_boot_source(self, target, enabled="Once", mode=None):
|
||||
"""Set the boot source.
|
||||
|
||||
Set the boot source to use on next reboot of the Node.
|
||||
|
||||
:param target: The target boot source.
|
||||
:param enabled: The frequency, whether to set it for the next
|
||||
reboot only (BOOT_SOURCE_ENABLED_ONCE) or persistent to all
|
||||
future reboots (BOOT_SOURCE_ENABLED_CONTINUOUS) or disabled
|
||||
(BOOT_SOURCE_ENABLED_DISABLED).
|
||||
:param mode: The boot mode, UEFI (BOOT_SOURCE_MODE_UEFI) or
|
||||
Legacy (BOOT_SOURCE_MODE_LEGACY).
|
||||
reboot only ("Once") or persistent to all future reboots
|
||||
("Continuous") or disabled ("Disabled").
|
||||
:param mode: The boot mode, UEFI ("UEFI") or BIOS ("Legacy").
|
||||
:raises: InvalidParameterValueError, if any information passed is
|
||||
invalid.
|
||||
"""
|
||||
valid_targets = self.get_allowed_node_boot_source_values()
|
||||
if target not in valid_targets:
|
||||
raise exceptions.InvalidParameterValueError(
|
||||
parameter='target', value=target, valid_values=valid_targets)
|
||||
parameter="target", value=target, valid_values=valid_targets
|
||||
)
|
||||
|
||||
if enabled not in node_maps.BOOT_SOURCE_ENABLED_MAP_REV:
|
||||
if enabled not in constants.BOOT_SOURCE_ENABLED_VALUE:
|
||||
raise exceptions.InvalidParameterValueError(
|
||||
parameter='enabled', value=enabled,
|
||||
valid_values=list(node_maps.BOOT_SOURCE_ENABLED_MAP_REV))
|
||||
parameter="enabled",
|
||||
value=enabled,
|
||||
valid_values=constants.BOOT_SOURCE_ENABLED_VALUE,
|
||||
)
|
||||
|
||||
data = {
|
||||
'Boot': {
|
||||
'BootSourceOverrideTarget':
|
||||
node_maps.BOOT_SOURCE_TARGET_MAP_REV[target],
|
||||
'BootSourceOverrideEnabled':
|
||||
node_maps.BOOT_SOURCE_ENABLED_MAP_REV[enabled]
|
||||
"Boot": {
|
||||
"BootSourceOverrideTarget": target,
|
||||
"BootSourceOverrideEnabled": enabled,
|
||||
}
|
||||
}
|
||||
|
||||
if mode is not None:
|
||||
if mode not in node_maps.BOOT_SOURCE_MODE_MAP_REV:
|
||||
valid_modes = self.get_allowed_node_boot_mode_values()
|
||||
if mode not in valid_modes:
|
||||
raise exceptions.InvalidParameterValueError(
|
||||
parameter='mode', value=mode,
|
||||
valid_values=list(node_maps.BOOT_SOURCE_MODE_MAP_REV))
|
||||
parameter="mode", value=mode, valid_values=valid_modes
|
||||
)
|
||||
|
||||
data['Boot']['BootSourceOverrideMode'] = (
|
||||
node_maps.BOOT_SOURCE_MODE_MAP_REV[mode])
|
||||
data["Boot"]["BootSourceOverrideMode"] = mode
|
||||
|
||||
self._conn.patch(self.path, data=data)
|
||||
|
||||
def _get_system_path(self):
|
||||
"""Helper function to find the System path"""
|
||||
return utils.get_sub_resource_path_by(
|
||||
self, ['Links', 'ComputerSystem'])
|
||||
|
||||
@property
|
||||
@utils.cache_it
|
||||
def system(self):
|
||||
"""Property to provide reference to `System` instance
|
||||
|
||||
It is calculated once the first time it is queried. On refresh,
|
||||
this property is reset.
|
||||
"""
|
||||
return system.System(
|
||||
self._conn, self._get_system_path(),
|
||||
redfish_version=self.redfish_version)
|
||||
|
||||
def _get_attach_endpoint_action_element(self):
|
||||
attach_endpoint_action = self._actions.attach_endpoint
|
||||
if not attach_endpoint_action:
|
||||
raise exceptions.MissingActionError(
|
||||
action='#ComposedNode.AttachEndpoint',
|
||||
resource=self._path)
|
||||
action="#ComposedNode.AttachEndpoint", resource=self._path
|
||||
)
|
||||
return attach_endpoint_action
|
||||
|
||||
def get_allowed_attach_endpoints(self):
|
||||
@ -343,14 +307,16 @@ class Node(base.ResourceBase):
|
||||
|
||||
if endpoint and endpoint not in valid_endpoints:
|
||||
raise exceptions.InvalidParameterValueError(
|
||||
parameter='endpoint', value=endpoint,
|
||||
valid_values=valid_endpoints)
|
||||
parameter="endpoint",
|
||||
value=endpoint,
|
||||
valid_values=valid_endpoints,
|
||||
)
|
||||
|
||||
data = {}
|
||||
if endpoint is not None:
|
||||
data['Resource'] = {'@odata.id': endpoint}
|
||||
data["Resource"] = {"@odata.id": endpoint}
|
||||
if capacity is not None:
|
||||
data['CapacityGiB'] = capacity
|
||||
data["CapacityGiB"] = capacity
|
||||
|
||||
self._conn.post(target_uri, data=data)
|
||||
|
||||
@ -358,8 +324,8 @@ class Node(base.ResourceBase):
|
||||
detach_endpoint_action = self._actions.detach_endpoint
|
||||
if not detach_endpoint_action:
|
||||
raise exceptions.MissingActionError(
|
||||
action='#ComposedNode.DetachEndpoint',
|
||||
resource=self._path)
|
||||
action="#ComposedNode.DetachEndpoint", resource=self._path
|
||||
)
|
||||
return detach_endpoint_action
|
||||
|
||||
def get_allowed_detach_endpoints(self):
|
||||
@ -383,10 +349,12 @@ class Node(base.ResourceBase):
|
||||
|
||||
if endpoint not in valid_endpoints:
|
||||
raise exceptions.InvalidParameterValueError(
|
||||
parameter='endpoint', value=endpoint,
|
||||
valid_values=valid_endpoints)
|
||||
parameter="endpoint",
|
||||
value=endpoint,
|
||||
valid_values=valid_endpoints,
|
||||
)
|
||||
|
||||
data = {'Resource': endpoint}
|
||||
data = {"Resource": endpoint}
|
||||
|
||||
self._conn.post(target_uri, data=data)
|
||||
|
||||
@ -401,89 +369,93 @@ class Node(base.ResourceBase):
|
||||
self._conn.delete(self.path)
|
||||
|
||||
|
||||
class NodeCollection(base.ResourceCollectionBase):
|
||||
class NodeCollection(rsd_lib_base.ResourceCollectionBase):
|
||||
|
||||
_actions = NodeCollectionActionsField('Actions', required=True)
|
||||
_actions = NodeCollectionActionsField("Actions", required=True)
|
||||
|
||||
@property
|
||||
def _resource_type(self):
|
||||
return Node
|
||||
|
||||
def __init__(self, connector, path, redfish_version=None):
|
||||
"""A class representing a ComposedNodeCollection
|
||||
|
||||
:param connector: A Connector instance
|
||||
:param path: The canonical path to the Node collection resource
|
||||
:param redfish_version: The version of RedFish. Used to construct
|
||||
the object according to schema of the given version.
|
||||
"""
|
||||
super(NodeCollection, self).__init__(connector, path,
|
||||
redfish_version)
|
||||
|
||||
def _get_compose_action_element(self):
|
||||
compose_action = self._actions.compose
|
||||
if not compose_action:
|
||||
raise exceptions.MissingActionError(
|
||||
action='#ComposedNodeCollection.Allocate',
|
||||
resource=self._path)
|
||||
action="#ComposedNodeCollection.Allocate", resource=self._path
|
||||
)
|
||||
return compose_action
|
||||
|
||||
def _create_compose_request(self, name=None, description=None,
|
||||
processor_req=None, memory_req=None,
|
||||
remote_drive_req=None, local_drive_req=None,
|
||||
ethernet_interface_req=None,
|
||||
total_system_core_req=None,
|
||||
total_system_memory_req=None):
|
||||
def _create_compose_request(
|
||||
self,
|
||||
name=None,
|
||||
description=None,
|
||||
processor_req=None,
|
||||
memory_req=None,
|
||||
remote_drive_req=None,
|
||||
local_drive_req=None,
|
||||
ethernet_interface_req=None,
|
||||
total_system_core_req=None,
|
||||
total_system_memory_req=None,
|
||||
):
|
||||
|
||||
request = {}
|
||||
|
||||
if name is not None:
|
||||
request['Name'] = name
|
||||
request["Name"] = name
|
||||
if description is not None:
|
||||
request['Description'] = description
|
||||
request["Description"] = description
|
||||
|
||||
if processor_req is not None:
|
||||
validate(processor_req,
|
||||
node_schemas.processor_req_schema)
|
||||
request['Processors'] = processor_req
|
||||
validate(processor_req, node_schemas.processor_req_schema)
|
||||
request["Processors"] = processor_req
|
||||
|
||||
if memory_req is not None:
|
||||
validate(memory_req,
|
||||
node_schemas.memory_req_schema)
|
||||
request['Memory'] = memory_req
|
||||
validate(memory_req, node_schemas.memory_req_schema)
|
||||
request["Memory"] = memory_req
|
||||
|
||||
if remote_drive_req is not None:
|
||||
validate(remote_drive_req,
|
||||
node_schemas.remote_drive_req_schema)
|
||||
request['RemoteDrives'] = remote_drive_req
|
||||
validate(remote_drive_req, node_schemas.remote_drive_req_schema)
|
||||
request["RemoteDrives"] = remote_drive_req
|
||||
|
||||
if local_drive_req is not None:
|
||||
validate(local_drive_req,
|
||||
node_schemas.local_drive_req_schema)
|
||||
request['LocalDrives'] = local_drive_req
|
||||
validate(local_drive_req, node_schemas.local_drive_req_schema)
|
||||
request["LocalDrives"] = local_drive_req
|
||||
|
||||
if ethernet_interface_req is not None:
|
||||
validate(ethernet_interface_req,
|
||||
node_schemas.ethernet_interface_req_schema)
|
||||
request['EthernetInterfaces'] = ethernet_interface_req
|
||||
validate(
|
||||
ethernet_interface_req,
|
||||
node_schemas.ethernet_interface_req_schema,
|
||||
)
|
||||
request["EthernetInterfaces"] = ethernet_interface_req
|
||||
|
||||
if total_system_core_req is not None:
|
||||
validate(total_system_core_req,
|
||||
node_schemas.total_system_core_req_schema)
|
||||
request['TotalSystemCoreCount'] = total_system_core_req
|
||||
validate(
|
||||
total_system_core_req,
|
||||
node_schemas.total_system_core_req_schema,
|
||||
)
|
||||
request["TotalSystemCoreCount"] = total_system_core_req
|
||||
|
||||
if total_system_memory_req is not None:
|
||||
validate(total_system_memory_req,
|
||||
node_schemas.total_system_memory_req_schema)
|
||||
request['TotalSystemMemoryMiB'] = total_system_memory_req
|
||||
validate(
|
||||
total_system_memory_req,
|
||||
node_schemas.total_system_memory_req_schema,
|
||||
)
|
||||
request["TotalSystemMemoryMiB"] = total_system_memory_req
|
||||
|
||||
return request
|
||||
|
||||
def compose_node(self, name=None, description=None,
|
||||
processor_req=None, memory_req=None,
|
||||
remote_drive_req=None, local_drive_req=None,
|
||||
ethernet_interface_req=None, total_system_core_req=None,
|
||||
total_system_memory_req=None):
|
||||
def compose_node(
|
||||
self,
|
||||
name=None,
|
||||
description=None,
|
||||
processor_req=None,
|
||||
memory_req=None,
|
||||
remote_drive_req=None,
|
||||
local_drive_req=None,
|
||||
ethernet_interface_req=None,
|
||||
total_system_core_req=None,
|
||||
total_system_memory_req=None,
|
||||
):
|
||||
"""Compose a node from RackScale hardware
|
||||
|
||||
:param name: Name of node
|
||||
@ -512,15 +484,17 @@ class NodeCollection(base.ResourceCollectionBase):
|
||||
"""
|
||||
target_uri = self._get_compose_action_element().target_uri
|
||||
properties = self._create_compose_request(
|
||||
name=name, description=description,
|
||||
name=name,
|
||||
description=description,
|
||||
processor_req=processor_req,
|
||||
memory_req=memory_req,
|
||||
remote_drive_req=remote_drive_req,
|
||||
local_drive_req=local_drive_req,
|
||||
ethernet_interface_req=ethernet_interface_req,
|
||||
total_system_core_req=total_system_core_req,
|
||||
total_system_memory_req=total_system_memory_req)
|
||||
total_system_memory_req=total_system_memory_req,
|
||||
)
|
||||
resp = self._conn.post(target_uri, data=properties)
|
||||
LOG.info("Node created at %s", resp.headers['Location'])
|
||||
node_url = resp.headers['Location']
|
||||
LOG.info("Node created at %s", resp.headers["Location"])
|
||||
node_url = resp.headers["Location"]
|
||||
return node_url[node_url.find(self._path):]
|
||||
|
@ -13,183 +13,201 @@
|
||||
# under the License.
|
||||
|
||||
processor_req_schema = {
|
||||
'type': 'array',
|
||||
'items': [{
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'Model': {'type': 'string'},
|
||||
'TotalCores': {'type': 'number'},
|
||||
'AchievableSpeedMHz': {'type': 'number'},
|
||||
'InstructionSet': {
|
||||
'type': 'string',
|
||||
'enum': ['x86', 'x86-64', 'IA-64', 'ARM-A32',
|
||||
'ARM-A64', 'MIPS32', 'MIPS64', 'OEM']
|
||||
},
|
||||
'Oem': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'Brand': {
|
||||
'type': 'string',
|
||||
'enum': ['E3', 'E5', 'E7', 'X3', 'X5', 'X7', 'I3',
|
||||
'I5', 'I7', 'Unknown']
|
||||
"type": "array",
|
||||
"items": [
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"Model": {"type": "string"},
|
||||
"TotalCores": {"type": "number"},
|
||||
"AchievableSpeedMHz": {"type": "number"},
|
||||
"InstructionSet": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"x86",
|
||||
"x86-64",
|
||||
"IA-64",
|
||||
"ARM-A32",
|
||||
"ARM-A64",
|
||||
"MIPS32",
|
||||
"MIPS64",
|
||||
"OEM",
|
||||
],
|
||||
},
|
||||
"Oem": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"Brand": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"E3",
|
||||
"E5",
|
||||
"E7",
|
||||
"X3",
|
||||
"X5",
|
||||
"X7",
|
||||
"I3",
|
||||
"I5",
|
||||
"I7",
|
||||
"Unknown",
|
||||
],
|
||||
},
|
||||
"Capabilities": {
|
||||
"type": "array",
|
||||
"items": [{"type": "string"}],
|
||||
},
|
||||
},
|
||||
'Capabilities': {
|
||||
'type': 'array',
|
||||
'items': [{'type': 'string'}]
|
||||
}
|
||||
}
|
||||
},
|
||||
"Resource": {
|
||||
"type": "object",
|
||||
"properties": {"@odata.id": {"type": "string"}},
|
||||
},
|
||||
"Chassis": {
|
||||
"type": "object",
|
||||
"properties": {"@odata.id": {"type": "string"}},
|
||||
},
|
||||
},
|
||||
'Resource': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'@odata.id': {'type': 'string'}
|
||||
}
|
||||
},
|
||||
'Chassis': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'@odata.id': {'type': 'string'}
|
||||
}
|
||||
}
|
||||
},
|
||||
'additionalProperties': False,
|
||||
}]
|
||||
"additionalProperties": False,
|
||||
}
|
||||
],
|
||||
}
|
||||
|
||||
memory_req_schema = {
|
||||
'type': 'array',
|
||||
'items': [{
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'CapacityMiB': {'type': 'number'},
|
||||
'MemoryDeviceType': {
|
||||
'type': 'string',
|
||||
'enum': ['DDR', 'DDR2', 'DDR3', 'DDR4', 'DDR4_SDRAM',
|
||||
'DDR4E_SDRAM', 'LPDDR4_SDRAM', 'DDR3_SDRAM',
|
||||
'LPDDR3_SDRAM', 'DDR2_SDRAM', 'DDR2_SDRAM_FB_DIMM',
|
||||
'DDR2_SDRAM_FB_DIMM_PROBE', 'DDR_SGRAM',
|
||||
'DDR_SDRAM', 'ROM', 'SDRAM', 'EDO',
|
||||
'FastPageMode', 'PipelinedNibble']
|
||||
"type": "array",
|
||||
"items": [
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"CapacityMiB": {"type": "number"},
|
||||
"MemoryDeviceType": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"DDR",
|
||||
"DDR2",
|
||||
"DDR3",
|
||||
"DDR4",
|
||||
"DDR4_SDRAM",
|
||||
"DDR4E_SDRAM",
|
||||
"LPDDR4_SDRAM",
|
||||
"DDR3_SDRAM",
|
||||
"LPDDR3_SDRAM",
|
||||
"DDR2_SDRAM",
|
||||
"DDR2_SDRAM_FB_DIMM",
|
||||
"DDR2_SDRAM_FB_DIMM_PROBE",
|
||||
"DDR_SGRAM",
|
||||
"DDR_SDRAM",
|
||||
"ROM",
|
||||
"SDRAM",
|
||||
"EDO",
|
||||
"FastPageMode",
|
||||
"PipelinedNibble",
|
||||
],
|
||||
},
|
||||
"SpeedMHz": {"type": "number"},
|
||||
"Manufacturer": {"type": "string"},
|
||||
"DataWidthBits": {"type": "number"},
|
||||
"Resource": {
|
||||
"type": "object",
|
||||
"properties": {"@odata.id": {"type": "string"}},
|
||||
},
|
||||
"Chassis": {
|
||||
"type": "object",
|
||||
"properties": {"@odata.id": {"type": "string"}},
|
||||
},
|
||||
},
|
||||
'SpeedMHz': {'type': 'number'},
|
||||
'Manufacturer': {'type': 'string'},
|
||||
'DataWidthBits': {'type': 'number'},
|
||||
'Resource': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'@odata.id': {'type': 'string'}
|
||||
}
|
||||
},
|
||||
'Chassis': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'@odata.id': {'type': 'string'}
|
||||
}
|
||||
}
|
||||
},
|
||||
'additionalProperties': False,
|
||||
}]
|
||||
"additionalProperties": False,
|
||||
}
|
||||
],
|
||||
}
|
||||
|
||||
remote_drive_req_schema = {
|
||||
'type': 'array',
|
||||
'items': [{
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'CapacityGiB': {'type': 'number'},
|
||||
'iSCSIAddress': {'type': 'string'},
|
||||
'Master': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'Type': {
|
||||
'type': 'string',
|
||||
'enum': ['Snapshot', 'Clone']
|
||||
"type": "array",
|
||||
"items": [
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"CapacityGiB": {"type": "number"},
|
||||
"iSCSIAddress": {"type": "string"},
|
||||
"Master": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"Type": {
|
||||
"type": "string",
|
||||
"enum": ["Snapshot", "Clone"],
|
||||
},
|
||||
"Address": {
|
||||
"type": "object",
|
||||
"properties": {"@odata.id": {"type": "string"}},
|
||||
},
|
||||
},
|
||||
'Address': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'@odata.id': {'type': 'string'}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
'additionalProperties': False,
|
||||
}]
|
||||
},
|
||||
},
|
||||
"additionalProperties": False,
|
||||
}
|
||||
],
|
||||
}
|
||||
|
||||
local_drive_req_schema = {
|
||||
'type': 'array',
|
||||
'items': [{
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'CapacityGiB': {'type': 'number'},
|
||||
'Type': {
|
||||
'type': 'string',
|
||||
'enum': ['HDD', 'SSD']
|
||||
"type": "array",
|
||||
"items": [
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"CapacityGiB": {"type": "number"},
|
||||
"Type": {"type": "string", "enum": ["HDD", "SSD"]},
|
||||
"MinRPM": {"type": "number"},
|
||||
"SerialNumber": {"type": "string"},
|
||||
"Interface": {
|
||||
"type": "string",
|
||||
"enum": ["SAS", "SATA", "NVMe"],
|
||||
},
|
||||
"Resource": {
|
||||
"type": "object",
|
||||
"properties": {"@odata.id": {"type": "string"}},
|
||||
},
|
||||
"Chassis": {
|
||||
"type": "object",
|
||||
"properties": {"@odata.id": {"type": "string"}},
|
||||
},
|
||||
"FabricSwitch": {"type": "boolean"},
|
||||
},
|
||||
'MinRPM': {'type': 'number'},
|
||||
'SerialNumber': {'type': 'string'},
|
||||
'Interface': {
|
||||
'type': 'string',
|
||||
'enum': ['SAS', 'SATA', 'NVMe']
|
||||
},
|
||||
'Resource': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'@odata.id': {'type': 'string'}
|
||||
}
|
||||
},
|
||||
'Chassis': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'@odata.id': {'type': 'string'}
|
||||
}
|
||||
},
|
||||
'FabricSwitch': {'type': 'boolean'}
|
||||
},
|
||||
'additionalProperties': False,
|
||||
}]
|
||||
"additionalProperties": False,
|
||||
}
|
||||
],
|
||||
}
|
||||
|
||||
ethernet_interface_req_schema = {
|
||||
'type': 'array',
|
||||
'items': [{
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'SpeedMbps': {'type': 'number'},
|
||||
'PrimaryVLAN': {'type': 'number'},
|
||||
'VLANs': {
|
||||
'type': 'array',
|
||||
'additionalItems': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'VLANId': {'type': 'number'},
|
||||
'Tagged': {'type': 'boolean'}
|
||||
}
|
||||
}
|
||||
"type": "array",
|
||||
"items": [
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"SpeedMbps": {"type": "number"},
|
||||
"PrimaryVLAN": {"type": "number"},
|
||||
"VLANs": {
|
||||
"type": "array",
|
||||
"additionalItems": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"VLANId": {"type": "number"},
|
||||
"Tagged": {"type": "boolean"},
|
||||
},
|
||||
},
|
||||
},
|
||||
"Resource": {
|
||||
"type": "object",
|
||||
"properties": {"@odata.id": {"type": "string"}},
|
||||
},
|
||||
"Chassis": {
|
||||
"type": "object",
|
||||
"properties": {"@odata.id": {"type": "string"}},
|
||||
},
|
||||
},
|
||||
'Resource': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'@odata.id': {'type': 'string'}
|
||||
}
|
||||
},
|
||||
'Chassis': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'@odata.id': {'type': 'string'}
|
||||
}
|
||||
}
|
||||
},
|
||||
'additionalProperties': False,
|
||||
}]
|
||||
"additionalProperties": False,
|
||||
}
|
||||
],
|
||||
}
|
||||
|
||||
total_system_core_req_schema = {
|
||||
'type': 'number'
|
||||
}
|
||||
total_system_core_req_schema = {"type": "number"}
|
||||
|
||||
total_system_memory_req_schema = {
|
||||
'type': 'number'
|
||||
}
|
||||
total_system_memory_req_schema = {"type": "number"}
|
||||
|
@ -340,11 +340,9 @@ class System(rsd_lib_base.ResourceBase):
|
||||
|
||||
:param target: The target boot source.
|
||||
:param enabled: The frequency, whether to set it for the next
|
||||
reboot only (BOOT_SOURCE_ENABLED_ONCE) or persistent to all
|
||||
future reboots (BOOT_SOURCE_ENABLED_CONTINUOUS) or disabled
|
||||
(BOOT_SOURCE_ENABLED_DISABLED).
|
||||
:param mode: The boot mode, UEFI (BOOT_SOURCE_MODE_UEFI) or
|
||||
BIOS (BOOT_SOURCE_MODE_BIOS).
|
||||
reboot only ("Once") or persistent to all future reboots
|
||||
("Continuous") or disabled ("Disabled").
|
||||
:param mode: The boot mode, UEFI ("UEFI") or BIOS ("Legacy").
|
||||
:raises: InvalidParameterValueError, if any information passed is
|
||||
invalid.
|
||||
"""
|
||||
|
@ -19,192 +19,299 @@ import mock
|
||||
import testtools
|
||||
|
||||
from sushy import exceptions
|
||||
from sushy.resources.system import system
|
||||
|
||||
from rsd_lib.resources.v2_1.node import constants as node_cons
|
||||
from rsd_lib.resources.v2_1.node import mappings as node_maps
|
||||
from rsd_lib import constants
|
||||
from rsd_lib.resources.v2_1.node import node
|
||||
from rsd_lib.tests.unit.fakes import request_fakes
|
||||
|
||||
|
||||
class NodeTestCase(testtools.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(NodeTestCase, self).setUp()
|
||||
self.conn = mock.Mock()
|
||||
with open('rsd_lib/tests/unit/json_samples/v2_1/node.json', 'r') as f:
|
||||
with open("rsd_lib/tests/unit/json_samples/v2_1/node.json", "r") as f:
|
||||
self.conn.get.return_value.json.return_value = json.loads(f.read())
|
||||
|
||||
self.node_inst = node.Node(
|
||||
self.conn, '/redfish/v1/Nodes/Node1',
|
||||
redfish_version='1.0.2')
|
||||
self.conn, "/redfish/v1/Nodes/Node1", redfish_version="1.0.2"
|
||||
)
|
||||
|
||||
def test__parse_attributes(self):
|
||||
self.node_inst._parse_attributes()
|
||||
self.assertEqual('1.0.2', self.node_inst.redfish_version)
|
||||
self.assertEqual('Node #1', self.node_inst.description)
|
||||
self.assertEqual(node_cons.COMPOSED_NODE_STATE_ALLOCATED,
|
||||
self.node_inst.composed_node_state)
|
||||
self.assertEqual('Node1', self.node_inst.identity)
|
||||
self.assertEqual('Composed Node', self.node_inst.name)
|
||||
self.assertEqual('fa39d108-7d70-400a-9db2-6940375c31c2',
|
||||
self.node_inst.uuid)
|
||||
self.assertEqual(node_cons.NODE_POWER_STATE_ON,
|
||||
self.node_inst.power_state)
|
||||
self.assertEqual('Enabled', self.node_inst.status.state)
|
||||
self.assertEqual('OK', self.node_inst.status.health)
|
||||
self.assertEqual('OK', self.node_inst.status.health_rollup)
|
||||
self.assertEqual(32, self.node_inst.memory_summary.size_gib)
|
||||
self.assertEqual('Enabled', self.node_inst.memory_summary.status.state)
|
||||
self.assertEqual('OK', self.node_inst.memory_summary.status.health)
|
||||
self.assertEqual("1.0.2", self.node_inst.redfish_version)
|
||||
self.assertEqual("Node #1", self.node_inst.description)
|
||||
self.assertEqual("Allocated", self.node_inst.composed_node_state)
|
||||
self.assertEqual("Node1", self.node_inst.identity)
|
||||
self.assertEqual("Composed Node", self.node_inst.name)
|
||||
self.assertEqual(
|
||||
'OK', self.node_inst.memory_summary.status.health_rollup)
|
||||
self.assertEqual(2, self.node_inst.processor_summary.count)
|
||||
self.assertEqual('Multi-Core Intel(R) Xeon(R) processor 7xxx Series',
|
||||
self.node_inst.processor_summary.model)
|
||||
"fa39d108-7d70-400a-9db2-6940375c31c2", self.node_inst.uuid
|
||||
)
|
||||
self.assertEqual("On", self.node_inst.power_state)
|
||||
self.assertEqual("Enabled", self.node_inst.status.state)
|
||||
self.assertEqual("OK", self.node_inst.status.health)
|
||||
self.assertEqual("OK", self.node_inst.status.health_rollup)
|
||||
self.assertEqual(32, self.node_inst.memory.total_system_memory_gib)
|
||||
self.assertEqual("Enabled", self.node_inst.memory.status.state)
|
||||
self.assertEqual("OK", self.node_inst.memory.status.health)
|
||||
self.assertEqual("OK", self.node_inst.memory.status.health_rollup)
|
||||
self.assertEqual(2, self.node_inst.processors.count)
|
||||
self.assertEqual(
|
||||
'Enabled', self.node_inst.processor_summary.status.state)
|
||||
self.assertEqual('OK', self.node_inst.processor_summary.status.health)
|
||||
"Multi-Core Intel(R) Xeon(R) processor 7xxx Series",
|
||||
self.node_inst.processors.model,
|
||||
)
|
||||
self.assertEqual("Enabled", self.node_inst.processors.status.state)
|
||||
self.assertEqual("OK", self.node_inst.processors.status.health)
|
||||
self.assertEqual("OK", self.node_inst.processors.status.health_rollup)
|
||||
self.assertEqual(
|
||||
'OK', self.node_inst.processor_summary.status.health_rollup)
|
||||
"/redfish/v1/Systems/System1", self.node_inst.links.computer_system
|
||||
)
|
||||
self.assertEqual(
|
||||
'/redfish/v1/Systems/System1', self.node_inst.links.system)
|
||||
("/redfish/v1/Systems/System1/Processors/CPU1",),
|
||||
self.node_inst.links.processors,
|
||||
)
|
||||
self.assertEqual(
|
||||
('/redfish/v1/Systems/System1/Processors/CPU1',),
|
||||
self.node_inst.links.processors)
|
||||
("/redfish/v1/Systems/System1/Memory/Dimm1",),
|
||||
self.node_inst.links.memory,
|
||||
)
|
||||
self.assertEqual(
|
||||
('/redfish/v1/Systems/System1/Memory/Dimm1',),
|
||||
self.node_inst.links.memory)
|
||||
("/redfish/v1/Systems/System1/EthernetInterfaces/LAN1",),
|
||||
self.node_inst.links.ethernet_interfaces,
|
||||
)
|
||||
self.assertEqual(
|
||||
('/redfish/v1/Systems/System1/EthernetInterfaces/LAN1',),
|
||||
self.node_inst.links.ethernet_interfaces)
|
||||
("/redfish/v1/Chassis/Blade1/Drives/1",),
|
||||
self.node_inst.links.local_drives,
|
||||
)
|
||||
self.assertEqual(
|
||||
('/redfish/v1/Chassis/Blade1/Drives/1',),
|
||||
self.node_inst.links.local_drives)
|
||||
self.assertEqual(
|
||||
('/redfish/v1/Services/RSS1/Targets/target1',),
|
||||
self.node_inst.links.remote_drives)
|
||||
("/redfish/v1/Services/RSS1/Targets/target1",),
|
||||
self.node_inst.links.remote_drives,
|
||||
)
|
||||
|
||||
def test__parse_attributes_missing_actions(self):
|
||||
self.node_inst.json.pop('Actions')
|
||||
self.assertRaisesRegex(
|
||||
exceptions.MissingAttributeError, 'attribute Actions',
|
||||
self.node_inst._parse_attributes)
|
||||
|
||||
def test__parse_attributes_missing_boot(self):
|
||||
self.node_inst.json.pop('Boot')
|
||||
self.assertRaisesRegex(
|
||||
exceptions.MissingAttributeError, 'attribute Boot',
|
||||
self.node_inst._parse_attributes)
|
||||
|
||||
def test__parse_attributes_missing_reset_target(self):
|
||||
self.node_inst.json['Actions']['#ComposedNode.Reset'].pop(
|
||||
'target')
|
||||
self.node_inst.json.pop("Actions")
|
||||
self.assertRaisesRegex(
|
||||
exceptions.MissingAttributeError,
|
||||
'attribute Actions/#ComposedNode.Reset/target',
|
||||
self.node_inst._parse_attributes)
|
||||
"attribute Actions",
|
||||
self.node_inst._parse_attributes,
|
||||
)
|
||||
|
||||
def test__parse_attributes_missing_reset_target(self):
|
||||
self.node_inst.json["Actions"]["#ComposedNode.Reset"].pop("target")
|
||||
self.assertRaisesRegex(
|
||||
exceptions.MissingAttributeError,
|
||||
"attribute Actions/#ComposedNode.Reset/target",
|
||||
self.node_inst._parse_attributes,
|
||||
)
|
||||
|
||||
def test_get__reset_action_element(self):
|
||||
value = self.node_inst._get_reset_action_element()
|
||||
self.assertEqual("/redfish/v1/Nodes/Node1/Actions/"
|
||||
"ComposedNode.Reset",
|
||||
value.target_uri)
|
||||
self.assertEqual(["On",
|
||||
"ForceOff",
|
||||
"GracefulRestart",
|
||||
"ForceRestart",
|
||||
"Nmi",
|
||||
"ForceOn",
|
||||
"PushPowerButton",
|
||||
"GracefulShutdown"
|
||||
],
|
||||
value.allowed_values)
|
||||
self.assertEqual(
|
||||
"/redfish/v1/Nodes/Node1/Actions/" "ComposedNode.Reset",
|
||||
value.target_uri,
|
||||
)
|
||||
self.assertEqual(
|
||||
[
|
||||
"On",
|
||||
"ForceOff",
|
||||
"GracefulRestart",
|
||||
"ForceRestart",
|
||||
"Nmi",
|
||||
"ForceOn",
|
||||
"PushPowerButton",
|
||||
"GracefulShutdown",
|
||||
],
|
||||
value.allowed_values,
|
||||
)
|
||||
|
||||
def test__get_reset_action_element_missing_reset_action(self):
|
||||
self.node_inst._actions.reset = None
|
||||
self.assertRaisesRegex(
|
||||
exceptions.MissingActionError, 'action #ComposedNode.Reset',
|
||||
self.node_inst._get_reset_action_element)
|
||||
exceptions.MissingActionError,
|
||||
"action #ComposedNode.Reset",
|
||||
self.node_inst._get_reset_action_element,
|
||||
)
|
||||
|
||||
def test__get_assemble_action_element(self):
|
||||
value = self.node_inst._get_assemble_action_element()
|
||||
self.assertEqual("/redfish/v1/Nodes/Node1/Actions/"
|
||||
"ComposedNode.Assemble",
|
||||
value.target_uri)
|
||||
self.assertEqual(
|
||||
"/redfish/v1/Nodes/Node1/Actions/" "ComposedNode.Assemble",
|
||||
value.target_uri,
|
||||
)
|
||||
|
||||
def test__get_attach_endpoint_action_element(self):
|
||||
value = self.node_inst._get_attach_endpoint_action_element()
|
||||
self.assertEqual('/redfish/v1/Nodes/Node1/Actions/'
|
||||
'ComposedNode.AttachEndpoint',
|
||||
value.target_uri)
|
||||
self.assertEqual(
|
||||
"/redfish/v1/Nodes/Node1/Actions/" "ComposedNode.AttachEndpoint",
|
||||
value.target_uri,
|
||||
)
|
||||
|
||||
self.assertEqual(('/redfish/v1/Chassis/PCIeSwitchChassis/'
|
||||
'Drives/Disk.Bay.1',
|
||||
'/redfish/v1/Chassis/PCIeSwitchChassis/'
|
||||
'Drives/Disk.Bay.2'),
|
||||
value.allowed_values)
|
||||
self.assertEqual(
|
||||
(
|
||||
"/redfish/v1/Chassis/PCIeSwitchChassis/" "Drives/Disk.Bay.1",
|
||||
"/redfish/v1/Chassis/PCIeSwitchChassis/" "Drives/Disk.Bay.2",
|
||||
),
|
||||
value.allowed_values,
|
||||
)
|
||||
|
||||
def test__get_detach_endpoint_action_element(self):
|
||||
value = self.node_inst._get_detach_endpoint_action_element()
|
||||
self.assertEqual('/redfish/v1/Nodes/Node1/Actions/'
|
||||
'ComposedNode.DetachEndpoint',
|
||||
value.target_uri)
|
||||
self.assertEqual(
|
||||
"/redfish/v1/Nodes/Node1/Actions/" "ComposedNode.DetachEndpoint",
|
||||
value.target_uri,
|
||||
)
|
||||
|
||||
self.assertEqual(tuple(['/redfish/v1/Chassis/'
|
||||
'PCIeSwitchChassis/Drives/Disk.Bay.3']),
|
||||
value.allowed_values)
|
||||
self.assertEqual(
|
||||
tuple(
|
||||
["/redfish/v1/Chassis/" "PCIeSwitchChassis/Drives/Disk.Bay.3"]
|
||||
),
|
||||
value.allowed_values,
|
||||
)
|
||||
|
||||
def test_get_allowed_reset_node_values(self):
|
||||
values = self.node_inst.get_allowed_reset_node_values()
|
||||
expected = set([node_cons.RESET_GRACEFUL_SHUTDOWN,
|
||||
node_cons.RESET_GRACEFUL_RESTART,
|
||||
node_cons.RESET_FORCE_RESTART,
|
||||
node_cons.RESET_FORCE_OFF,
|
||||
node_cons.RESET_FORCE_ON,
|
||||
node_cons.RESET_ON,
|
||||
node_cons.RESET_NMI,
|
||||
node_cons.RESET_PUSH_POWER_BUTTON])
|
||||
expected = set(
|
||||
[
|
||||
"On",
|
||||
"ForceOff",
|
||||
"GracefulShutdown",
|
||||
"GracefulRestart",
|
||||
"ForceRestart",
|
||||
"Nmi",
|
||||
"ForceOn",
|
||||
"PushPowerButton",
|
||||
]
|
||||
)
|
||||
self.assertEqual(expected, values)
|
||||
self.assertIsInstance(values, set)
|
||||
|
||||
@mock.patch.object(node.LOG, 'warning', autospec=True)
|
||||
def test_get_allowed_reset_system_values_no_values_specified(
|
||||
self, mock_log):
|
||||
@mock.patch.object(node.LOG, "warning", autospec=True)
|
||||
def test_get_allowed_reset_node_values_no_values_specified(self, mock_log):
|
||||
self.node_inst._actions.reset.allowed_values = {}
|
||||
values = self.node_inst.get_allowed_reset_node_values()
|
||||
# Assert it returns all values if it can't get the specific ones
|
||||
expected = set([node_cons.RESET_GRACEFUL_SHUTDOWN,
|
||||
node_cons.RESET_GRACEFUL_RESTART,
|
||||
node_cons.RESET_FORCE_RESTART,
|
||||
node_cons.RESET_FORCE_OFF,
|
||||
node_cons.RESET_FORCE_ON,
|
||||
node_cons.RESET_ON,
|
||||
node_cons.RESET_NMI,
|
||||
node_cons.RESET_PUSH_POWER_BUTTON])
|
||||
expected = set(constants.RESET_TYPE_VALUE)
|
||||
self.assertEqual(expected, values)
|
||||
self.assertIsInstance(values, set)
|
||||
self.assertEqual(1, mock_log.call_count)
|
||||
|
||||
def test_reset_node(self):
|
||||
self.node_inst.reset_node(node_cons.RESET_FORCE_OFF)
|
||||
self.node_inst.reset_node("ForceOff")
|
||||
self.node_inst._conn.post.assert_called_once_with(
|
||||
'/redfish/v1/Nodes/Node1/Actions/ComposedNode.Reset',
|
||||
data={'ResetType': 'ForceOff'})
|
||||
"/redfish/v1/Nodes/Node1/Actions/ComposedNode.Reset",
|
||||
data={"ResetType": "ForceOff"},
|
||||
)
|
||||
|
||||
def test_reset_node_invalid_value(self):
|
||||
with self.assertRaisesRegex(
|
||||
exceptions.InvalidParameterValueError,
|
||||
'The parameter "value" value "invalid-value" is invalid',
|
||||
):
|
||||
self.node_inst.reset_node("invalid-value")
|
||||
|
||||
def test_get_allowed_node_boot_source_values(self):
|
||||
values = self.node_inst.get_allowed_node_boot_source_values()
|
||||
expected = set(["None", "Pxe", "Hdd", "RemoteDrive"])
|
||||
self.assertEqual(expected, values)
|
||||
self.assertIsInstance(values, set)
|
||||
|
||||
@mock.patch.object(node.LOG, "warning", autospec=True)
|
||||
def test_get_allowed_node_boot_source_values_no_values_specified(
|
||||
self, mock_log
|
||||
):
|
||||
self.node_inst.boot.boot_source_override_target_allowed_values = None
|
||||
values = self.node_inst.get_allowed_node_boot_source_values()
|
||||
# Assert it returns all values if it can't get the specific ones
|
||||
expected = set(constants.BOOT_SOURCE_TARGET_VALUE)
|
||||
self.assertEqual(expected, values)
|
||||
self.assertIsInstance(values, set)
|
||||
self.assertEqual(1, mock_log.call_count)
|
||||
|
||||
def test_get_allowed_node_boot_mode_values(self):
|
||||
values = self.node_inst.get_allowed_node_boot_mode_values()
|
||||
expected = set(["Legacy", "UEFI"])
|
||||
self.assertEqual(expected, values)
|
||||
self.assertIsInstance(values, set)
|
||||
|
||||
@mock.patch.object(node.LOG, "warning", autospec=True)
|
||||
def test_get_allowed_node_boot_mode_values_no_values_specified(
|
||||
self, mock_log
|
||||
):
|
||||
self.node_inst.boot.boot_source_override_mode_allowed_values = None
|
||||
values = self.node_inst.get_allowed_node_boot_mode_values()
|
||||
# Assert it returns all values if it can't get the specific ones
|
||||
expected = set(constants.BOOT_SOURCE_MODE_VALUE)
|
||||
self.assertEqual(expected, values)
|
||||
self.assertIsInstance(values, set)
|
||||
self.assertEqual(1, mock_log.call_count)
|
||||
|
||||
def test_set_node_boot_source(self):
|
||||
self.node_inst.set_node_boot_source(
|
||||
target="Pxe", enabled="Continuous", mode="UEFI"
|
||||
)
|
||||
self.node_inst._conn.patch.assert_called_once_with(
|
||||
"/redfish/v1/Nodes/Node1",
|
||||
data={
|
||||
"Boot": {
|
||||
"BootSourceOverrideEnabled": "Continuous",
|
||||
"BootSourceOverrideTarget": "Pxe",
|
||||
"BootSourceOverrideMode": "UEFI",
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
def test_set_node_boot_source_no_mode_specified(self):
|
||||
self.node_inst.set_node_boot_source(target="Hdd")
|
||||
self.node_inst._conn.patch.assert_called_once_with(
|
||||
"/redfish/v1/Nodes/Node1",
|
||||
data={
|
||||
"Boot": {
|
||||
"BootSourceOverrideEnabled": "Once",
|
||||
"BootSourceOverrideTarget": "Hdd",
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
def test_set_node_boot_source_invalid_target(self):
|
||||
with self.assertRaisesRegex(
|
||||
exceptions.InvalidParameterValueError,
|
||||
'The parameter "target" value "invalid-target" is invalid',
|
||||
):
|
||||
self.node_inst.set_node_boot_source("invalid-target")
|
||||
|
||||
def test_set_node_boot_source_invalid_enabled(self):
|
||||
with self.assertRaisesRegex(
|
||||
exceptions.InvalidParameterValueError,
|
||||
'The parameter "enabled" value "invalid-enabled" is invalid',
|
||||
):
|
||||
self.node_inst.set_node_boot_source(
|
||||
"Hdd", enabled="invalid-enabled"
|
||||
)
|
||||
|
||||
def test_set_node_boot_source_invalid_mode(self):
|
||||
with self.assertRaisesRegex(
|
||||
exceptions.InvalidParameterValueError,
|
||||
'The parameter "mode" value "invalid-mode" is invalid',
|
||||
):
|
||||
self.node_inst.set_node_boot_source("Hdd", mode="invalid-mode")
|
||||
|
||||
def test_assemble_node(self):
|
||||
self.node_inst.assemble_node()
|
||||
self.node_inst._conn.post.assert_called_once_with(
|
||||
'/redfish/v1/Nodes/Node1/Actions/ComposedNode.Assemble')
|
||||
"/redfish/v1/Nodes/Node1/Actions/ComposedNode.Assemble"
|
||||
)
|
||||
|
||||
def test_get_allowed_attach_endpoints(self):
|
||||
expected = self.node_inst.get_allowed_attach_endpoints()
|
||||
result = ("/redfish/v1/Chassis/PCIeSwitchChassis/Drives/Disk.Bay.1",
|
||||
"/redfish/v1/Chassis/PCIeSwitchChassis/Drives/Disk.Bay.2")
|
||||
result = (
|
||||
"/redfish/v1/Chassis/PCIeSwitchChassis/Drives/Disk.Bay.1",
|
||||
"/redfish/v1/Chassis/PCIeSwitchChassis/Drives/Disk.Bay.2",
|
||||
)
|
||||
self.assertEqual(expected, result)
|
||||
|
||||
(self.node_inst._json['Actions']['#ComposedNode.AttachEndpoint']
|
||||
['Resource@Redfish.AllowableValues']) = []
|
||||
(
|
||||
self.node_inst._json["Actions"]["#ComposedNode.AttachEndpoint"][
|
||||
"Resource@Redfish.AllowableValues"
|
||||
]
|
||||
) = []
|
||||
self.node_inst._parse_attributes()
|
||||
expected = self.node_inst.get_allowed_attach_endpoints()
|
||||
result = ()
|
||||
@ -212,32 +319,44 @@ class NodeTestCase(testtools.TestCase):
|
||||
|
||||
def test_attach_endpoint(self):
|
||||
self.node_inst.attach_endpoint(
|
||||
endpoint='/redfish/v1/Chassis/PCIeSwitchChassis/Drives/Disk.Bay.1',
|
||||
capacity=100)
|
||||
endpoint="/redfish/v1/Chassis/PCIeSwitchChassis/Drives/Disk.Bay.1",
|
||||
capacity=100,
|
||||
)
|
||||
self.node_inst._conn.post.assert_called_once_with(
|
||||
'/redfish/v1/Nodes/Node1/Actions/ComposedNode.AttachEndpoint',
|
||||
data={'Resource': {'@odata.id': '/redfish/v1/Chassis/'
|
||||
'PCIeSwitchChassis/Drives/Disk.Bay.1'},
|
||||
'CapacityGiB': 100})
|
||||
"/redfish/v1/Nodes/Node1/Actions/ComposedNode.AttachEndpoint",
|
||||
data={
|
||||
"Resource": {
|
||||
"@odata.id": "/redfish/v1/Chassis/"
|
||||
"PCIeSwitchChassis/Drives/Disk.Bay.1"
|
||||
},
|
||||
"CapacityGiB": 100,
|
||||
},
|
||||
)
|
||||
|
||||
def test_attach_endpoint_invalid_parameter(self):
|
||||
self.assertRaises(exceptions.InvalidParameterValueError,
|
||||
self.node_inst.attach_endpoint,
|
||||
endpoint='invalid')
|
||||
self.assertRaises(
|
||||
exceptions.InvalidParameterValueError,
|
||||
self.node_inst.attach_endpoint,
|
||||
endpoint="invalid",
|
||||
)
|
||||
|
||||
def test_attach_endpoint_only_with_capacity_parameter(self):
|
||||
self.node_inst.attach_endpoint(capacity=100)
|
||||
self.node_inst._conn.post.assert_called_once_with(
|
||||
'/redfish/v1/Nodes/Node1/Actions/ComposedNode.AttachEndpoint',
|
||||
data={'CapacityGiB': 100})
|
||||
"/redfish/v1/Nodes/Node1/Actions/ComposedNode.AttachEndpoint",
|
||||
data={"CapacityGiB": 100},
|
||||
)
|
||||
|
||||
def test_get_allowed_detach_endpoints(self):
|
||||
expected = self.node_inst.get_allowed_detach_endpoints()
|
||||
result = ("/redfish/v1/Chassis/PCIeSwitchChassis/Drives/Disk.Bay.3",)
|
||||
self.assertEqual(expected, result)
|
||||
|
||||
(self.node_inst._json['Actions']['#ComposedNode.DetachEndpoint']
|
||||
['Resource@Redfish.AllowableValues']) = []
|
||||
(
|
||||
self.node_inst._json["Actions"]["#ComposedNode.DetachEndpoint"][
|
||||
"Resource@Redfish.AllowableValues"
|
||||
]
|
||||
) = []
|
||||
self.node_inst._parse_attributes()
|
||||
expected = self.node_inst.get_allowed_detach_endpoints()
|
||||
result = ()
|
||||
@ -245,319 +364,231 @@ class NodeTestCase(testtools.TestCase):
|
||||
|
||||
def test_detach_endpoint(self):
|
||||
self.node_inst.detach_endpoint(
|
||||
endpoint='/redfish/v1/Chassis/PCIeSwitchChassis/Drives/Disk.Bay.3')
|
||||
endpoint="/redfish/v1/Chassis/PCIeSwitchChassis/Drives/Disk.Bay.3"
|
||||
)
|
||||
self.node_inst._conn.post.assert_called_once_with(
|
||||
'/redfish/v1/Nodes/Node1/Actions/ComposedNode.DetachEndpoint',
|
||||
data={'Resource': '/redfish/v1/Chassis/PCIeSwitchChassis/'
|
||||
'Drives/Disk.Bay.3'})
|
||||
"/redfish/v1/Nodes/Node1/Actions/ComposedNode.DetachEndpoint",
|
||||
data={
|
||||
"Resource": "/redfish/v1/Chassis/PCIeSwitchChassis/"
|
||||
"Drives/Disk.Bay.3"
|
||||
},
|
||||
)
|
||||
|
||||
def test_detach_endpoint_invalid_parameter(self):
|
||||
self.assertRaises(exceptions.InvalidParameterValueError,
|
||||
self.node_inst.detach_endpoint,
|
||||
endpoint='invalid')
|
||||
|
||||
def test_reset_node_invalid_value(self):
|
||||
self.assertRaises(exceptions.InvalidParameterValueError,
|
||||
self.node_inst.reset_node, 'invalid-value')
|
||||
|
||||
def test_get_allowed_node_boot_source_values(self):
|
||||
values = self.node_inst.get_allowed_node_boot_source_values()
|
||||
expected = set([node_cons.BOOT_SOURCE_TARGET_NONE,
|
||||
node_cons.BOOT_SOURCE_TARGET_PXE,
|
||||
node_cons.BOOT_SOURCE_TARGET_HDD])
|
||||
self.assertEqual(expected, values)
|
||||
self.assertIsInstance(values, set)
|
||||
|
||||
@mock.patch.object(node.LOG, 'warning', autospec=True)
|
||||
def test_get_allowed_node_boot_source_values_no_values_specified(
|
||||
self, mock_log):
|
||||
self.node_inst.boot.allowed_values = None
|
||||
values = self.node_inst.get_allowed_node_boot_source_values()
|
||||
# Assert it returns all values if it can't get the specific ones
|
||||
expected = set([node_cons.BOOT_SOURCE_TARGET_NONE,
|
||||
node_cons.BOOT_SOURCE_TARGET_PXE,
|
||||
node_cons.BOOT_SOURCE_TARGET_HDD])
|
||||
self.assertEqual(expected, values)
|
||||
self.assertIsInstance(values, set)
|
||||
self.assertEqual(1, mock_log.call_count)
|
||||
|
||||
def test_set_node_boot_source(self):
|
||||
self.node_inst.set_node_boot_source(
|
||||
node_cons.BOOT_SOURCE_TARGET_PXE,
|
||||
enabled=node_cons.BOOT_SOURCE_ENABLED_CONTINUOUS,
|
||||
mode=node_cons.BOOT_SOURCE_MODE_UEFI)
|
||||
self.node_inst._conn.patch.assert_called_once_with(
|
||||
'/redfish/v1/Nodes/Node1',
|
||||
data={'Boot': {'BootSourceOverrideEnabled': 'Continuous',
|
||||
'BootSourceOverrideTarget': 'Pxe',
|
||||
'BootSourceOverrideMode': 'UEFI'}})
|
||||
|
||||
def test_set_node_boot_source_no_mode_specified(self):
|
||||
self.node_inst.set_node_boot_source(
|
||||
node_cons.BOOT_SOURCE_TARGET_HDD,
|
||||
enabled=node_cons.BOOT_SOURCE_ENABLED_ONCE)
|
||||
self.node_inst._conn.patch.assert_called_once_with(
|
||||
'/redfish/v1/Nodes/Node1',
|
||||
data={'Boot': {'BootSourceOverrideEnabled': 'Once',
|
||||
'BootSourceOverrideTarget': 'Hdd'}})
|
||||
|
||||
def test_set_node_boot_source_invalid_target(self):
|
||||
self.assertRaises(exceptions.InvalidParameterValueError,
|
||||
self.node_inst.set_node_boot_source,
|
||||
'invalid-target')
|
||||
|
||||
def test_set_node_boot_source_invalid_enabled(self):
|
||||
with self.assertRaisesRegex(
|
||||
self.assertRaises(
|
||||
exceptions.InvalidParameterValueError,
|
||||
'"enabled" value.*{0}'.format(
|
||||
list(node_maps.BOOT_SOURCE_ENABLED_MAP_REV))):
|
||||
|
||||
self.node_inst.set_node_boot_source(
|
||||
node_cons.BOOT_SOURCE_TARGET_HDD,
|
||||
enabled='invalid-enabled')
|
||||
|
||||
def test__get_system_path_missing_systems_attr(self):
|
||||
self.node_inst._json.get('Links').pop('ComputerSystem')
|
||||
self.assertRaisesRegex(
|
||||
exceptions.MissingAttributeError, 'attribute Links/ComputerSystem',
|
||||
self.node_inst._get_system_path)
|
||||
self.node_inst.detach_endpoint,
|
||||
endpoint="invalid",
|
||||
)
|
||||
|
||||
def test_memory_summary_missing_attr(self):
|
||||
# | GIVEN |
|
||||
self.node_inst._json['Memory']['Status'].pop('Health')
|
||||
self.node_inst._json["Memory"]["Status"].pop("Health")
|
||||
# | WHEN |
|
||||
self.node_inst._parse_attributes()
|
||||
# | THEN |
|
||||
self.assertEqual(32, self.node_inst.memory_summary.size_gib)
|
||||
self.assertEqual(None, self.node_inst.memory_summary.status.health)
|
||||
self.assertEqual('Enabled', self.node_inst.memory_summary.status.state)
|
||||
self.assertEqual(
|
||||
'OK', self.node_inst.memory_summary.status.health_rollup)
|
||||
self.assertEqual(32, self.node_inst.memory.total_system_memory_gib)
|
||||
self.assertEqual(None, self.node_inst.memory.status.health)
|
||||
self.assertEqual("Enabled", self.node_inst.memory.status.state)
|
||||
self.assertEqual("OK", self.node_inst.memory.status.health_rollup)
|
||||
|
||||
# | GIVEN |
|
||||
self.node_inst._json['Memory']['Status'].pop('State')
|
||||
self.node_inst._json["Memory"]["Status"].pop("State")
|
||||
# | WHEN |
|
||||
self.node_inst._parse_attributes()
|
||||
# | THEN |
|
||||
self.assertEqual(32, self.node_inst.memory_summary.size_gib)
|
||||
self.assertEqual(None, self.node_inst.memory_summary.status.health)
|
||||
self.assertEqual(None, self.node_inst.memory_summary.status.state)
|
||||
self.assertEqual(
|
||||
'OK', self.node_inst.memory_summary.status.health_rollup)
|
||||
self.assertEqual(32, self.node_inst.memory.total_system_memory_gib)
|
||||
self.assertEqual(None, self.node_inst.memory.status.health)
|
||||
self.assertEqual(None, self.node_inst.memory.status.state)
|
||||
self.assertEqual("OK", self.node_inst.memory.status.health_rollup)
|
||||
|
||||
# | GIVEN |
|
||||
self.node_inst._json['Memory']['Status'].pop('HealthRollup')
|
||||
self.node_inst._json["Memory"]["Status"].pop("HealthRollup")
|
||||
# | WHEN |
|
||||
self.node_inst._parse_attributes()
|
||||
# | THEN |
|
||||
self.assertEqual(32, self.node_inst.memory_summary.size_gib)
|
||||
self.assertEqual(None, self.node_inst.memory_summary.status.health)
|
||||
self.assertEqual(None, self.node_inst.memory_summary.status.state)
|
||||
self.assertEqual(
|
||||
None, self.node_inst.memory_summary.status.health_rollup)
|
||||
self.assertEqual(32, self.node_inst.memory.total_system_memory_gib)
|
||||
self.assertEqual(None, self.node_inst.memory.status.health)
|
||||
self.assertEqual(None, self.node_inst.memory.status.state)
|
||||
self.assertEqual(None, self.node_inst.memory.status.health_rollup)
|
||||
|
||||
# | GIVEN |
|
||||
self.node_inst._json['Memory'].pop('Status')
|
||||
self.node_inst._json["Memory"].pop("Status")
|
||||
# | WHEN |
|
||||
self.node_inst._parse_attributes()
|
||||
# | THEN |
|
||||
self.assertEqual(32, self.node_inst.memory_summary.size_gib)
|
||||
self.assertEqual(None, self.node_inst.memory_summary.status)
|
||||
self.assertEqual(32, self.node_inst.memory.total_system_memory_gib)
|
||||
self.assertEqual(None, self.node_inst.memory.status)
|
||||
|
||||
# | GIVEN |
|
||||
self.node_inst._json['Memory'].pop('TotalSystemMemoryGiB')
|
||||
self.node_inst._json["Memory"].pop("TotalSystemMemoryGiB")
|
||||
# | WHEN |
|
||||
self.node_inst._parse_attributes()
|
||||
# | THEN |
|
||||
self.assertEqual(None, self.node_inst.memory_summary.size_gib)
|
||||
self.assertEqual(None, self.node_inst.memory_summary.status)
|
||||
self.assertEqual(None, self.node_inst.memory.total_system_memory_gib)
|
||||
self.assertEqual(None, self.node_inst.memory.status)
|
||||
|
||||
# | GIVEN |
|
||||
self.node_inst._json.pop('Memory')
|
||||
self.node_inst._json.pop("Memory")
|
||||
# | WHEN |
|
||||
self.node_inst._parse_attributes()
|
||||
# | THEN |
|
||||
self.assertEqual(None, self.node_inst.memory_summary)
|
||||
|
||||
def test_system(self):
|
||||
# | GIVEN |
|
||||
self.conn.get.return_value.json.reset_mock()
|
||||
with open('rsd_lib/tests/unit/json_samples/v2_1/system.json',
|
||||
'r') as f:
|
||||
self.conn.get.return_value.json.return_value = json.loads(f.read())
|
||||
# | WHEN |
|
||||
actual_system = self.node_inst.system
|
||||
# | THEN |
|
||||
self.assertIsInstance(actual_system,
|
||||
system.System)
|
||||
self.conn.get.return_value.json.assert_called_once_with()
|
||||
|
||||
# reset mock
|
||||
self.conn.get.return_value.json.reset_mock()
|
||||
# | WHEN & THEN |
|
||||
# tests for same object on invoking subsequently
|
||||
self.assertIs(actual_system,
|
||||
self.node_inst.system)
|
||||
self.conn.get.return_value.json.assert_not_called()
|
||||
|
||||
def test_system_on_refresh(self):
|
||||
# | GIVEN |
|
||||
with open('rsd_lib/tests/unit/json_samples/v2_1/system.json',
|
||||
'r') as f:
|
||||
self.conn.get.return_value.json.return_value = json.loads(f.read())
|
||||
# | WHEN & THEN |
|
||||
self.assertIsInstance(self.node_inst.system,
|
||||
system.System)
|
||||
|
||||
# On refreshing the system instance...
|
||||
with open('rsd_lib/tests/unit/json_samples/v2_1/node.json', 'r') as f:
|
||||
self.conn.get.return_value.json.return_value = json.loads(f.read())
|
||||
|
||||
self.node_inst.invalidate()
|
||||
self.node_inst.refresh(force=False)
|
||||
|
||||
# | GIVEN |
|
||||
with open('rsd_lib/tests/unit/json_samples/v2_1/system.json',
|
||||
'r') as f:
|
||||
self.conn.get.return_value.json.return_value = json.loads(f.read())
|
||||
# | WHEN & THEN |
|
||||
self.assertIsInstance(self.node_inst.system,
|
||||
system.System)
|
||||
self.assertEqual(None, self.node_inst.memory)
|
||||
|
||||
def test_delete_node(self):
|
||||
self.node_inst.delete_node()
|
||||
self.node_inst._conn.delete.assert_called_once_with(
|
||||
self.node_inst.path)
|
||||
self.node_inst.path
|
||||
)
|
||||
|
||||
|
||||
class NodeCollectionTestCase(testtools.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(NodeCollectionTestCase, self).setUp()
|
||||
self.conn = mock.Mock()
|
||||
with open('rsd_lib/tests/unit/json_samples/v2_1/node_collection.json',
|
||||
'r') as f:
|
||||
with open(
|
||||
"rsd_lib/tests/unit/json_samples/v2_1/node_collection.json", "r"
|
||||
) as f:
|
||||
self.conn.get.return_value = request_fakes.fake_request_get(
|
||||
json.loads(f.read()))
|
||||
json.loads(f.read())
|
||||
)
|
||||
self.conn.post.return_value = request_fakes.fake_request_post(
|
||||
None, headers={"Location": "https://localhost:8443/"
|
||||
"redfish/v1/Nodes/1"})
|
||||
None,
|
||||
headers={
|
||||
"Location": "https://localhost:8443/" "redfish/v1/Nodes/1"
|
||||
},
|
||||
)
|
||||
self.node_col = node.NodeCollection(
|
||||
self.conn, '/redfish/v1/Nodes', redfish_version='1.0.2')
|
||||
self.conn, "/redfish/v1/Nodes", redfish_version="1.0.2"
|
||||
)
|
||||
|
||||
def test__parse_attributes(self):
|
||||
self.node_col._parse_attributes()
|
||||
self.assertEqual('1.0.2', self.node_col.redfish_version)
|
||||
self.assertEqual('Composed Nodes Collection', self.node_col.name)
|
||||
self.assertEqual(('/redfish/v1/Nodes/Node1',),
|
||||
self.node_col.members_identities)
|
||||
self.assertEqual("1.0.2", self.node_col.redfish_version)
|
||||
self.assertEqual("Composed Nodes Collection", self.node_col.name)
|
||||
self.assertEqual(
|
||||
("/redfish/v1/Nodes/Node1",), self.node_col.members_identities
|
||||
)
|
||||
|
||||
@mock.patch.object(node, 'Node', autospec=True)
|
||||
@mock.patch.object(node, "Node", autospec=True)
|
||||
def test_get_member(self, mock_node):
|
||||
self.node_col.get_member('/redfish/v1/Nodes/Node1')
|
||||
self.node_col.get_member("/redfish/v1/Nodes/Node1")
|
||||
mock_node.assert_called_once_with(
|
||||
self.node_col._conn, '/redfish/v1/Nodes/Node1',
|
||||
redfish_version=self.node_col.redfish_version)
|
||||
self.node_col._conn,
|
||||
"/redfish/v1/Nodes/Node1",
|
||||
redfish_version=self.node_col.redfish_version,
|
||||
)
|
||||
|
||||
@mock.patch.object(node, 'Node', autospec=True)
|
||||
@mock.patch.object(node, "Node", autospec=True)
|
||||
def test_get_members(self, mock_node):
|
||||
members = self.node_col.get_members()
|
||||
mock_node.assert_called_once_with(
|
||||
self.node_col._conn, '/redfish/v1/Nodes/Node1',
|
||||
redfish_version=self.node_col.redfish_version)
|
||||
self.node_col._conn,
|
||||
"/redfish/v1/Nodes/Node1",
|
||||
redfish_version=self.node_col.redfish_version,
|
||||
)
|
||||
self.assertIsInstance(members, list)
|
||||
self.assertEqual(1, len(members))
|
||||
|
||||
def test__get_compose_action_element(self):
|
||||
value = self.node_col._get_compose_action_element()
|
||||
self.assertEqual('/redfish/v1/Nodes/Actions/Allocate',
|
||||
value.target_uri)
|
||||
self.assertEqual(
|
||||
"/redfish/v1/Nodes/Actions/Allocate", value.target_uri
|
||||
)
|
||||
|
||||
def test_compose_node_no_reqs(self):
|
||||
result = self.node_col.compose_node()
|
||||
self.node_col._conn.post.assert_called_once_with(
|
||||
'/redfish/v1/Nodes/Actions/Allocate', data={})
|
||||
self.assertEqual(result, '/redfish/v1/Nodes/1')
|
||||
"/redfish/v1/Nodes/Actions/Allocate", data={}
|
||||
)
|
||||
self.assertEqual(result, "/redfish/v1/Nodes/1")
|
||||
|
||||
def test_compose_node_reqs(self):
|
||||
reqs = {
|
||||
'Name': 'test',
|
||||
'Description': 'this is a test node',
|
||||
'Processors': [{
|
||||
'TotalCores': 4,
|
||||
'Oem': {
|
||||
'Brand': 'E7',
|
||||
'Capabilities': ['sse']
|
||||
"Name": "test",
|
||||
"Description": "this is a test node",
|
||||
"Processors": [
|
||||
{
|
||||
"TotalCores": 4,
|
||||
"Oem": {"Brand": "E7", "Capabilities": ["sse"]},
|
||||
}
|
||||
}],
|
||||
'Memory': [{
|
||||
'CapacityMiB': 8000
|
||||
}],
|
||||
'TotalSystemCoreCount': 8,
|
||||
'TotalSystemMemoryMiB': 16000
|
||||
],
|
||||
"Memory": [{"CapacityMiB": 8000}],
|
||||
"TotalSystemCoreCount": 8,
|
||||
"TotalSystemMemoryMiB": 16000,
|
||||
}
|
||||
result = self.node_col.compose_node(
|
||||
name='test', description='this is a test node',
|
||||
processor_req=[{
|
||||
'TotalCores': 4,
|
||||
'Oem': {
|
||||
'Brand': 'E7',
|
||||
'Capabilities': ['sse']
|
||||
name="test",
|
||||
description="this is a test node",
|
||||
processor_req=[
|
||||
{
|
||||
"TotalCores": 4,
|
||||
"Oem": {"Brand": "E7", "Capabilities": ["sse"]},
|
||||
}
|
||||
}],
|
||||
memory_req=[{'CapacityMiB': 8000}],
|
||||
],
|
||||
memory_req=[{"CapacityMiB": 8000}],
|
||||
total_system_core_req=8,
|
||||
total_system_memory_req=16000)
|
||||
total_system_memory_req=16000,
|
||||
)
|
||||
self.node_col._conn.post.assert_called_once_with(
|
||||
'/redfish/v1/Nodes/Actions/Allocate', data=reqs)
|
||||
self.assertEqual(result, '/redfish/v1/Nodes/1')
|
||||
"/redfish/v1/Nodes/Actions/Allocate", data=reqs
|
||||
)
|
||||
self.assertEqual(result, "/redfish/v1/Nodes/1")
|
||||
|
||||
def test_compose_node_invalid_reqs(self):
|
||||
self.assertRaises(jsonschema.exceptions.ValidationError,
|
||||
self.node_col.compose_node,
|
||||
processor_req='invalid')
|
||||
self.assertRaises(
|
||||
jsonschema.exceptions.ValidationError,
|
||||
self.node_col.compose_node,
|
||||
processor_req="invalid",
|
||||
)
|
||||
|
||||
# Wrong processor Oem Brand
|
||||
with self.assertRaisesRegex(
|
||||
jsonschema.exceptions.ValidationError,
|
||||
("'Platinum' is not one of \['E3', 'E5'")):
|
||||
("'Platinum' is not one of \['E3', 'E5'"),
|
||||
):
|
||||
|
||||
self.node_col.compose_node(
|
||||
name='test', description='this is a test node',
|
||||
processor_req=[{
|
||||
'TotalCores': 4,
|
||||
'Oem': {
|
||||
'Brand': 'Platinum',
|
||||
'Capabilities': ['sse']
|
||||
name="test",
|
||||
description="this is a test node",
|
||||
processor_req=[
|
||||
{
|
||||
"TotalCores": 4,
|
||||
"Oem": {"Brand": "Platinum", "Capabilities": ["sse"]},
|
||||
}
|
||||
}])
|
||||
],
|
||||
)
|
||||
|
||||
# Wrong processor Oem Capabilities
|
||||
with self.assertRaisesRegex(
|
||||
jsonschema.exceptions.ValidationError,
|
||||
("'sse' is not of type 'array'")):
|
||||
("'sse' is not of type 'array'"),
|
||||
):
|
||||
|
||||
self.node_col.compose_node(
|
||||
name='test', description='this is a test node',
|
||||
processor_req=[{
|
||||
'TotalCores': 4,
|
||||
'Oem': {
|
||||
'Brand': 'E3',
|
||||
'Capabilities': 'sse'
|
||||
name="test",
|
||||
description="this is a test node",
|
||||
processor_req=[
|
||||
{
|
||||
"TotalCores": 4,
|
||||
"Oem": {"Brand": "E3", "Capabilities": "sse"},
|
||||
}
|
||||
}])
|
||||
],
|
||||
)
|
||||
|
||||
# Wrong processor Oem Capabilities
|
||||
with self.assertRaisesRegex(
|
||||
jsonschema.exceptions.ValidationError,
|
||||
("0 is not of type 'string'")):
|
||||
("0 is not of type 'string'"),
|
||||
):
|
||||
|
||||
self.node_col.compose_node(
|
||||
name='test', description='this is a test node',
|
||||
processor_req=[{
|
||||
'TotalCores': 4,
|
||||
'Oem': {
|
||||
'Brand': 'E3',
|
||||
'Capabilities': [0]
|
||||
name="test",
|
||||
description="this is a test node",
|
||||
processor_req=[
|
||||
{
|
||||
"TotalCores": 4,
|
||||
"Oem": {"Brand": "E3", "Capabilities": [0]},
|
||||
}
|
||||
}])
|
||||
],
|
||||
)
|
||||
|
Loading…
x
Reference in New Issue
Block a user