From fc74ed6498b5ca34cbfd6a0c665448655b21b8ab Mon Sep 17 00:00:00 2001 From: Lin Yang Date: Mon, 18 Mar 2019 13:46:04 -0700 Subject: [PATCH] Add endpoint in RSD 2.4 Add FPGA-oF related support in endpoint resource. Change-Id: Iac1a221010b26b8f88ce005a86754480d0b7b024 --- rsd_lib/resources/v2_4/__init__.py | 22 + rsd_lib/resources/v2_4/fabric/__init__.py | 0 rsd_lib/resources/v2_4/fabric/endpoint.py | 242 +++++++++++ .../resources/v2_4/fabric/endpoint_schemas.py | 155 +++++++ rsd_lib/resources/v2_4/fabric/fabric.py | 58 +++ .../unit/json_samples/v2_4/endpoint.json | 83 ++++ .../v2_4/endpoint_collection.json | 16 + .../tests/unit/json_samples/v2_4/fabric.json | 25 ++ .../json_samples/v2_4/fabric_collection.json | 13 + .../json_samples/v2_4/zone_collection.json | 13 + .../unit/resources/v2_3/fabric/test_fabric.py | 8 +- .../unit/resources/v2_4/fabric/__init__.py | 0 .../resources/v2_4/fabric/test_endpoint.py | 405 ++++++++++++++++++ .../unit/resources/v2_4/fabric/test_fabric.py | 199 +++++++++ .../unit/resources/v2_4/test_rsdlib_v2_4.py | 6 +- 15 files changed, 1238 insertions(+), 7 deletions(-) create mode 100644 rsd_lib/resources/v2_4/fabric/__init__.py create mode 100644 rsd_lib/resources/v2_4/fabric/endpoint.py create mode 100644 rsd_lib/resources/v2_4/fabric/endpoint_schemas.py create mode 100644 rsd_lib/resources/v2_4/fabric/fabric.py create mode 100644 rsd_lib/tests/unit/json_samples/v2_4/endpoint.json create mode 100644 rsd_lib/tests/unit/json_samples/v2_4/endpoint_collection.json create mode 100644 rsd_lib/tests/unit/json_samples/v2_4/fabric.json create mode 100644 rsd_lib/tests/unit/json_samples/v2_4/fabric_collection.json create mode 100644 rsd_lib/tests/unit/json_samples/v2_4/zone_collection.json create mode 100644 rsd_lib/tests/unit/resources/v2_4/fabric/__init__.py create mode 100644 rsd_lib/tests/unit/resources/v2_4/fabric/test_endpoint.py create mode 100644 rsd_lib/tests/unit/resources/v2_4/fabric/test_fabric.py diff --git a/rsd_lib/resources/v2_4/__init__.py b/rsd_lib/resources/v2_4/__init__.py index 08295a6..e9ab475 100644 --- a/rsd_lib/resources/v2_4/__init__.py +++ b/rsd_lib/resources/v2_4/__init__.py @@ -16,6 +16,7 @@ from sushy.resources import base from rsd_lib.resources import v2_3 +from rsd_lib.resources.v2_4.fabric import fabric from rsd_lib.resources.v2_4.node import node from rsd_lib.resources.v2_4.storage_service import storage_service from rsd_lib.resources.v2_4.system import system @@ -85,3 +86,24 @@ class RSDLibV2_4(v2_3.RSDLibV2_3): """ return node.Node(self._conn, identity, redfish_version=self.redfish_version) + + def get_fabric_collection(self): + """Get the FabricCollection object + + :raises: MissingAttributeError, if the collection attribute is + not found + :returns: a FabricCollection object + """ + return fabric.FabricCollection(self._conn, + self._fabrics_path, + redfish_version=self.redfish_version) + + def get_fabric(self, identity): + """Given the identity return a Fabric object + + :param identity: The identity of the Fabric resource + :returns: The Fabric object + """ + return fabric.Fabric(self._conn, + identity, + redfish_version=self.redfish_version) diff --git a/rsd_lib/resources/v2_4/fabric/__init__.py b/rsd_lib/resources/v2_4/fabric/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/rsd_lib/resources/v2_4/fabric/endpoint.py b/rsd_lib/resources/v2_4/fabric/endpoint.py new file mode 100644 index 0000000..afeb85c --- /dev/null +++ b/rsd_lib/resources/v2_4/fabric/endpoint.py @@ -0,0 +1,242 @@ +# Copyright 2018 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. + +import jsonschema +import logging + +from sushy.resources import base +from sushy import utils + +from rsd_lib import common as rsd_lib_common +from rsd_lib.resources.v2_4.fabric import endpoint_schemas +from rsd_lib import utils as rsd_lib_utils + + +LOG = logging.getLogger(__name__) + + +class IdentifiersField(base.ListField): + name_format = base.Field('DurableNameFormat') + name = base.Field('DurableName') + + +class PciIdField(base.ListField): + device_id = base.Field('DeviceId') + vendor_id = base.Field('VendorId') + subsystem_id = base.Field('SubsystemId') + subsystem_vendor_id = base.Field('SubsystemVendorId') + + +class ConnectedEntitiesField(base.ListField): + entity_type = base.Field('EntityType') + entity_role = base.Field('EntityRole') + entity_link = base.Field('EntityLink', + adapter=rsd_lib_utils.get_resource_identity) + identifiers = IdentifiersField('Identifiers') + entity_pci_id = PciIdField('EntityPciId') + pci_function_number = base.Field( + 'PciFunctionNumber', adapter=rsd_lib_utils.num_or_none) + pci_class_code = base.Field('PciClassCode') + + +class LinksOemIntelRackScaleField(base.CompositeField): + zones = base.Field('Zones', adapter=utils.get_members_identities) + interfaces = base.Field('Interfaces', adapter=utils.get_members_identities) + + +class LinksOemField(base.CompositeField): + intel_rackscale = LinksOemIntelRackScaleField('Intel_RackScale') + + +class LinksField(base.CompositeField): + ports = base.Field('Ports', adapter=utils.get_members_identities) + endpoints = base.Field('Endpoints', adapter=utils.get_members_identities) + oem = LinksOemField('Oem') + + +class IPTransportDetailsField(base.ListField): + transport_protocol = base.Field('TransportProtocol') + ipv4_address = base.Field(['IPv4Address', 'Address']) + ipv6_address = base.Field(['IPv6Address', 'Address']) + port = base.Field('Port', adapter=rsd_lib_utils.num_or_none) + + +class AuthenticationField(base.CompositeField): + username = base.Field('Username') + password = base.Field('Password') + + +class OemIntelRackScaleField(base.CompositeField): + authentication = AuthenticationField('Authentication') + + endpoint_protocol = base.Field('EndpointProtocol') + """Protocol for endpoint (i.e. PCIe)""" + + pcie_function = base.Field( + 'PCIeFunction', adapter=rsd_lib_utils.get_resource_identity) + """A reference to the PCIe function that provides this processor + functionality + """ + + +class OemField(base.CompositeField): + intel_rackscale = OemIntelRackScaleField('Intel_RackScale') + + +class Endpoint(base.ResourceBase): + + identity = base.Field('Id', required=True) + """The endpoint identity string""" + + name = base.Field('Name') + """The endpoint name""" + + description = base.Field('Description') + """The endpoint description""" + + endpoint_protocol = base.Field('EndpointProtocol') + """Protocol for endpoint (i.e. PCIe)""" + + connected_entities = ConnectedEntitiesField('ConnectedEntities') + """Entities connected to endpoint""" + + identifiers = IdentifiersField('Identifiers') + """Identifiers for endpoint""" + + status = rsd_lib_common.StatusField('Status') + """The endpoint status""" + + pci_id = PciIdField('PciId') + """PCI ID of the endpoint""" + + host_reservation_memory_bytes = base.Field('HostReservationMemoryBytes') + """The amount of memory in bytes that the Host should allocate to connect + to this endpoint + """ + + ip_transport_details = IPTransportDetailsField('IPTransportDetails') + """IP transport details info of this endpoint""" + + links = LinksField('Links') + """These links to related components of this endpoint""" + + oem = OemField('Oem') + """The OEM additional info of this endpoint""" + + def __init__(self, connector, identity, redfish_version=None): + """A class representing an Endpoint + + :param connector: A Connector instance + :param identity: The identity of the RemoteTarget resource + :param redfish_version: The version of RedFish. Used to construct + the object according to schema of the given version. + """ + super(Endpoint, self).__init__(connector, identity, + redfish_version) + + def delete(self): + """Delete this endpoint""" + self._conn.delete(self.path) + + +class EndpointCollection(base.ResourceCollectionBase): + + @property + def _resource_type(self): + return Endpoint + + def __init__(self, connector, path, redfish_version=None): + """A class representing an Endpoint + + :param connector: A Connector instance + :param path: The canonical path to the Endpoint collection resource + :param redfish_version: The version of RedFish. Used to construct + the object according to schema of the given version. + """ + super(EndpointCollection, self).__init__(connector, path, + redfish_version) + + def _create_endpoint_request(self, connected_entities, identifiers=None, + protocol=None, pci_id=None, + host_reservation_memory_bytes=None, + ip_transport_details=None, links=None, + oem=None): + + request = {} + + jsonschema.validate(connected_entities, + endpoint_schemas.connected_entities_req_schema) + request['ConnectedEntities'] = connected_entities + + if identifiers is not None: + jsonschema.validate(identifiers, + endpoint_schemas.identifiers_req_schema) + request['Identifiers'] = identifiers + + if protocol is not None: + jsonschema.validate(protocol, endpoint_schemas.protocol_req_schema) + request['EndpointProtocol'] = protocol + + if pci_id is not None: + jsonschema.validate(pci_id, endpoint_schemas.pci_id_req_schema) + request['PciId'] = pci_id + + if host_reservation_memory_bytes is not None: + jsonschema.validate( + host_reservation_memory_bytes, + endpoint_schemas.host_reservation_memory_bytes_req_schema) + request['HostReservationMemoryBytes'] = \ + host_reservation_memory_bytes + + if ip_transport_details is not None: + jsonschema.validate( + ip_transport_details, + endpoint_schemas.ip_transport_details_req_schema) + request['IPTransportDetails'] = ip_transport_details + + if links is not None: + jsonschema.validate(links, endpoint_schemas.links_req_schema) + request['Links'] = links + + if oem is not None: + jsonschema.validate(oem, endpoint_schemas.oem_req_schema) + request['Oem'] = oem + + return request + + def create_endpoint(self, connected_entities, identifiers=None, + protocol=None, pci_id=None, + host_reservation_memory_bytes=None, + ip_transport_details=None, links=None, oem=None): + """Create a new endpoint + + :param connected_entities: provides information about entities + connected to the endpoint + :param identifiers: provides iQN or NQN of created entity + :param protocol: the protocol used by the endpoint + :param pci_id: the PCI ID of the endpoint + :param ip_transport_details: the transport used for accessing the + endpoint + :param oem: the oem fields of this endpoint creation request + :returns: The uri of the new endpoint + """ + properties = self._create_endpoint_request( + connected_entities, identifiers, protocol, pci_id, + host_reservation_memory_bytes, ip_transport_details, links, oem) + + resp = self._conn.post(self._path, data=properties) + LOG.info("Endpoint created at %s", resp.headers['Location']) + endpoint_url = resp.headers['Location'] + return endpoint_url[endpoint_url.find(self._path):] diff --git a/rsd_lib/resources/v2_4/fabric/endpoint_schemas.py b/rsd_lib/resources/v2_4/fabric/endpoint_schemas.py new file mode 100644 index 0000000..f5aa8b3 --- /dev/null +++ b/rsd_lib/resources/v2_4/fabric/endpoint_schemas.py @@ -0,0 +1,155 @@ +# Copyright (c) 2018 Intel, Corp. +# +# 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. + +identifiers_req_schema = { + 'type': 'array', + 'items': { + 'type': 'object', + 'properties': { + 'DurableNameFormat': {'type': 'string'}, + 'DurableName': {'type': 'string'} + }, + "required": ['DurableNameFormat', 'DurableName'] + } +} + +connected_entities_req_schema = { + 'type': 'array', + 'items': { + 'type': 'object', + 'properties': { + 'EntityLink': { + 'type': ['object', 'null'], + 'properties': { + '@odata.id': {'type': 'string'} + }, + "required": ['@odata.id'] + }, + 'EntityRole': { + 'type': 'string', + 'enum': ['Initiator', 'Target', 'Both'] + }, + 'EntityType': { + 'type': 'string', + 'enum': [ + 'StorageInitiator', 'RootComplex', 'NetworkController', + 'Drive', 'StorageExpander', 'DisplayController', 'Bridge', + 'Processor', 'Volume' + ] + }, + 'EntityPciId': { + 'type': 'object', + 'properties': { + 'DeviceId': {'type': 'string'}, + 'VendorId': {'type': 'string'}, + 'SubsystemId': {'type': 'string'}, + 'SubsystemVendorId': {'type': 'string'} + } + }, + 'PciFunctionNumber': {'type': 'number'}, + 'PciClassCode': {'type': 'string'}, + 'Identifiers': { + 'type': 'array', + 'items': { + 'type': 'object', + 'properties': { + 'DurableNameFormat': { + 'type': 'string', + 'enum': ['NQN', 'iQN', 'FC_WWN', 'UUID', 'EUI', + 'NAA', 'NSID', 'SystemPath', 'LUN'] + }, + 'DurableName': {'type': 'string'} + }, + "required": ['DurableNameFormat', 'DurableName'] + } + } + }, + "required": ['EntityRole'] + } +} + +protocol_req_schema = { + 'type': 'string' +} + +pci_id_req_schema = { + 'type': 'object', + 'properties': { + 'DeviceId': {'type': 'string'}, + 'VendorId': {'type': 'string'}, + 'SubsystemId': {'type': 'string'}, + 'SubsystemVendorId': {'type': 'string'} + } +} + +host_reservation_memory_bytes_req_schema = { + 'type': 'number' +} + +ip_transport_details_req_schema = { + 'type': 'array', + 'items': { + 'type': 'object', + 'properties': { + 'TransportProtocol': {'type': 'string'}, + 'IPv4Address': { + 'type': 'object', + 'properties': { + 'Address': {'type': 'string'} + } + }, + 'IPv6Address': { + 'type': 'object', + 'properties': { + 'Address': {'type': 'string'} + } + }, + 'Port': {'type': 'number'} + } + } +} + +links_req_schema = { + 'type': 'object', + 'properties': { + 'Ports': { + 'type': 'array', + 'items': { + 'type': 'object', + 'properties': { + '@odata.id': {'type': 'string'} + } + } + } + } +} + +oem_req_schema = { + 'type': 'object', + 'properties': { + 'Intel_RackScale': { + 'type': 'object', + 'properties': { + 'EndpointProtocol': {'type': 'string'}, + 'Authentication': { + 'type': 'object', + 'properties': { + 'Username': {'type': 'string'}, + 'Password': {'type': 'string'} + } + } + } + } + } +} diff --git a/rsd_lib/resources/v2_4/fabric/fabric.py b/rsd_lib/resources/v2_4/fabric/fabric.py new file mode 100644 index 0000000..6995b48 --- /dev/null +++ b/rsd_lib/resources/v2_4/fabric/fabric.py @@ -0,0 +1,58 @@ +# Copyright 2018 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.resources import base +from sushy import utils + +from rsd_lib.resources.v2_3.fabric import fabric +from rsd_lib.resources.v2_4.fabric import endpoint + + +class Fabric(fabric.Fabric): + + def _get_endpoint_collection_path(self): + """Helper function to find the EndpointCollection path""" + return utils.get_sub_resource_path_by(self, 'Endpoints') + + @property + @utils.cache_it + def endpoints(self): + """Property to provide reference to `EndpointCollection` instance + + It is calculated once when it is queried for the first time. On + refresh, this property is reset. + """ + return endpoint.EndpointCollection( + self._conn, self._get_endpoint_collection_path(), + redfish_version=self.redfish_version) + + +class FabricCollection(base.ResourceCollectionBase): + + @property + def _resource_type(self): + return Fabric + + def __init__(self, connector, path, redfish_version=None): + """A class representing a FabricCollection + + :param connector: A Connector instance + :param path: The canonical path to the Fabric collection + resource + :param redfish_version: The version of RedFish. Used to construct + the object according to schema of the given version. + """ + super(FabricCollection, self).__init__(connector, path, + redfish_version) diff --git a/rsd_lib/tests/unit/json_samples/v2_4/endpoint.json b/rsd_lib/tests/unit/json_samples/v2_4/endpoint.json new file mode 100644 index 0000000..2ad772c --- /dev/null +++ b/rsd_lib/tests/unit/json_samples/v2_4/endpoint.json @@ -0,0 +1,83 @@ +{ + "@odata.context": "/redfish/v1/$metadata#Endpoint.Endpoint", + "@odata.id": "/redfish/v1/Fabrics/NVMeoE/Endpoints/1", + "@odata.type": "#Endpoint.v1_1_0.Endpoint", + "ConnectedEntities": [ + { + "EntityType": "Drive", + "EntityRole": "Target", + "EntityLink": { + "@odata.id": "/redfish/v1/Chassis/PCIeSwitch1/Drives/Disk.Bay.0" + }, + "Identifiers": [ + { + "DurableNameFormat": "UUID", + "DurableName": "00000000-0000-0000-0000-000000000000" + } + ] + } + ], + "Description": "Fabric Endpoint", + "EndpointProtocol": "NVMeOverFabrics", + "Id": "1", + "Identifiers": [ + { + "DurableName": "nqn.2014-08.org.nvmexpress:NVMf:uuid:397f9b78-7e94-11e7-9ea4-001e67dfa170", + "DurableNameFormat": "NQN" + }, + { + "DurableName": "397f9b78-7e94-11e7-9ea4-001e67dfa170", + "DurableNameFormat": "UUID" + } + ], + "Links": { + "Ports": [ + { + "@odata.id": "/redfish/v1/Fabrics/PCIe/Switches/1/Ports/Down1" + } + ], + "Endpoints": [ + { + "@odata.id": "/redfish/v1/Fabrics/PCIe/Endpoints" + } + ], + "Oem": { + "Intel_RackScale": { + "@odata.type": "#Intel.Oem.EndpointLinks", + "Zones": [ + { + "@odata.id": "/redfish/v1/Fabrics/NVMeoE/Zones/1" + } + ], + "Interfaces": [ + { + "@odata.id": "/redfish/v1/Systems/Target/EthernetInterfaces/1" + } + ] + } + } + }, + "Name": "Fabric Endpoint", + "IPTransportDetails": [ + { + "TransportProtocol": "RoCEv2", + "IPv4Address": { + "Address": "192.168.0.10" + }, + "IPv6Address": {}, + "Port": 1023 + } + ], + "Status": { + "Health": "OK", + "HealthRollup": "OK", + "State": "Enabled" + }, + "Oem": { + "Intel_RackScale": { + "@odata.type": "#Intel.Oem.Endpoint", + "Authentication": null, + "EndpointProtocol": "FPGA-oF" + } + } +} \ No newline at end of file diff --git a/rsd_lib/tests/unit/json_samples/v2_4/endpoint_collection.json b/rsd_lib/tests/unit/json_samples/v2_4/endpoint_collection.json new file mode 100644 index 0000000..1262d43 --- /dev/null +++ b/rsd_lib/tests/unit/json_samples/v2_4/endpoint_collection.json @@ -0,0 +1,16 @@ +{ + "@odata.context": "/redfish/v1/$metadata#EndpointCollection.EndpointCollection", + "@odata.id": "/redfish/v1/Fabrics/NVMeoE/Endpoints", + "@odata.type": "#EndpointCollection.EndpointCollection", + "Members": [ + { + "@odata.id": "/redfish/v1/Fabrics/NVMeoE/Endpoints/1" + }, + { + "@odata.id": "/redfish/v1/Fabrics/NVMeoE/Endpoints/2" + } + ], + "Members@odata.count": 2, + "Name": "Endpoint Collection", + "Description": "Endpoint Collection" +} \ No newline at end of file diff --git a/rsd_lib/tests/unit/json_samples/v2_4/fabric.json b/rsd_lib/tests/unit/json_samples/v2_4/fabric.json new file mode 100644 index 0000000..a0f8f01 --- /dev/null +++ b/rsd_lib/tests/unit/json_samples/v2_4/fabric.json @@ -0,0 +1,25 @@ +{ + "@odata.context": "/redfish/v1/$metadata#Fabric.Fabric", + "@odata.id": "/redfish/v1/Fabrics/NVMeoE", + "@odata.type": "#Fabric.v1_0_0.Fabric", + "Id": "NVMeoE", + "Actions": { + "Oem": null + }, + "Zones": { + "@odata.id": "/redfish/v1/Fabrics/NVMeoE/Zones" + }, + "Endpoints": { + "@odata.id": "/redfish/v1/Fabrics/NVMeoE/Endpoints" + }, + "FabricType": "NVMeOverFabrics", + "Links": { + "Oem": {} + }, + "Oem": {}, + "Status": { + "Health": "OK", + "HealthRollup": "OK", + "State": "Enabled" + } +} \ No newline at end of file diff --git a/rsd_lib/tests/unit/json_samples/v2_4/fabric_collection.json b/rsd_lib/tests/unit/json_samples/v2_4/fabric_collection.json new file mode 100644 index 0000000..23db059 --- /dev/null +++ b/rsd_lib/tests/unit/json_samples/v2_4/fabric_collection.json @@ -0,0 +1,13 @@ +{ + "@odata.context": "/redfish/v1/$metadata#FabricCollection.FabricCollection", + "@odata.id": "/redfish/v1/Fabrics", + "@odata.type": "#FabricCollection.FabricCollection", + "Name": "Fabric Collection", + "Description": " Fabric Collection", + "Members@odata.count": 1, + "Members": [ + { + "@odata.id": "/redfish/v1/Fabrics/NVMeoE" + } + ] +} \ No newline at end of file diff --git a/rsd_lib/tests/unit/json_samples/v2_4/zone_collection.json b/rsd_lib/tests/unit/json_samples/v2_4/zone_collection.json new file mode 100644 index 0000000..46fb240 --- /dev/null +++ b/rsd_lib/tests/unit/json_samples/v2_4/zone_collection.json @@ -0,0 +1,13 @@ +{ + "@odata.context": "/redfish/v1/$metadata#ZoneCollection.ZoneCollection", + "@odata.id": "/redfish/v1/Fabrics/NVMeoE/Zones", + "@odata.type": "#ZoneCollection.ZoneCollection", + "Description": "Zone Collection", + "Members": [ + { + "@odata.id": "/redfish/v1/Fabrics/NVMeoE/Zones/1" + } + ], + "Members@odata.count": 1, + "Name": "Zone Collection" +} \ No newline at end of file diff --git a/rsd_lib/tests/unit/resources/v2_3/fabric/test_fabric.py b/rsd_lib/tests/unit/resources/v2_3/fabric/test_fabric.py index a6ac22a..07483a1 100644 --- a/rsd_lib/tests/unit/resources/v2_3/fabric/test_fabric.py +++ b/rsd_lib/tests/unit/resources/v2_3/fabric/test_fabric.py @@ -62,7 +62,7 @@ class FabricTestCase(testtools.TestCase): def test_endpoints(self): # | GIVEN | self.conn.get.return_value.json.reset_mock() - with open('rsd_lib/tests/unit/json_samples/v2_1/' + with open('rsd_lib/tests/unit/json_samples/v2_3/' 'endpoint_collection.json', 'r') as f: self.conn.get.return_value.json.return_value = json.loads(f.read()) # | WHEN | @@ -82,7 +82,7 @@ class FabricTestCase(testtools.TestCase): def test_endpoints_on_refresh(self): # | GIVEN | - with open('rsd_lib/tests/unit/json_samples/v2_1/' + with open('rsd_lib/tests/unit/json_samples/v2_3/' 'endpoint_collection.json', 'r') as f: self.conn.get.return_value.json.return_value = json.loads(f.read()) # | WHEN & THEN | @@ -90,7 +90,7 @@ class FabricTestCase(testtools.TestCase): endpoint.EndpointCollection) # On refreshing the fabric instance... - with open('rsd_lib/tests/unit/json_samples/v2_1/' + with open('rsd_lib/tests/unit/json_samples/v2_3/' 'fabric.json', 'r') as f: self.conn.get.return_value.json.return_value = json.loads(f.read()) @@ -98,7 +98,7 @@ class FabricTestCase(testtools.TestCase): self.fabric_inst.refresh(force=False) # | GIVEN | - with open('rsd_lib/tests/unit/json_samples/v2_1/' + with open('rsd_lib/tests/unit/json_samples/v2_3/' 'endpoint_collection.json', 'r') as f: self.conn.get.return_value.json.return_value = json.loads(f.read()) # | WHEN & THEN | diff --git a/rsd_lib/tests/unit/resources/v2_4/fabric/__init__.py b/rsd_lib/tests/unit/resources/v2_4/fabric/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/rsd_lib/tests/unit/resources/v2_4/fabric/test_endpoint.py b/rsd_lib/tests/unit/resources/v2_4/fabric/test_endpoint.py new file mode 100644 index 0000000..cd567f8 --- /dev/null +++ b/rsd_lib/tests/unit/resources/v2_4/fabric/test_endpoint.py @@ -0,0 +1,405 @@ +# Copyright 2018 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. + +import copy +import json +import jsonschema +import mock +import testtools + +from rsd_lib.resources.v2_4.fabric import endpoint +from rsd_lib.tests.unit.fakes import request_fakes + + +class EndpointTestCase(testtools.TestCase): + + def setUp(self): + super(EndpointTestCase, self).setUp() + self.conn = mock.Mock() + with open('rsd_lib/tests/unit/json_samples/v2_4/endpoint.json', + 'r') as f: + self.conn.get.return_value.json.return_value = json.loads(f.read()) + + self.endpoint_inst = endpoint.Endpoint( + self.conn, '/redfish/v1/Fabrics/NVMeoE/Endpoints/1', + redfish_version='1.0.2') + + def test__parse_attributes(self): + self.endpoint_inst._parse_attributes() + self.assertEqual('1.0.2', self.endpoint_inst.redfish_version) + self.assertEqual('Fabric Endpoint', + self.endpoint_inst.description) + self.assertEqual('1', self.endpoint_inst.identity) + self.assertEqual('Fabric Endpoint', self.endpoint_inst.name) + self.assertEqual('Target', + self.endpoint_inst.connected_entities[0].entity_role) + self.assertEqual('/redfish/v1/Chassis/PCIeSwitch1/Drives/Disk.Bay.0', + self.endpoint_inst.connected_entities[0].entity_link) + self.assertEqual( + 'Drive', self.endpoint_inst.connected_entities[0].entity_type) + self.assertEqual( + 'UUID', + self.endpoint_inst.connected_entities[0].identifiers[0]. + name_format) + self.assertEqual( + '00000000-0000-0000-0000-000000000000', + self.endpoint_inst.connected_entities[0].identifiers[0].name) + self.assertEqual('Enabled', self.endpoint_inst.status.state) + self.assertEqual('OK', self.endpoint_inst.status.health) + self.assertEqual('OK', self.endpoint_inst.status.health_rollup) + self.assertEqual( + 'NVMeOverFabrics', self.endpoint_inst.endpoint_protocol) + self.assertEqual('NQN', + self.endpoint_inst.identifiers[0].name_format) + self.assertEqual('nqn.2014-08.org.nvmexpress:NVMf:uuid:' + '397f9b78-7e94-11e7-9ea4-001e67dfa170', + self.endpoint_inst.identifiers[0].name) + self.assertEqual('UUID', + self.endpoint_inst.identifiers[1].name_format) + self.assertEqual('397f9b78-7e94-11e7-9ea4-001e67dfa170', + self.endpoint_inst.identifiers[1].name) + + self.assertEqual( + ('/redfish/v1/Fabrics/PCIe/Switches/1/Ports/Down1',), + self.endpoint_inst.links.ports) + self.assertEqual( + ('/redfish/v1/Fabrics/PCIe/Endpoints',), + self.endpoint_inst.links.endpoints) + self.assertEqual(('/redfish/v1/Fabrics/NVMeoE/Zones/1',), + self.endpoint_inst.links.oem.intel_rackscale.zones) + self.assertEqual( + ('/redfish/v1/Systems/Target/EthernetInterfaces/1',), + self.endpoint_inst.links.oem.intel_rackscale.interfaces) + self.assertEqual( + 'RoCEv2', + self.endpoint_inst.ip_transport_details[0].transport_protocol) + self.assertEqual( + '192.168.0.10', + self.endpoint_inst.ip_transport_details[0].ipv4_address) + self.assertEqual( + None, self.endpoint_inst.ip_transport_details[0].ipv6_address) + self.assertEqual(1023, self.endpoint_inst.ip_transport_details[0].port) + self.assertEqual( + None, self.endpoint_inst.oem.intel_rackscale.authentication) + self.assertEqual( + 'FPGA-oF', + self.endpoint_inst.oem.intel_rackscale.endpoint_protocol) + + +class EndpointCollectionTestCase(testtools.TestCase): + + def setUp(self): + super(EndpointCollectionTestCase, self).setUp() + self.conn = mock.Mock() + with open('rsd_lib/tests/unit/json_samples/v2_4/' + 'endpoint_collection.json', 'r') as f: + self.conn.get.return_value.json.return_value = json.loads(f.read()) + + self.conn.post.return_value = request_fakes.fake_request_post( + None, headers={"Location": "https://localhost:8443/redfish/v1/" + "Fabrics/NVMeoE/Endpoints/3"}) + + self.endpoint_col = endpoint.EndpointCollection( + self.conn, '/redfish/v1/Fabrics/NVMeoE/Endpoints', + redfish_version='1.0.2') + + def test__parse_attributes(self): + self.endpoint_col._parse_attributes() + self.assertEqual('1.0.2', self.endpoint_col.redfish_version) + self.assertEqual('Endpoint Collection', + self.endpoint_col.name) + self.assertEqual(('/redfish/v1/Fabrics/NVMeoE/Endpoints/1', + '/redfish/v1/Fabrics/NVMeoE/Endpoints/2'), + self.endpoint_col.members_identities) + + @mock.patch.object(endpoint, 'Endpoint', autospec=True) + def test_get_member(self, mock_endpoint): + self.endpoint_col.get_member( + '/redfish/v1/Fabrics/NVMeoE/Endpoints/1') + mock_endpoint.assert_called_once_with( + self.endpoint_col._conn, + '/redfish/v1/Fabrics/NVMeoE/Endpoints/1', + redfish_version=self.endpoint_col.redfish_version) + + @mock.patch.object(endpoint, 'Endpoint', autospec=True) + def test_get_members(self, mock_endpoint): + members = self.endpoint_col.get_members() + calls = [ + mock.call(self.endpoint_col._conn, + '/redfish/v1/Fabrics/NVMeoE/Endpoints/1', + redfish_version=self.endpoint_col.redfish_version), + mock.call(self.endpoint_col._conn, + '/redfish/v1/Fabrics/NVMeoE/Endpoints/2', + redfish_version=self.endpoint_col.redfish_version) + ] + mock_endpoint.assert_has_calls(calls) + self.assertIsInstance(members, list) + self.assertEqual(2, len(members)) + + def test_create_endpoint(self): + reqs = { + "EndpointProtocol": "PCIe", + "ConnectedEntities": [ + { + "EntityRole": "Initiator", + "EntityLink": None + } + ], + "Links": { + "Ports": [ + { + "@odata.id": "/redfish/v1/Fabrics/PCIe/Switches/1/" + "Ports/Up1" + } + ] + } + } + result = self.endpoint_col.create_endpoint( + links={ + "Ports": [ + { + "@odata.id": "/redfish/v1/Fabrics/PCIe/Switches/1/" + "Ports/Up1" + } + ] + }, + connected_entities=[ + { + "EntityRole": "Initiator", + "EntityLink": None + } + ], + protocol="PCIe") + self.endpoint_col._conn.post.assert_called_once_with( + '/redfish/v1/Fabrics/NVMeoE/Endpoints', data=reqs) + self.assertEqual(result, + '/redfish/v1/Fabrics/NVMeoE/Endpoints/3') + + self.endpoint_col._conn.post.reset_mock() + reqs = { + "EndpointProtocol": "PCIe", + "ConnectedEntities": [ + { + "EntityRole": "Target", + "EntityLink": { + "@odata.id": "/redfish/v1/Systems/System1/Processors/" + "FPGA1" + } + } + ] + } + result = self.endpoint_col.create_endpoint( + connected_entities=[ + { + "EntityRole": "Target", + "EntityLink": { + "@odata.id": "/redfish/v1/Systems/System1/Processors/" + "FPGA1" + } + } + ], + protocol="PCIe") + self.endpoint_col._conn.post.assert_called_once_with( + '/redfish/v1/Fabrics/NVMeoE/Endpoints', data=reqs) + self.assertEqual(result, + '/redfish/v1/Fabrics/NVMeoE/Endpoints/3') + + self.endpoint_col._conn.post.reset_mock() + reqs = { + "EndpointProtocol": "OEM", + "ConnectedEntities": [ + { + "EntityRole": "Initiator", + "EntityLink": None + } + ], + "Identifiers": [ + { + "DurableName": "12345678-90ab-cdef-0000-000000000000", + "DurableNameFormat": "UUID" + } + ], + "Oem": { + "Intel_RackScale": { + "EndpointProtocol": "FPGA-oF" + } + } + } + result = self.endpoint_col.create_endpoint( + connected_entities=[ + { + "EntityRole": "Initiator", + "EntityLink": None + } + ], + protocol="OEM", + identifiers=[ + { + "DurableName": "12345678-90ab-cdef-0000-000000000000", + "DurableNameFormat": "UUID" + } + ], + oem={ + "Intel_RackScale": { + "EndpointProtocol": "FPGA-oF" + } + }) + self.endpoint_col._conn.post.assert_called_once_with( + '/redfish/v1/Fabrics/NVMeoE/Endpoints', data=reqs) + self.assertEqual(result, + '/redfish/v1/Fabrics/NVMeoE/Endpoints/3') + + self.endpoint_col._conn.post.reset_mock() + reqs = { + "ConnectedEntities": [ + { + "EntityRole": "Target", + "EntityLink": { + "@odata.id": "/redfish/v1/Systems/System1/Processors/" + "FPGA1" + } + } + ], + "Identifiers": [ + { + "DurableName": "123e4567-e89b-12d3-a456-426655440000", + "DurableNameFormat": "UUID" + } + ], + "IPTransportDetails": [ + { + "TransportProtocol": "RoCEv2", + "IPv4Address": { + "Address": "192.168.0.10" + }, + "IPv6Address": {}, + "Port": 4424 + } + ], + "EndpointProtocol": "OEM", + "Oem": { + "Intel_RackScale": { + "EndpointProtocol": "FPGA-oF" + } + } + } + result = self.endpoint_col.create_endpoint( + connected_entities=[ + { + "EntityRole": "Target", + "EntityLink": { + "@odata.id": "/redfish/v1/Systems/System1/Processors/" + "FPGA1" + } + } + ], + identifiers=[ + { + "DurableName": "123e4567-e89b-12d3-a456-426655440000", + "DurableNameFormat": "UUID" + } + ], + ip_transport_details=[ + { + "TransportProtocol": "RoCEv2", + "IPv4Address": { + "Address": "192.168.0.10" + }, + "IPv6Address": {}, + "Port": 4424 + } + ], + protocol="OEM", + oem={ + "Intel_RackScale": { + "EndpointProtocol": "FPGA-oF" + } + }) + self.endpoint_col._conn.post.assert_called_once_with( + '/redfish/v1/Fabrics/NVMeoE/Endpoints', data=reqs) + self.assertEqual(result, + '/redfish/v1/Fabrics/NVMeoE/Endpoints/3') + + def test_create_endpoint_with_invalid_reqs(self): + identifiers = [ + { + "DurableNameFormat": "iQN", + "DurableName": "iqn.1986-03.com.intel:my_storage-uuid:" + "397f9b78-7e94-11e7-9ea4-001e67dfa170" + } + ] + connected_entities = [ + { + "EntityLink": { + "@odata.id": "/redfish/v1/StorageServices/1/Volumes/1" + }, + "EntityRole": "Target", + "Identifiers": [ + { + "DurableNameFormat": "LUN", + "DurableName": "1" + } + ] + } + ] + + result = self.endpoint_col.create_endpoint( + identifiers=identifiers, connected_entities=connected_entities) + self.assertEqual(result, + '/redfish/v1/Fabrics/NVMeoE/Endpoints/3') + + # Test invalid identifiers argument + invalid_identifiers = copy.deepcopy(identifiers) + invalid_identifiers[0].pop('DurableNameFormat') + self.assertRaises(jsonschema.exceptions.ValidationError, + self.endpoint_col.create_endpoint, + identifiers=invalid_identifiers, + connected_entities=connected_entities) + + invalid_identifiers = copy.deepcopy(identifiers) + invalid_identifiers[0].pop('DurableName') + self.assertRaises(jsonschema.exceptions.ValidationError, + self.endpoint_col.create_endpoint, + identifiers=invalid_identifiers, + connected_entities=connected_entities) + + # Test invalid connected_entities argument + invalid_connected_entities = copy.deepcopy(connected_entities) + invalid_connected_entities[0]['EntityRole'] = 'fake-format' + self.assertRaises(jsonschema.exceptions.ValidationError, + self.endpoint_col.create_endpoint, + identifiers=identifiers, + connected_entities=invalid_connected_entities) + + invalid_connected_entities = copy.deepcopy(connected_entities) + invalid_connected_entities[0]['EntityLink'].pop('@odata.id') + self.assertRaises(jsonschema.exceptions.ValidationError, + self.endpoint_col.create_endpoint, + identifiers=identifiers, + connected_entities=invalid_connected_entities) + + invalid_connected_entities = copy.deepcopy(connected_entities) + invalid_connected_entities[0].pop('EntityRole') + self.assertRaises(jsonschema.exceptions.ValidationError, + self.endpoint_col.create_endpoint, + identifiers=identifiers, + connected_entities=invalid_connected_entities) + + # Test invalid protocol argument + self.assertRaises(jsonschema.exceptions.ValidationError, + self.endpoint_col.create_endpoint, + identifiers=identifiers, + connected_entities=connected_entities, + protocol=1) diff --git a/rsd_lib/tests/unit/resources/v2_4/fabric/test_fabric.py b/rsd_lib/tests/unit/resources/v2_4/fabric/test_fabric.py new file mode 100644 index 0000000..1e1d2f8 --- /dev/null +++ b/rsd_lib/tests/unit/resources/v2_4/fabric/test_fabric.py @@ -0,0 +1,199 @@ +# 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. + +import json +import mock +import testtools + +from sushy import exceptions + +from rsd_lib.resources.v2_3.fabric import zone +from rsd_lib.resources.v2_4.fabric import endpoint +from rsd_lib.resources.v2_4.fabric import fabric + + +class FabricTestCase(testtools.TestCase): + + def setUp(self): + super(FabricTestCase, self).setUp() + self.conn = mock.Mock() + with open('rsd_lib/tests/unit/json_samples/v2_4/fabric.json', + 'r') as f: + self.conn.get.return_value.json.return_value = json.loads(f.read()) + + self.fabric_inst = fabric.Fabric( + self.conn, '/redfish/v1/Fabrics/NVMeoE', + redfish_version='1.0.2') + + def test__parse_attributes(self): + self.fabric_inst._parse_attributes() + self.assertEqual('1.0.2', self.fabric_inst.redfish_version) + self.assertEqual('NVMeoE', self.fabric_inst.identity) + self.assertEqual(None, self.fabric_inst.name) + self.assertEqual('NVMeOverFabrics', self.fabric_inst.fabric_type) + self.assertEqual(None, self.fabric_inst.max_zones) + self.assertEqual('Enabled', self.fabric_inst.status.state) + self.assertEqual('OK', self.fabric_inst.status.health) + self.assertEqual('OK', self.fabric_inst.status.health_rollup) + + def test__get_endpoint_collection_path(self): + expected = '/redfish/v1/Fabrics/NVMeoE/Endpoints' + result = self.fabric_inst._get_endpoint_collection_path() + self.assertEqual(expected, result) + + def test__get_endpoint_collection_path_missing_attr(self): + self.fabric_inst._json.pop('Endpoints') + self.assertRaisesRegex( + exceptions.MissingAttributeError, 'attribute Endpoints', + self.fabric_inst._get_endpoint_collection_path) + + def test_endpoints(self): + # | GIVEN | + self.conn.get.return_value.json.reset_mock() + with open('rsd_lib/tests/unit/json_samples/v2_4/' + 'endpoint_collection.json', 'r') as f: + self.conn.get.return_value.json.return_value = json.loads(f.read()) + # | WHEN | + actual_endpoints = self.fabric_inst.endpoints + # | THEN | + self.assertIsInstance(actual_endpoints, + endpoint.EndpointCollection) + 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_endpoints, + self.fabric_inst.endpoints) + self.conn.get.return_value.json.assert_not_called() + + def test_endpoints_on_refresh(self): + # | GIVEN | + with open('rsd_lib/tests/unit/json_samples/v2_4/' + 'endpoint_collection.json', 'r') as f: + self.conn.get.return_value.json.return_value = json.loads(f.read()) + # | WHEN & THEN | + self.assertIsInstance(self.fabric_inst.endpoints, + endpoint.EndpointCollection) + + # On refreshing the fabric instance... + with open('rsd_lib/tests/unit/json_samples/v2_4/' + 'fabric.json', 'r') as f: + self.conn.get.return_value.json.return_value = json.loads(f.read()) + + self.fabric_inst.invalidate() + self.fabric_inst.refresh(force=False) + + # | GIVEN | + with open('rsd_lib/tests/unit/json_samples/v2_4/' + 'endpoint_collection.json', 'r') as f: + self.conn.get.return_value.json.return_value = json.loads(f.read()) + # | WHEN & THEN | + self.assertIsInstance(self.fabric_inst.endpoints, + endpoint.EndpointCollection) + + def test__get_zone_collection_path(self): + expected = '/redfish/v1/Fabrics/NVMeoE/Zones' + result = self.fabric_inst._get_zone_collection_path() + self.assertEqual(expected, result) + + def test__get_zone_collection_path_missing_attr(self): + self.fabric_inst._json.pop('Zones') + self.assertRaisesRegex( + exceptions.MissingAttributeError, 'attribute Zones', + self.fabric_inst._get_zone_collection_path) + + def test_zones(self): + # | GIVEN | + self.conn.get.return_value.json.reset_mock() + with open('rsd_lib/tests/unit/json_samples/v2_4/' + 'zone_collection.json', 'r') as f: + self.conn.get.return_value.json.return_value = json.loads(f.read()) + # | WHEN | + actual_zones = self.fabric_inst.zones + # | THEN | + self.assertIsInstance(actual_zones, + zone.ZoneCollection) + 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_zones, + self.fabric_inst.zones) + self.conn.get.return_value.json.assert_not_called() + + def test_zones_on_refresh(self): + # | GIVEN | + with open('rsd_lib/tests/unit/json_samples/v2_4/' + 'zone_collection.json', 'r') as f: + self.conn.get.return_value.json.return_value = json.loads(f.read()) + # | WHEN & THEN | + self.assertIsInstance(self.fabric_inst.zones, + zone.ZoneCollection) + + # On refreshing the fabric instance... + with open('rsd_lib/tests/unit/json_samples/v2_4/' + 'fabric.json', 'r') as f: + self.conn.get.return_value.json.return_value = json.loads(f.read()) + + self.fabric_inst.invalidate() + self.fabric_inst.refresh(force=False) + + # | GIVEN | + with open('rsd_lib/tests/unit/json_samples/v2_4/' + 'zone_collection.json', 'r') as f: + self.conn.get.return_value.json.return_value = json.loads(f.read()) + # | WHEN & THEN | + self.assertIsInstance(self.fabric_inst.zones, + zone.ZoneCollection) + + +class FabricCollectionTestCase(testtools.TestCase): + + def setUp(self): + super(FabricCollectionTestCase, self).setUp() + self.conn = mock.Mock() + with open('rsd_lib/tests/unit/json_samples/v2_4/' + 'fabric_collection.json', 'r') as f: + self.conn.get.return_value.json.return_value = json.loads(f.read()) + self.fabric_col = fabric.FabricCollection( + self.conn, '/redfish/v1/Fabrics', redfish_version='1.0.2') + + def test__parse_attributes(self): + self.fabric_col._parse_attributes() + self.assertEqual('1.0.2', self.fabric_col.redfish_version) + self.assertEqual('Fabric Collection', + self.fabric_col.name) + self.assertEqual(('/redfish/v1/Fabrics/NVMeoE',), + self.fabric_col.members_identities) + + @mock.patch.object(fabric, 'Fabric', autospec=True) + def test_get_member(self, mock_fabric): + self.fabric_col.get_member('/redfish/v1/Fabrics/NVMeoE') + mock_fabric.assert_called_once_with( + self.fabric_col._conn, '/redfish/v1/Fabrics/NVMeoE', + redfish_version=self.fabric_col.redfish_version) + + @mock.patch.object(fabric, 'Fabric', autospec=True) + def test_get_members(self, mock_fabric): + members = self.fabric_col.get_members() + mock_fabric.assert_called_once_with( + self.fabric_col._conn, '/redfish/v1/Fabrics/NVMeoE', + redfish_version=self.fabric_col.redfish_version) + self.assertIsInstance(members, list) + self.assertEqual(1, len(members)) diff --git a/rsd_lib/tests/unit/resources/v2_4/test_rsdlib_v2_4.py b/rsd_lib/tests/unit/resources/v2_4/test_rsdlib_v2_4.py index 1825473..e4022ff 100644 --- a/rsd_lib/tests/unit/resources/v2_4/test_rsdlib_v2_4.py +++ b/rsd_lib/tests/unit/resources/v2_4/test_rsdlib_v2_4.py @@ -26,9 +26,9 @@ from rsd_lib.resources.v2_2.update_service import update_service \ as v2_2_update_service from rsd_lib.resources.v2_3.ethernet_switch import ethernet_switch \ as v2_3_ethernet_switch -from rsd_lib.resources.v2_3.fabric import fabric as v2_3_fabric from rsd_lib.resources.v2_3.manager import manager as v2_3_manager from rsd_lib.resources import v2_4 +from rsd_lib.resources.v2_4.fabric import fabric as v2_4_fabric from rsd_lib.resources.v2_4.node import node as v2_4_node from rsd_lib.resources.v2_4.storage_service import storage_service \ as v2_4_storage_service @@ -96,14 +96,14 @@ class RSDLibV2_3TestCase(testtools.TestCase): self.rsd._conn, 'fake-node-id', redfish_version=self.rsd.redfish_version) - @mock.patch.object(v2_3_fabric, 'FabricCollection', autospec=True) + @mock.patch.object(v2_4_fabric, 'FabricCollection', autospec=True) def test_get_fabric_collection(self, mock_fabric_collection): self.rsd.get_fabric_collection() mock_fabric_collection.assert_called_once_with( self.rsd._conn, '/redfish/v1/Fabrics', redfish_version=self.rsd.redfish_version) - @mock.patch.object(v2_3_fabric, 'Fabric', autospec=True) + @mock.patch.object(v2_4_fabric, 'Fabric', autospec=True) def test_get_fabric(self, mock_fabric): self.rsd.get_fabric('fake-fabric-id') mock_fabric.assert_called_once_with(