From fb699b6424614d703c7845d361f2f257f20aad61 Mon Sep 17 00:00:00 2001 From: Lin Yang Date: Tue, 8 May 2018 11:09:01 -0700 Subject: [PATCH] Add endpoint class in fabric resource in RSD 2.3 Change-Id: I1e9afa74edac6ff9baa56f58ab62f6969bca88f9 --- rsd_lib/resources/v2_3/fabric/endpoint.py | 129 ++++++++++++++ .../unit/json_samples/v2_3/endpoint_1.json | 65 +++++++ .../unit/json_samples/v2_3/endpoint_2.json | 61 +++++++ .../v2_3/endpoint_collection.json | 16 ++ .../resources/v2_3/fabric/test_endpoint.py | 165 ++++++++++++++++++ rsd_lib/utils.py | 5 +- 6 files changed, 440 insertions(+), 1 deletion(-) create mode 100644 rsd_lib/resources/v2_3/fabric/endpoint.py create mode 100644 rsd_lib/tests/unit/json_samples/v2_3/endpoint_1.json create mode 100644 rsd_lib/tests/unit/json_samples/v2_3/endpoint_2.json create mode 100644 rsd_lib/tests/unit/json_samples/v2_3/endpoint_collection.json create mode 100644 rsd_lib/tests/unit/resources/v2_3/fabric/test_endpoint.py diff --git a/rsd_lib/resources/v2_3/fabric/endpoint.py b/rsd_lib/resources/v2_3/fabric/endpoint.py new file mode 100644 index 0000000..0650d64 --- /dev/null +++ b/rsd_lib/resources/v2_3/fabric/endpoint.py @@ -0,0 +1,129 @@ +# 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 logging + +from sushy.resources import base +from sushy import utils + +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 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) + + +class StatusField(base.CompositeField): + state = base.Field('State') + health = base.Field('Health') + health_rollup = base.Field('HealthRollup') + + +class LinksField(base.CompositeField): + ports = base.Field('Ports', adapter=utils.get_members_identities) + endpoints = base.Field('Endpoints', adapter=utils.get_members_identities) + zones = base.Field(['Oem', 'Intel_RackScale', 'Zones'], + adapter=utils.get_members_identities) + interface = base.Field(['Oem', 'Intel_RackScale', 'Interface'], + adapter=rsd_lib_utils.get_resource_identity) + + +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=int) + + +class AuthenticationField(base.CompositeField): + username = base.Field('Username') + password = base.Field('Password') + + +class OemField(base.CompositeField): + authentication = AuthenticationField(['Intel_RackScale', 'Authentication']) + + +class Endpoint(base.ResourceBase): + + connected_entities = ConnectedEntitiesField('ConnectedEntities') + """Entities connected to endpoint""" + + description = base.Field('Description') + """The endpoint description""" + + protocol = base.Field('EndpointProtocol') + """Protocol for endpoint (i.e. PCIe)""" + + identifiers = IdentifiersField('Identifiers') + """Identifiers for endpoint""" + + identity = base.Field('Id', required=True) + """The endpoint identity string""" + + name = base.Field('Name') + """The endpoint name""" + + status = StatusField('Status') + """The endpoint status""" + + links = LinksField('Links') + """These links to related components of this endpoint""" + + ip_transport_details = IPTransportDetailsField('IPTransportDetails') + """IP transport details info 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) + + +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) diff --git a/rsd_lib/tests/unit/json_samples/v2_3/endpoint_1.json b/rsd_lib/tests/unit/json_samples/v2_3/endpoint_1.json new file mode 100644 index 0000000..97ebad6 --- /dev/null +++ b/rsd_lib/tests/unit/json_samples/v2_3/endpoint_1.json @@ -0,0 +1,65 @@ +{ + "@odata.context": "/redfish/v1/$metadata#Endpoint.Endpoint", + "@odata.id": "/redfish/v1/Fabrics/NVMeoE/Endpoints/1", + "@odata.type": "#Endpoint.v1_1_0.Endpoint", + "ConnectedEntities": [ + { + "EntityLink": { + "@odata.id": "/redfish/v1/StorageServices/1/Volumes/1" + }, + "EntityRole": "Target" + } + ], + "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": [], + "Endpoints": [], + "Oem": { + "Intel_RackScale": { + "@odata.type": "#Intel.Oem.EndpointLinks", + "Zones": [ + { + "@odata.id": "/redfish/v1/Fabrics/NVMeoE/Zones/1" + } + ], + "Interface": { + "@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 + } + } +} \ No newline at end of file diff --git a/rsd_lib/tests/unit/json_samples/v2_3/endpoint_2.json b/rsd_lib/tests/unit/json_samples/v2_3/endpoint_2.json new file mode 100644 index 0000000..ef01511 --- /dev/null +++ b/rsd_lib/tests/unit/json_samples/v2_3/endpoint_2.json @@ -0,0 +1,61 @@ +{ + "@odata.context": "/redfish/v1/$metadata#Endpoint.Endpoint", + "@odata.id": "/redfish/v1/Fabrics/NVMeoE/Endpoints/2", + "@odata.type": "#Endpoint.v1_1_0.Endpoint", + "Name": "Fabric Endpoint", + "Id": "1", + "Description": "Fabric Initiator Endpoint", + "ConnectedEntities": [ + { + "EntityLink": null, + "EntityRole": "Initiator" + } + ], + "EndpointProtocol": "NVMeOverFabrics", + "Identifiers": [ + { + "DurableName": "nqn.2014-08.org.nvmexpress:NVMf:uuid:12345678-90ab-cdef-0000-000000000000", + "DurableNameFormat": "NQN" + }, + { + "DurableName": "12345678-90ab-cdef-0000-000000000000", + "DurableNameFormat": "UUID" + } + ], + "Links": { + "Ports": [], + "Endpoints": [], + "Oem": { + "Intel_RackScale": { + "@odata.type": "#Intel.Oem.EndpointLinks", + "Zones": [ + { + "@odata.id": "/redfish/v1/Fabrics/NVMeoE/Zones/1" + } + ], + "Interface": null + } + } + }, + "IPTransportDetails": [ + { + "TransportProtocol": "RoCEv2", + "IPv4Address": { + "Address": "192.168.0.10" + }, + "IPv6Address": {}, + "Port": 4791 + } + ], + "Status": { + "Health": null, + "HealthRollup": null, + "State": null + }, + "Oem": { + "Intel_RackScale": { + "@odata.type": "#Intel.Oem.Endpoint", + "Authentication": null + } + } +} \ No newline at end of file diff --git a/rsd_lib/tests/unit/json_samples/v2_3/endpoint_collection.json b/rsd_lib/tests/unit/json_samples/v2_3/endpoint_collection.json new file mode 100644 index 0000000..1262d43 --- /dev/null +++ b/rsd_lib/tests/unit/json_samples/v2_3/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/resources/v2_3/fabric/test_endpoint.py b/rsd_lib/tests/unit/resources/v2_3/fabric/test_endpoint.py new file mode 100644 index 0000000..3df14cc --- /dev/null +++ b/rsd_lib/tests/unit/resources/v2_3/fabric/test_endpoint.py @@ -0,0 +1,165 @@ +# 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 json + +import mock +import testtools + +from rsd_lib.resources.v2_3.fabric import endpoint + + +class EndpointTestCase(testtools.TestCase): + + def setUp(self): + super(EndpointTestCase, self).setUp() + self.conn = mock.Mock() + with open('rsd_lib/tests/unit/json_samples/v2_3/endpoint_1.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/StorageServices/1/Volumes/1', + self.endpoint_inst.connected_entities[0].entity_link) + 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.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((), self.endpoint_inst.links.ports) + self.assertEqual((), self.endpoint_inst.links.endpoints) + self.assertEqual(('/redfish/v1/Fabrics/NVMeoE/Zones/1',), + self.endpoint_inst.links.zones) + self.assertEqual('/redfish/v1/Systems/Target/EthernetInterfaces/1', + self.endpoint_inst.links.interface) + 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.authentication) + + with open('rsd_lib/tests/unit/json_samples/v2_3/endpoint_2.json', + 'r') as f: + self.endpoint_inst._json = json.loads(f.read()) + + self.endpoint_inst._parse_attributes() + self.assertEqual('1.0.2', self.endpoint_inst.redfish_version) + self.assertEqual('Fabric Initiator Endpoint', + self.endpoint_inst.description) + self.assertEqual('1', self.endpoint_inst.identity) + self.assertEqual('Fabric Endpoint', self.endpoint_inst.name) + self.assertEqual('Initiator', + self.endpoint_inst.connected_entities[0].entity_role) + self.assertEqual(None, + self.endpoint_inst.connected_entities[0].entity_link) + self.assertEqual(None, self.endpoint_inst.status.state) + self.assertEqual(None, self.endpoint_inst.status.health) + self.assertEqual(None, self.endpoint_inst.status.health_rollup) + self.assertEqual('NVMeOverFabrics', self.endpoint_inst.protocol) + self.assertEqual('NQN', + self.endpoint_inst.identifiers[0].name_format) + self.assertEqual('nqn.2014-08.org.nvmexpress:NVMf:uuid:' + '12345678-90ab-cdef-0000-000000000000', + self.endpoint_inst.identifiers[0].name) + self.assertEqual('UUID', + self.endpoint_inst.identifiers[1].name_format) + self.assertEqual('12345678-90ab-cdef-0000-000000000000', + self.endpoint_inst.identifiers[1].name) + self.assertEqual((), self.endpoint_inst.links.ports) + self.assertEqual((), self.endpoint_inst.links.endpoints) + self.assertEqual(('/redfish/v1/Fabrics/NVMeoE/Zones/1',), + self.endpoint_inst.links.zones) + self.assertEqual(None, self.endpoint_inst.links.interface) + 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(4791, self.endpoint_inst.ip_transport_details[0].port) + self.assertEqual(None, self.endpoint_inst.oem.authentication) + + +class EndpointCollectionTestCase(testtools.TestCase): + + def setUp(self): + super(EndpointCollectionTestCase, self).setUp() + self.conn = mock.Mock() + 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()) + self.endpoint_col = endpoint.EndpointCollection( + self.conn, '/redfish/v1/Fabrics/PCIe/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)) diff --git a/rsd_lib/utils.py b/rsd_lib/utils.py index 1b88a94..525aa0f 100644 --- a/rsd_lib/utils.py +++ b/rsd_lib/utils.py @@ -15,4 +15,7 @@ def get_resource_identity(resource): - return resource.get('@odata.id') + if resource is None: + return None + else: + return resource.get('@odata.id')