From 680d023ab6d9ff514f79cf286b282c2ae774878c Mon Sep 17 00:00:00 2001 From: Varsha Date: Tue, 12 Mar 2019 18:50:54 +0530 Subject: [PATCH] Add `FabricCollection` and `Fabric` classes Add representation of Fabric and FabricCollection resources. The Fabric represents a simple fabric consisting of one or more switches, zero or more endpoints, and zero or more zones. Also adds the methods get_fabric_collection and get_fabric in the public API. Implements: FabricCollection and Fabric classes Story: #2003853 Task: #26648 Change-Id: Ib9ad7f562cfd46efac4038e17d524efcdc9fff92 --- .../add-fabric-support-1520f7fcb0e12539.yaml | 5 ++ sushy/__init__.py | 1 + sushy/main.py | 27 +++++++ sushy/resources/fabric/__init__.py | 0 sushy/resources/fabric/constants.py | 42 ++++++++++ sushy/resources/fabric/fabric.py | 77 ++++++++++++++++++ sushy/resources/fabric/mappings.py | 47 +++++++++++ sushy/tests/unit/json_samples/fabric.json | 29 +++++++ .../unit/json_samples/fabric_collection.json | 16 ++++ sushy/tests/unit/json_samples/root.json | 3 + sushy/tests/unit/resources/fabric/__init__.py | 0 .../unit/resources/fabric/test_fabric.py | 79 +++++++++++++++++++ sushy/tests/unit/test_main.py | 21 +++++ 13 files changed, 347 insertions(+) create mode 100644 releasenotes/notes/add-fabric-support-1520f7fcb0e12539.yaml create mode 100644 sushy/resources/fabric/__init__.py create mode 100644 sushy/resources/fabric/constants.py create mode 100644 sushy/resources/fabric/fabric.py create mode 100644 sushy/resources/fabric/mappings.py create mode 100644 sushy/tests/unit/json_samples/fabric.json create mode 100644 sushy/tests/unit/json_samples/fabric_collection.json create mode 100644 sushy/tests/unit/resources/fabric/__init__.py create mode 100644 sushy/tests/unit/resources/fabric/test_fabric.py diff --git a/releasenotes/notes/add-fabric-support-1520f7fcb0e12539.yaml b/releasenotes/notes/add-fabric-support-1520f7fcb0e12539.yaml new file mode 100644 index 00000000..f9f12982 --- /dev/null +++ b/releasenotes/notes/add-fabric-support-1520f7fcb0e12539.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + Adds support for the Fabric resource to the library. + diff --git a/sushy/__init__.py b/sushy/__init__.py index 92623cfe..8b49ac2e 100644 --- a/sushy/__init__.py +++ b/sushy/__init__.py @@ -21,6 +21,7 @@ from sushy.resources.constants import * # noqa from sushy.resources.system.constants import * # noqa from sushy.resources.manager.constants import * # noqa from sushy.resources.chassis.constants import * # noqa +from sushy.resources.fabric.constants import * # noqa __all__ = ('Sushy',) __version__ = pbr.version.VersionInfo( diff --git a/sushy/main.py b/sushy/main.py index b1c59633..437dbdbd 100644 --- a/sushy/main.py +++ b/sushy/main.py @@ -20,6 +20,7 @@ from sushy import exceptions from sushy.resources import base from sushy.resources.chassis import chassis from sushy.resources.compositionservice import compositionservice +from sushy.resources.fabric import fabric from sushy.resources.manager import manager from sushy.resources.registry import message_registry_file from sushy.resources.sessionservice import session @@ -79,6 +80,9 @@ class Sushy(base.ResourceBase): _chassis_path = base.Field(['Chassis', '@odata.id']) """ChassisCollection path""" + _fabrics_path = base.Field(['Fabrics', '@odata.id']) + """FabricCollection path""" + _session_service_path = base.Field(['SessionService', '@odata.id']) """SessionService path""" @@ -188,6 +192,29 @@ class Sushy(base.ResourceBase): return chassis.Chassis(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 + """ + if not self._fabrics_path: + raise exceptions.MissingAttributeError( + attribute='Fabrics/@odata.id', resource=self._path) + + 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) + def get_manager_collection(self): """Get the ManagerCollection object diff --git a/sushy/resources/fabric/__init__.py b/sushy/resources/fabric/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/sushy/resources/fabric/constants.py b/sushy/resources/fabric/constants.py new file mode 100644 index 00000000..27f094fa --- /dev/null +++ b/sushy/resources/fabric/constants.py @@ -0,0 +1,42 @@ +# 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 come from the Redfish Fabric json-schema 1.0.4: +# http://redfish.dmtf.org/schemas/v1/Fabric.v1_0_4.json#/definitions/Fabric + +# Fabric Types constants + +FABRIC_TYPE_AHCI = 'Advanced Host Controller Interface' +FABRIC_TYPE_FC = 'Fibre Channel' +FABRIC_TYPE_FCP = 'Fibre Channel Protocol for SCSI' +FABRIC_TYPE_FCoE = 'Fibre Channel over Ethernet' +FABRIC_TYPE_FICON = 'FIbre CONnection (FICON)' +FABRIC_TYPE_FTP = 'File Transfer Protocol' +FABRIC_TYPE_HTTP = 'Hypertext Transport Protocol' +FABRIC_TYPE_HTTPS = 'Secure Hypertext Transport Protocol' +FABRIC_TYPE_I2C = 'Inter-Integrated Circuit Bus' +FABRIC_TYPE_NFSv3 = 'Network File System version 3' +FABRIC_TYPE_NFSv4 = 'Network File System version 4' +FABRIC_TYPE_NVMe = 'Non-Volatile Memory Express' +FABRIC_TYPE_NVMeOverFabrics = 'NVMe over Fabrics' +FABRIC_TYPE_OEM = 'OEM specific' +FABRIC_TYPE_PCIe = 'PCI Express' +FABRIC_TYPE_RoCE = 'RDMA over Converged Ethernet Protocol' +FABRIC_TYPE_RoCEv2 = 'RDMA over Converged Ethernet Protocol Version 2' +FABRIC_TYPE_SAS = 'Serial Attached SCSI' +FABRIC_TYPE_SATA = 'Serial AT Attachment' +FABRIC_TYPE_SFTP = 'Secure File Transfer Protocol' +FABRIC_TYPE_SMB = 'Server Message Block (aka CIFS Common Internet File System)' +FABRIC_TYPE_UHCI = 'Universal Host Controller Interface' +FABRIC_TYPE_USB = 'Universal Serial Bus' +FABRIC_TYPE_iSCSI = 'Internet SCSI' +FABRIC_TYPE_iWARP = 'Internet Wide Area Remote Direct Memory Access Protocol' diff --git a/sushy/resources/fabric/fabric.py b/sushy/resources/fabric/fabric.py new file mode 100644 index 00000000..e9c6fc2e --- /dev/null +++ b/sushy/resources/fabric/fabric.py @@ -0,0 +1,77 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +# This is referred from Redfish standard schema. +# http://redfish.dmtf.org/schemas/v1/Fabric.v1_0_4.json + +from sushy.resources import base +from sushy.resources import common +from sushy.resources.fabric import mappings as fab_maps + +import logging + +LOG = logging.getLogger(__name__) + + +class Fabric(base.ResourceBase): + """Fabric resource + + The Fabric represents a simple fabric consisting of one or more + switches, zero or more endpoints, and zero or more zones. + """ + + identity = base.Field('Id', required=True) + """Identifier for the fabric""" + + name = base.Field('Name', required=True) + """The fabric name""" + + description = base.Field('Description') + """The fabric description""" + + max_zones = base.Field('MaxZones') + """The maximum number of zones the switch can currently configure""" + + status = common.StatusField('Status') + """The fabric status""" + + fabric_type = base.MappedField('FabricType', + fab_maps.FABRIC_TYPE_VALUE_MAP) + """The protocol being sent over this fabric""" + + def __init__(self, connector, identity, redfish_version=None): + """A class representing a Fabric + + :param connector: A Connector instance + :param identity: The identity of the Fabric resource + :param redfish_version: The version of RedFish. Used to construct + the object according to schema of the given version. + """ + super(Fabric, self).__init__(connector, identity, 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/sushy/resources/fabric/mappings.py b/sushy/resources/fabric/mappings.py new file mode 100644 index 00000000..6ddd8909 --- /dev/null +++ b/sushy/resources/fabric/mappings.py @@ -0,0 +1,47 @@ +# Copyright 2017 Red Hat, 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.fabric import constants as fab_cons +from sushy import utils + +FABRIC_TYPE_VALUE_MAP = { + 'AHCI': fab_cons.FABRIC_TYPE_AHCI, + 'FC': fab_cons.FABRIC_TYPE_FC, + 'FCP': fab_cons.FABRIC_TYPE_FCP, + 'FCoE': fab_cons.FABRIC_TYPE_FCoE, + 'FICON': fab_cons.FABRIC_TYPE_FICON, + 'FTP': fab_cons.FABRIC_TYPE_FTP, + 'HTTP': fab_cons.FABRIC_TYPE_HTTP, + 'HTTPS': fab_cons.FABRIC_TYPE_HTTPS, + 'I2C': fab_cons.FABRIC_TYPE_I2C, + 'NFSv3': fab_cons.FABRIC_TYPE_NFSv3, + 'NFSv4': fab_cons.FABRIC_TYPE_NFSv4, + 'NVMe': fab_cons.FABRIC_TYPE_NVMe, + 'NVMeOverFabrics': fab_cons.FABRIC_TYPE_NVMeOverFabrics, + 'OEM': fab_cons.FABRIC_TYPE_OEM, + 'PCIe': fab_cons.FABRIC_TYPE_PCIe, + 'RoCE': fab_cons.FABRIC_TYPE_RoCE, + 'RoCEv2': fab_cons.FABRIC_TYPE_RoCEv2, + 'SAS': fab_cons.FABRIC_TYPE_SAS, + 'SATA': fab_cons.FABRIC_TYPE_SATA, + 'SFTP': fab_cons.FABRIC_TYPE_SFTP, + 'SMB': fab_cons.FABRIC_TYPE_SMB, + 'UHCI': fab_cons.FABRIC_TYPE_UHCI, + 'USB': fab_cons.FABRIC_TYPE_USB, + 'iSCSI': fab_cons.FABRIC_TYPE_iSCSI, + 'iWARP': fab_cons.FABRIC_TYPE_iWARP, +} + +FABRIC_TYPE_VALUE_MAP_REV = utils.revert_dictionary(FABRIC_TYPE_VALUE_MAP) diff --git a/sushy/tests/unit/json_samples/fabric.json b/sushy/tests/unit/json_samples/fabric.json new file mode 100644 index 00000000..9876f076 --- /dev/null +++ b/sushy/tests/unit/json_samples/fabric.json @@ -0,0 +1,29 @@ +{ + "@odata.type": "#Fabric.v1_0_3.Fabric", + "Id": "SAS", + "Name": "SAS Fabric", + "FabricType": "SAS", + "Description": "A SAS Fabric with redundant switches.", + "Status": { + "State": "Enabled", + "Health": "OK" + }, + "Zones": { + "@odata.id": "/redfish/v1/Fabrics/SAS/Zones" + }, + "Endpoints": { + "@odata.id": "/redfish/v1/Fabrics/SAS/Endpoints" + }, + "Switches": { + "@odata.id": "/redfish/v1/Fabrics/SAS/Switches" + }, + "Links": { + "Oem": {} + }, + "Actions": { + "Oem": {} + }, + "Oem": {}, + "@odata.context": "/redfish/v1/$metadata#Fabric.Fabric", + "@odata.id": "/redfish/v1/Fabrics/SAS" +} \ No newline at end of file diff --git a/sushy/tests/unit/json_samples/fabric_collection.json b/sushy/tests/unit/json_samples/fabric_collection.json new file mode 100644 index 00000000..f58a5d6d --- /dev/null +++ b/sushy/tests/unit/json_samples/fabric_collection.json @@ -0,0 +1,16 @@ +{ + "@odata.type": "#FabricCollection.FabricCollection", + "Name": "Fabric Collection", + "Members@odata.count": 2, + "Members": [ + { + "@odata.id": "/redfish/v1/Fabrics/SAS1" + }, + { + "@odata.id": "/redfish/v1/Fabrics/SAS2" + } + ], + "@odata.context": "/redfish/v1/$metadata#FabricCollection.FabricCollection", + "@odata.id": "/redfish/v1/Fabrics", + "@Redfish.Copyright": "Copyright 2014-2017 Distributed Management Task Force, Inc. (DMTF). For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright." +} \ No newline at end of file diff --git a/sushy/tests/unit/json_samples/root.json b/sushy/tests/unit/json_samples/root.json index 6e770eff..c1705ab8 100644 --- a/sushy/tests/unit/json_samples/root.json +++ b/sushy/tests/unit/json_samples/root.json @@ -21,6 +21,9 @@ "Managers": { "@odata.id": "/redfish/v1/Managers" }, + "Fabrics": { + "@odata.id": "/redfish/v1/Fabrics" + }, "Tasks": { "@odata.id": "/redfish/v1/TaskService" }, diff --git a/sushy/tests/unit/resources/fabric/__init__.py b/sushy/tests/unit/resources/fabric/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/sushy/tests/unit/resources/fabric/test_fabric.py b/sushy/tests/unit/resources/fabric/test_fabric.py new file mode 100644 index 00000000..4ac04816 --- /dev/null +++ b/sushy/tests/unit/resources/fabric/test_fabric.py @@ -0,0 +1,79 @@ +# -*- coding: utf-8 -*- + +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import json + +import mock + +import sushy +from sushy.resources.fabric import fabric +from sushy.tests.unit import base + + +class FabricTestCase(base.TestCase): + + def setUp(self): + super(FabricTestCase, self).setUp() + self.conn = mock.Mock() + with open('sushy/tests/unit/json_samples/fabric.json') as f: + self.conn.get.return_value.json.return_value = json.load(f) + + self.fabric = fabric.Fabric(self.conn, '/redfish/v1/Fabrics/SAS', + redfish_version='1.0.3') + + def test__parse_attributes(self): + # | WHEN | + self.fabric._parse_attributes() + # | THEN | + self.assertEqual('1.0.3', self.fabric.redfish_version) + self.assertEqual('SAS', self.fabric.identity) + self.assertEqual('SAS Fabric', self.fabric.name) + self.assertEqual('A SAS Fabric with redundant switches.', + self.fabric.description) + self.assertEqual(sushy.FABRIC_TYPE_SAS, + self.fabric.fabric_type) + self.assertEqual(sushy.STATE_ENABLED, self.fabric.status.state) + self.assertEqual(sushy.HEALTH_OK, self.fabric.status.health) + + +class FabricCollectionTestCase(base.TestCase): + + def setUp(self): + super(FabricCollectionTestCase, self).setUp() + self.conn = mock.Mock() + with open('sushy/tests/unit/json_samples/' + 'fabric_collection.json') as f: + self.conn.get.return_value.json.return_value = json.load(f) + self.fabric = fabric.FabricCollection( + self.conn, '/redfish/v1/Fabrics', redfish_version='1.0.3') + + @mock.patch.object(fabric, 'Fabric', autospec=True) + def test_get_member(self, fabric_mock): + self.fabric.get_member('/redfish/v1/Fabrics/SAS1') + fabric_mock.assert_called_once_with( + self.fabric._conn, '/redfish/v1/Fabrics/SAS1', + redfish_version=self.fabric.redfish_version) + + @mock.patch.object(fabric, 'Fabric', autospec=True) + def test_get_members(self, fabric_mock): + members = self.fabric.get_members() + calls = [ + mock.call(self.fabric._conn, '/redfish/v1/Fabrics/SAS1', + redfish_version=self.fabric.redfish_version), + mock.call(self.fabric._conn, '/redfish/v1/Fabrics/SAS2', + redfish_version=self.fabric.redfish_version) + ] + fabric_mock.assert_has_calls(calls) + self.assertIsInstance(members, list) + self.assertEqual(2, len(members)) diff --git a/sushy/tests/unit/test_main.py b/sushy/tests/unit/test_main.py index a343f3c6..bab2645b 100644 --- a/sushy/tests/unit/test_main.py +++ b/sushy/tests/unit/test_main.py @@ -23,6 +23,7 @@ from sushy import exceptions from sushy import main from sushy.resources.chassis import chassis from sushy.resources.compositionservice import compositionservice +from sushy.resources.fabric import fabric from sushy.resources.manager import manager from sushy.resources.registry import message_registry_file from sushy.resources.sessionservice import session @@ -68,6 +69,7 @@ class MainTestCase(base.TestCase): self.assertEqual('/redfish/v1/Systems', self.root._systems_path) self.assertEqual('/redfish/v1/Managers', self.root._managers_path) self.assertEqual('/redfish/v1/Chassis', self.root._chassis_path) + self.assertEqual('/redfish/v1/Fabrics', self.root._fabrics_path) self.assertEqual('/redfish/v1/SessionService', self.root._session_service_path) self.assertEqual('/redfish/v1/CompositionService', @@ -119,6 +121,20 @@ class MainTestCase(base.TestCase): self.root._conn, '/redfish/v1/Chassis', redfish_version=self.root.redfish_version) + @mock.patch.object(fabric, 'Fabric', autospec=True) + def test_get_fabric(self, mock_fabric): + self.root.get_fabric('fake-fabric-id') + mock_fabric.assert_called_once_with( + self.root._conn, 'fake-fabric-id', + redfish_version=self.root.redfish_version) + + @mock.patch.object(fabric, 'FabricCollection', autospec=True) + def test_get_fabric_collection(self, fabric_collection_mock): + self.root.get_fabric_collection() + fabric_collection_mock.assert_called_once_with( + self.root._conn, '/redfish/v1/Fabrics', + redfish_version=self.root.redfish_version) + @mock.patch.object(manager, 'ManagerCollection', autospec=True) def test_get_manager_collection(self, ManagerCollection_mock): self.root.get_manager_collection() @@ -199,6 +215,11 @@ class BareMinimumMainTestCase(base.TestCase): exceptions.MissingAttributeError, 'Chassis/@odata.id', self.root.get_chassis_collection) + def test_get_fabric_collection_when_fabrics_attr_absent(self): + self.assertRaisesRegex( + exceptions.MissingAttributeError, + 'Fabrics/@odata.id', self.root.get_fabric_collection) + def test_get_session_service_when_sessionservice_attr_absent(self): self.assertRaisesRegex( exceptions.MissingAttributeError,