diff --git a/rsd_lib/resources/v2_1/node/constants.py b/rsd_lib/resources/v2_1/node/constants.py deleted file mode 100644 index 42242c8..0000000 --- a/rsd_lib/resources/v2_1/node/constants.py +++ /dev/null @@ -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' diff --git a/rsd_lib/resources/v2_1/node/mappings.py b/rsd_lib/resources/v2_1/node/mappings.py deleted file mode 100644 index e0245ed..0000000 --- a/rsd_lib/resources/v2_1/node/mappings.py +++ /dev/null @@ -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)) diff --git a/rsd_lib/resources/v2_1/node/node.py b/rsd_lib/resources/v2_1/node/node.py index 668c614..811a1c0 100644 --- a/rsd_lib/resources/v2_1/node/node.py +++ b/rsd_lib/resources/v2_1/node/node.py @@ -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):] diff --git a/rsd_lib/resources/v2_1/node/schemas.py b/rsd_lib/resources/v2_1/node/schemas.py index cedc4d9..b990ba2 100644 --- a/rsd_lib/resources/v2_1/node/schemas.py +++ b/rsd_lib/resources/v2_1/node/schemas.py @@ -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"} diff --git a/rsd_lib/resources/v2_1/system/system.py b/rsd_lib/resources/v2_1/system/system.py index 2739343..46c46fd 100644 --- a/rsd_lib/resources/v2_1/system/system.py +++ b/rsd_lib/resources/v2_1/system/system.py @@ -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. """ diff --git a/rsd_lib/tests/unit/resources/v2_1/node/test_node.py b/rsd_lib/tests/unit/resources/v2_1/node/test_node.py index a487231..1bc9854 100644 --- a/rsd_lib/tests/unit/resources/v2_1/node/test_node.py +++ b/rsd_lib/tests/unit/resources/v2_1/node/test_node.py @@ -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]}, } - }]) + ], + )