From a9c4a8e7c2e8ed242f038f0c56476d4a578a88f4 Mon Sep 17 00:00:00 2001 From: Winicius Silva Date: Fri, 29 Dec 2023 01:02:08 -0300 Subject: [PATCH] Allows System to access VirtualMedia in Sushy After DMTF release a new version of their guide[1], there was an update in VirtualMedia URI. Deprecated: /redfish/v1/Managers/{ManagerId}/VirtualMedia/{VirtualMediaId} New: /redfish/v1/Systems/{ComputerSystemId}/VirtualMedia/{VirtualMediaId} So, it is needed that System has access to VirtualMedia in Sushy lib to later Ironic may be able to handle this. This way, i added a property in System that it can have access to VirtualMedia instance. [1]https://www.dmtf.org/sites/default/files/standards/documents/DSP2046_2023.2.pdf Partial-Bug: #2039458 Change-Id: I7bd7db539eab158c283524aecdaa9ebebd033cf6 --- ...-system-virtualmedia-7a61bd77780f7b0e.yaml | 6 + sushy/resources/system/system.py | 13 ++ .../tests/unit/json_samples/managerv1_18.json | 98 +++++++++++ .../tests/unit/json_samples/systemv1_20.json | 161 ++++++++++++++++++ .../virtual_media_collectionv1_6.json | 12 ++ .../unit/json_samples/virtual_mediav1_6.json | 19 +++ .../unit/resources/manager/test_manager.py | 20 +++ .../unit/resources/system/test_system.py | 80 +++++++++ 8 files changed, 409 insertions(+) create mode 100644 releasenotes/notes/sushy-system-virtualmedia-7a61bd77780f7b0e.yaml create mode 100644 sushy/tests/unit/json_samples/managerv1_18.json create mode 100644 sushy/tests/unit/json_samples/systemv1_20.json create mode 100644 sushy/tests/unit/json_samples/virtual_media_collectionv1_6.json create mode 100644 sushy/tests/unit/json_samples/virtual_mediav1_6.json diff --git a/releasenotes/notes/sushy-system-virtualmedia-7a61bd77780f7b0e.yaml b/releasenotes/notes/sushy-system-virtualmedia-7a61bd77780f7b0e.yaml new file mode 100644 index 00000000..a5ab6ae0 --- /dev/null +++ b/releasenotes/notes/sushy-system-virtualmedia-7a61bd77780f7b0e.yaml @@ -0,0 +1,6 @@ +--- +features: + - | + Adds support for virtual media via ``System`` resource. For more + information, see `bug 2039458 + `_. diff --git a/sushy/resources/system/system.py b/sushy/resources/system/system.py index a98ad06b..33284e9e 100644 --- a/sushy/resources/system/system.py +++ b/sushy/resources/system/system.py @@ -27,6 +27,7 @@ from sushy.resources.chassis import chassis from sushy.resources import common from sushy.resources import constants as res_cons from sushy.resources.manager import manager +from sushy.resources.manager import virtual_media from sushy.resources import settings from sushy.resources.system import bios from sushy.resources.system import constants as sys_cons @@ -548,6 +549,18 @@ class System(base.ResourceBase): redfish_version=self.redfish_version, registries=self.registries, root=self.root) + @property + @utils.cache_it + def virtual_media(self): + """Property to reference `VirtualMedia` instance + + :returns: A `VirtualMediaCollection` instance. + """ + return virtual_media.VirtualMediaCollection( + self._conn, utils.get_sub_resource_path_by(self, 'VirtualMedia'), + redfish_version=self.redfish_version, registries=self.registries, + root=self.root) + class SystemCollection(base.ResourceCollectionBase): diff --git a/sushy/tests/unit/json_samples/managerv1_18.json b/sushy/tests/unit/json_samples/managerv1_18.json new file mode 100644 index 00000000..1a758350 --- /dev/null +++ b/sushy/tests/unit/json_samples/managerv1_18.json @@ -0,0 +1,98 @@ +{ + "@odata.type": "#Manager.v1_18_0.Manager", + "Id": "BMC", + "Name": "Manager", + "ManagerType": "BMC", + "Description": "Contoso BMC", + "ServiceEntryPointUUID": "92384634-2938-2342-8820-489239905423", + "UUID": "58893887-8974-2487-2389-841168418919", + "Model": "Joo Janta 200", + "DateTime": "2015-03-13T04:14:33+06:00", + "DateTimeLocalOffset": "+06:00", + "Status": { + "State": "Enabled", + "Health": "OK" + }, + "PowerState": "On", + "GraphicalConsole": { + "ServiceEnabled": true, + "MaxConcurrentSessions": 2, + "ConnectTypesSupported": [ + "KVMIP" + ] + }, + "SerialConsole": { + "ServiceEnabled": true, + "MaxConcurrentSessions": 1, + "ConnectTypesSupported": [ + "Telnet", + "SSH", + "IPMI" + ] + }, + "CommandShell": { + "ServiceEnabled": true, + "MaxConcurrentSessions": 4, + "ConnectTypesSupported": [ + "Telnet", + "SSH" + ] + }, + "FirmwareVersion": "1.45.455b66-rev4", + "AdditionalFirmwareVersions": { + "Bootloader": "v2022.01", + "Kernel": "Linux 5.13.0-30-generic arm71", + "Oem": { + "Contoso": { + "@odata.type": "#ContosoSoftwareInventory.v1_0_0.AdditionalVersions", + "ManagementApp": "1.30" + } + } + }, + "NetworkProtocol": { + "@odata.id": "/redfish/v1/Managers/BMC/NetworkProtocol" + }, + "EthernetInterfaces": { + "@odata.id": "/redfish/v1/Managers/BMC/EthernetInterfaces" + }, + "HostInterfaces": { + "@odata.id": "/redfish/v1/Managers/BMC/HostInterfaces" + }, + "SerialInterfaces": { + "@odata.id": "/redfish/v1/Managers/BMC/SerialInterfaces" + }, + "LogServices": { + "@odata.id": "/redfish/v1/Managers/BMC/LogServices" + }, + "DedicatedNetworkPorts": { + "@odata.id": "/redfish/v1/Managers/BMC/DedicatedNetworkPorts" + }, + "SecurityPolicy": { + "@odata.id": "/redfish/v1/Managers/BMC/SecurityPolicy" + }, + "Links": { + "ManagerForServers": [ + { + "@odata.id": "/redfish/v1/Systems/437XR1138R2" + } + ], + "ManagerForChassis": [ + { + "@odata.id": "/redfish/v1/Chassis/1U" + } + ], + "ManagerInChassis": { + "@odata.id": "/redfish/v1/Chassis/1U" + } + }, + "Actions": { + "#Manager.Reset": { + "target": "/redfish/v1/Managers/BMC/Actions/Manager.Reset", + "ResetType@Redfish.AllowableValues": [ + "ForceRestart", + "GracefulRestart" + ] + } + }, + "@odata.id": "/redfish/v1/Managers/BMC" + } \ No newline at end of file diff --git a/sushy/tests/unit/json_samples/systemv1_20.json b/sushy/tests/unit/json_samples/systemv1_20.json new file mode 100644 index 00000000..d2945573 --- /dev/null +++ b/sushy/tests/unit/json_samples/systemv1_20.json @@ -0,0 +1,161 @@ +{ + "@odata.type": "#ComputerSystem.v1_20_1.ComputerSystem", + "Id": "437XR1138R2", + "Name": "WebFrontEnd483", + "SystemType": "Physical", + "AssetTag": "Chicago-45Z-2381", + "Manufacturer": "Contoso", + "Model": "3500", + "SubModel": "RX", + "SKU": "8675309", + "SerialNumber": "437XR1138R2", + "PartNumber": "224071-J23", + "Description": "Web Front End node", + "UUID": "38947555-7742-3448-3784-823347823834", + "HostName": "web483", + "Status": { + "State": "Enabled", + "Health": "OK", + "HealthRollup": "OK" + }, + "HostingRoles": [ + "ApplicationServer" + ], + "IndicatorLED": "Off", + "PowerState": "On", + "Boot": { + "BootSourceOverrideEnabled": "Once", + "BootSourceOverrideTarget": "Pxe", + "BootSourceOverrideTarget@Redfish.AllowableValues": [ + "None", + "Pxe", + "Cd", + "Usb", + "Hdd", + "BiosSetup", + "Utilities", + "Diags", + "SDCard", + "UefiTarget" + ], + "BootSourceOverrideMode": "UEFI", + "UefiTargetBootSourceOverride": "/0x31/0x33/0x01/0x01" + }, + "TrustedModules": [ + { + "FirmwareVersion": "1.13b", + "InterfaceType": "TPM1_2", + "Status": { + "State": "Enabled", + "Health": "OK" + } + } + ], + "Oem": { + "Contoso": { + "@odata.type": "#Contoso.ComputerSystem", + "ProductionLocation": { + "FacilityName": "PacWest Production Facility", + "Country": "USA" + } + }, + "Chipwise": { + "@odata.type": "#Chipwise.ComputerSystem", + "Style": "Executive" + } + }, + "BootProgress": { + "LastState": "OSRunning", + "LastStateTime": "2021-03-13T04:14:13+06:00", + "LastBootTimeSeconds": 676 + }, + "LastResetTime": "2021-03-13T04:02:57+06:00", + "BiosVersion": "P79 v1.45 (12/06/2017)", + "ProcessorSummary": { + "Count": 2, + "Model": "Multi-Core Intel(R) Xeon(R) processor 7xxx Series", + "LogicalProcessorCount": 16, + "CoreCount": 8, + "Status": { + "State": "Enabled", + "Health": "OK", + "HealthRollup": "OK" + } + }, + "MemorySummary": { + "TotalSystemMemoryGiB": 96, + "TotalSystemPersistentMemoryGiB": 0, + "MemoryMirroring": "None", + "Status": { + "State": "Enabled", + "Health": "OK", + "HealthRollup": "OK" + } + }, + "Bios": { + "@odata.id": "/redfish/v1/Systems/437XR1138R2/Bios" + }, + "SecureBoot": { + "@odata.id": "/redfish/v1/Systems/437XR1138R2/SecureBoot" + }, + "Processors": { + "@odata.id": "/redfish/v1/Systems/437XR1138R2/Processors" + }, + "Memory": { + "@odata.id": "/redfish/v1/Systems/437XR1138R2/Memory" + }, + "EthernetInterfaces": { + "@odata.id": "/redfish/v1/Systems/437XR1138R2/EthernetInterfaces" + }, + "SimpleStorage": { + "@odata.id": "/redfish/v1/Systems/437XR1138R2/SimpleStorage" + }, + "LogServices": { + "@odata.id": "/redfish/v1/Systems/437XR1138R2/LogServices" + }, + "GraphicsControllers": { + "@odata.id": "/redfish/v1/Systems/437XR1138R2/GraphicsControllers" + }, + "USBControllers": { + "@odata.id": "/redfish/v1/Systems/437XR1138R2/USBControllers" + }, + "Certificates": { + "@odata.id": "/redfish/v1/Systems/437XR1138R2/Certificates" + }, + "VirtualMedia": { + "@odata.id": "/redfish/v1/Systems/437XR1138R2/VirtualMedia" + }, + "Links": { + "Chassis": [ + { + "@odata.id": "/redfish/v1/Chassis/1U" + } + ], + "ManagedBy": [ + { + "@odata.id": "/redfish/v1/Managers/BMC" + } + ] + }, + "Actions": { + "#ComputerSystem.Reset": { + "target": "/redfish/v1/Systems/437XR1138R2/Actions/ComputerSystem.Reset", + "ResetType@Redfish.AllowableValues": [ + "On", + "ForceOff", + "GracefulShutdown", + "GracefulRestart", + "ForceRestart", + "Nmi", + "ForceOn", + "PushPowerButton" + ] + }, + "Oem": { + "#Contoso.Reset": { + "target": "/redfish/v1/Systems/437XR1138R2/Oem/Contoso/Actions/Contoso.Reset" + } + } + }, + "@odata.id": "/redfish/v1/Systems/437XR1138R2" + } \ No newline at end of file diff --git a/sushy/tests/unit/json_samples/virtual_media_collectionv1_6.json b/sushy/tests/unit/json_samples/virtual_media_collectionv1_6.json new file mode 100644 index 00000000..e47bb69d --- /dev/null +++ b/sushy/tests/unit/json_samples/virtual_media_collectionv1_6.json @@ -0,0 +1,12 @@ +{ + "@odata.type": "#VirtualMediaCollection.VirtualMediaCollection", + "Name": "Virtual Media Services", + "Description": "Virtual Media Service Settings", + "Members@odata.count": 1, + "Members": [ + { + "@odata.id": "/redfish/v1/Systems/437XR1138R2/VirtualMedia/CD1" + } + ], + "@odata.id": "/redfish/v1/Systems/437XR1138R2/VirtualMedia" + } \ No newline at end of file diff --git a/sushy/tests/unit/json_samples/virtual_mediav1_6.json b/sushy/tests/unit/json_samples/virtual_mediav1_6.json new file mode 100644 index 00000000..b0b112a5 --- /dev/null +++ b/sushy/tests/unit/json_samples/virtual_mediav1_6.json @@ -0,0 +1,19 @@ +{ + "@odata.type": "#VirtualMedia.v1_6_1.VirtualMedia", + "Id": "CD1", + "Name": "Virtual CD", + "MediaTypes": [ + "CD", + "DVD" + ], + "Image": "redfish.dmtf.org/freeImages/freeOS.1.1.iso", + "ImageName": "mymedia-read-only", + "ConnectedVia": "Applet", + "Inserted": true, + "WriteProtected": false, + "VerifyCertificate": true, + "Certificates": { + "@odata.id": "/redfish/v1/Systems/437XR1138R2/VirtualMedia/CD1/Certificates" + }, + "@odata.id": "/redfish/v1/Systems/437XR1138R2/VirtualMedia/CD1" + } \ No newline at end of file diff --git a/sushy/tests/unit/resources/manager/test_manager.py b/sushy/tests/unit/resources/manager/test_manager.py index 200fa4c3..7cd7e6e6 100644 --- a/sushy/tests/unit/resources/manager/test_manager.py +++ b/sushy/tests/unit/resources/manager/test_manager.py @@ -305,6 +305,26 @@ class ManagerTestCase(base.TestCase): '/redfish/v1/Chassis/1U', actual_chassis[0].path) +class ManagerWithoutVirtualMedia(base.TestCase): + + def setUp(self): + super(ManagerWithoutVirtualMedia, self).setUp() + self.conn = mock.Mock() + with open('sushy/tests/unit/json_samples/' + 'managerv1_18.json') as f: + self.json_doc = json.load(f) + + self.conn.get.return_value.json.return_value = self.json_doc + + self.manager = manager.Manager(self.conn, '/redfish/v1/Managers/BMC', + redfish_version='1.0.2') + + def test_no_virtual_media_attr(self): + with self.assertRaisesRegex( + exceptions.MissingAttributeError, 'attribute VirtualMedia'): + self.manager.virtual_media + + class ManagerCollectionTestCase(base.TestCase): def setUp(self): diff --git a/sushy/tests/unit/resources/system/test_system.py b/sushy/tests/unit/resources/system/test_system.py index 0731ce03..d6e56674 100644 --- a/sushy/tests/unit/resources/system/test_system.py +++ b/sushy/tests/unit/resources/system/test_system.py @@ -23,6 +23,7 @@ from sushy import exceptions from sushy.resources.chassis import chassis from sushy.resources import constants as res_cons from sushy.resources.manager import manager +from sushy.resources.manager import virtual_media from sushy.resources.oem import fake from sushy.resources.system import bios from sushy.resources.system import processor @@ -910,6 +911,85 @@ class SystemTestCase(base.TestCase): self.assertIs(self.sys_inst, contoso_system_extn_inst._parent_resource) self.assertEqual('Contoso', contoso_system_extn_inst._vendor_id) + def test_no_virtual_media_attr(self): + with self.assertRaisesRegex( + exceptions.MissingAttributeError, 'attribute VirtualMedia'): + self.sys_inst.virtual_media + + +class SystemWithVirtualMedia(base.TestCase): + + def setUp(self): + super(SystemWithVirtualMedia, self).setUp() + self.conn = mock.Mock() + with open('sushy/tests/unit/json_samples/' + 'systemv1_20.json') as f: + self.json_doc = json.load(f) + + self.conn.get.return_value.json.return_value = self.json_doc + + self.sys_inst = system.System( + self.conn, '/redfish/v1/Systems/437XR1138R2', + redfish_version='1.0.2') + + def test_virtual_media(self): + # | GIVEN | + with open('sushy/tests/unit/json_samples/' + 'virtual_mediav1_6.json') as f: + virtual_media_json = json.load(f) + + with open('sushy/tests/unit/json_samples/' + 'virtual_media_collectionv1_6.json') as f: + virtual_media_collection_json = json.load(f) + + self.conn.get.return_value.json.side_effect = [ + virtual_media_collection_json, virtual_media_json] + + # | WHEN | + actual_virtual_media = self.sys_inst.virtual_media + + # | THEN | + self.assertIsInstance(actual_virtual_media, + virtual_media.VirtualMediaCollection) + self.assertEqual(actual_virtual_media.name, 'Virtual Media Services') + + member = actual_virtual_media.get_member( + '/redfish/v1/Systems/437XR1138R2/VirtualMedia/CD1') + + self.assertEqual(member.image_name, 'mymedia-read-only') + self.assertTrue(member.inserted) + self.assertFalse(member.write_protected) + self.assertTrue(member.verify_certificate) + + def test_virtual_media_on_refresh(self): + # | GIVEN | + with open('sushy/tests/unit/json_samples/' + 'virtual_media_collectionv1_6.json') as f: + self.conn.get.return_value.json.return_value = json.load(f) + + # | WHEN & THEN | + actual_virtual_media = self.sys_inst.virtual_media + self.assertIsInstance(actual_virtual_media, + virtual_media.VirtualMediaCollection) + + with open('sushy/tests/unit/json_samples/systemv1_20.json', 'r') as f: + self.conn.get.return_value.json.return_value = json.loads(f.read()) + + self.sys_inst.invalidate() + self.sys_inst.refresh(force=False) + + self.assertTrue(actual_virtual_media._is_stale) + + # | GIVEN | + with open('sushy/tests/unit/json_samples/' + 'virtual_media_collectionv1_6.json') as f: + self.conn.get.return_value.json.return_value = json.load(f) + + # | WHEN & THEN | + self.assertIsInstance(self.sys_inst.virtual_media, + virtual_media.VirtualMediaCollection) + self.assertFalse(actual_virtual_media._is_stale) + class SystemCollectionTestCase(base.TestCase):