]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
node-proxy: rename firmwares to firmware with legacy aliases
authorGuillaume Abrioux <gabrioux@ibm.com>
Mon, 15 Jun 2026 13:18:48 +0000 (15:18 +0200)
committerGuillaume Abrioux <gabrioux@ibm.com>
Wed, 24 Jun 2026 02:17:39 +0000 (04:17 +0200)
Let's use 'firmware' as the standard name in node-proxy, cephadm,
and orch hardware status.

We still accept 'firmwares' as a deprecated alias and read legacy
cache payloads transparently for backward compatibility.

Fixes: https://tracker.ceph.com/issues/77410
Signed-off-by: Guillaume Abrioux <gabrioux@ibm.com>
13 files changed:
doc/hardware-monitoring/index.rst
src/ceph-node-proxy/ceph_node_proxy/api.py
src/ceph-node-proxy/ceph_node_proxy/baseredfishsystem.py
src/ceph-node-proxy/ceph_node_proxy/basesystem.py
src/ceph-node-proxy/ceph_node_proxy/protocols.py
src/ceph-node-proxy/tests/test_baseredfishsystem.py
src/pybind/mgr/cephadm/agent.py
src/pybind/mgr/cephadm/inventory.py
src/pybind/mgr/cephadm/module.py
src/pybind/mgr/cephadm/tests/node_proxy_data.py
src/pybind/mgr/cephadm/tests/test_node_proxy.py
src/pybind/mgr/orchestrator/_interface.py
src/pybind/mgr/orchestrator/module.py

index 033b89aa6107a07b0c7dc47e3bea118e73b9f6d9..900a9129456d5267c5f6d1385066ffa2c7027749 100644 (file)
@@ -89,7 +89,7 @@ Supported categories are:
 * power
 * fans
 * temperatures
-* firmwares
+* firmware
 * criticals
 
 
@@ -144,7 +144,7 @@ ________________
 
 .. prompt:: bash # auto
 
-  # ceph orch hardware status node-10 --category firmwares
+  # ceph orch hardware status node-10 --category firmware
   +------------+----------------------------------------------------------------------------+--------------------------------------------------------------+----------------------+-------------+--------+
   |    HOST    |                                 COMPONENT                                  |                             NAME                             |         DATE         |   VERSION   | STATUS |
   +------------+----------------------------------------------------------------------------+--------------------------------------------------------------+----------------------+-------------+--------+
@@ -189,6 +189,6 @@ For Developers
 .. automethod:: NodeProxyEndpoint.processors
 .. automethod:: NodeProxyEndpoint.fans
 .. automethod:: NodeProxyEndpoint.temperatures
-.. automethod:: NodeProxyEndpoint.firmwares
+.. automethod:: NodeProxyEndpoint.firmware
 .. automethod:: NodeProxyEndpoint.led
 
index e51a1a3397734b0a3f87d3e8e6c98e8d66a4e81f..563ac88c8f79391a8880f8d67e09bfc0e7d7e48d 100644 (file)
@@ -116,11 +116,17 @@ class API(Server):
     def temperatures(self) -> Dict[str, Any]:
         return {"temperatures": self.backend.get_temperatures()}
 
+    @cherrypy.expose
+    @cherrypy.tools.allow(methods=["GET"])
+    @cherrypy.tools.json_out()
+    def firmware(self) -> Dict[str, Any]:
+        return {"firmware": self.backend.get_firmware()}
+
     @cherrypy.expose
     @cherrypy.tools.allow(methods=["GET"])
     @cherrypy.tools.json_out()
     def firmwares(self) -> Dict[str, Any]:
-        return {"firmwares": self.backend.get_firmwares()}
+        return {"firmwares": self.backend.get_firmware()}
 
     def _cp_dispatch(self, vpath: List[str]) -> "API":
         if vpath[0] == "led" and len(vpath) > 1:  # /led/{type}/{id}
index 15b513da6f0ac57f34860186b7afac279ca07abd..3da5a0f9398bf36984e1871c37e1d7759f89d195 100644 (file)
@@ -47,7 +47,7 @@ class BaseRedfishSystem(BaseSystem):
         "ReadingUnits",
         "Status",
     ]
-    FIRMWARES_FIELDS: List[str] = [
+    FIRMWARE_FIELDS: List[str] = [
         "Name",
         "Description",
         "ReleaseDate",
@@ -81,9 +81,9 @@ class BaseRedfishSystem(BaseSystem):
                 "chassis", "Thermal", TEMPERATURES_FIELDS, "Temperatures"
             ),
         ],
-        "firmwares": [
+        "firmware": [
             ComponentUpdateSpec(
-                "update_service", "FirmwareInventory", FIRMWARES_FIELDS, None
+                "update_service", "FirmwareInventory", FIRMWARE_FIELDS, None
             ),
         ],
     }
@@ -124,7 +124,7 @@ class BaseRedfishSystem(BaseSystem):
                 "network",
                 "processors",
                 "storage",
-                "firmwares",
+                "firmware",
             ],
         )
         self.update_funcs: List[Callable] = []
@@ -230,7 +230,7 @@ class BaseRedfishSystem(BaseSystem):
                 "fans": self.get_fans(),
                 "temperatures": self.get_temperatures(),
             },
-            "firmwares": self.get_firmwares(),
+            "firmware": self.get_firmware(),
         }
         return result
 
@@ -262,8 +262,8 @@ class BaseRedfishSystem(BaseSystem):
     def get_storage(self) -> Dict[str, Dict[str, Dict]]:
         return dict(self._sys.get("storage", {}))
 
-    def get_firmwares(self) -> Dict[str, Dict[str, Dict]]:
-        return dict(self._sys.get("firmwares", {}))
+    def get_firmware(self) -> Dict[str, Dict[str, Dict]]:
+        return dict(self._sys.get("firmware", {}))
 
     def get_power(self) -> Dict[str, Dict[str, Dict]]:
         return dict(self._sys.get("power", {}))
@@ -387,8 +387,8 @@ class BaseRedfishSystem(BaseSystem):
     def _update_temperatures(self) -> None:
         self._run_update("temperatures")
 
-    def _update_firmwares(self) -> None:
-        self._run_update("firmwares")
+    def _update_firmware(self) -> None:
+        self._run_update("firmware")
 
     def device_led_on(self, device: str) -> int:
         raise NotImplementedError()
index 025638b5cde3fdd5ccc3122eb6f7ebf71f56fcc3..aecc0e137c0d5be6a1e6f9a24158c907fa953ff5 100644 (file)
@@ -48,7 +48,7 @@ class BaseSystem(BaseThread):
     def get_storage(self) -> Dict[str, Dict[str, Dict]]:
         raise NotImplementedError()
 
-    def get_firmwares(self) -> Dict[str, Dict[str, Dict]]:
+    def get_firmware(self) -> Dict[str, Dict[str, Dict]]:
         raise NotImplementedError()
 
     def get_sn(self) -> str:
index 2b322e8ed7e8bf772e194fbfd3e3c7e637c20ead..e82d425bea5de79e2df1495492afb6fd6b415fcc 100644 (file)
@@ -18,7 +18,7 @@ class SystemBackend(Protocol):
 
     def get_temperatures(self) -> Dict[str, Any]: ...
 
-    def get_firmwares(self) -> Dict[str, Any]: ...
+    def get_firmware(self) -> Dict[str, Any]: ...
 
     def get_led(self) -> Dict[str, Any]: ...
 
index 2939081935ced6e0af618a42e3f2e3157542ff9c..28a9052140617ec0337a70f22b101b792400905b 100644 (file)
@@ -46,7 +46,7 @@ class TestBaseRedfishSystemInit:
         assert "network" in system.component_list
         assert "processors" in system.component_list
         assert "storage" in system.component_list
-        assert "firmwares" in system.component_list
+        assert "firmware" in system.component_list
         assert "fans" in system.component_list
         assert "temperatures" in system.component_list
 
@@ -110,8 +110,8 @@ class TestBaseRedfishSystemGetters:
     def test_get_temperatures_empty(self, system):
         assert system.get_temperatures() == {}
 
-    def test_get_firmwares_empty(self, system):
-        assert system.get_firmwares() == {}
+    def test_get_firmware_empty(self, system):
+        assert system.get_firmware() == {}
 
     def test_get_status_empty(self, system):
         assert system.get_status() == {}
@@ -127,7 +127,7 @@ class TestBaseRedfishSystemGetSystem:
         system._sys["power"] = {}
         system._sys["fans"] = {}
         system._sys["temperatures"] = {}
-        system._sys["firmwares"] = {}
+        system._sys["firmware"] = {}
         result = system.get_system()
         assert "host" in result
         assert "sn" in result
@@ -140,7 +140,7 @@ class TestBaseRedfishSystemGetSystem:
         assert result["status"]["power"] == {}
         assert result["status"]["fans"] == {}
         assert result["status"]["temperatures"] == {}
-        assert "firmwares" in result
+        assert "firmware" in result
 
 
 class TestBaseRedfishSystemGetSpecs:
@@ -248,7 +248,7 @@ class TestBaseRedfishSystemComponentSpecs:
         assert "power" in BaseRedfishSystem.COMPONENT_SPECS
         assert "fans" in BaseRedfishSystem.COMPONENT_SPECS
         assert "temperatures" in BaseRedfishSystem.COMPONENT_SPECS
-        assert "firmwares" in BaseRedfishSystem.COMPONENT_SPECS
+        assert "firmware" in BaseRedfishSystem.COMPONENT_SPECS
 
     def test_field_lists_non_empty(self):
         assert len(BaseRedfishSystem.NETWORK_FIELDS) > 0
index 166d9ba48c06e6314b494f4e475e9cd0d9b0179b..a7405c969a2159fb273a300ed337cf6620aa6f0d 100644 (file)
@@ -206,7 +206,7 @@ class NodeProxyEndpoint:
         for sys_id in data.keys():
             for member in data[sys_id].keys():
                 member_data = data[sys_id][member]
-                if member == 'firmwares':
+                if member in ('firmware', 'firmwares'):
                     continue
                 _status = self._get_health_value(member_data)
                 if _status and _status != 'ok':
@@ -626,11 +626,17 @@ class NodeProxyEndpoint:
     @cherrypy.tools.allow(methods=['GET'])
     @cherrypy.tools.json_out()
     def firmwares(self, **kw: Any) -> Dict[str, Any]:
+        return self.firmware(**kw)
+
+    @cherrypy.expose
+    @cherrypy.tools.allow(methods=['GET'])
+    @cherrypy.tools.json_out()
+    def firmware(self, **kw: Any) -> Dict[str, Any]:
         """
         Handles GET request to retrieve firmware information.
 
         This function is exposed to handle GET requests and fetches firmware data using
-        the 'firmwares' method from the NodeProxyCache class.
+        the 'firmware' method from the NodeProxyCache class.
 
         :param kw: Keyword arguments for the request.
         :type kw: dict
@@ -641,7 +647,7 @@ class NodeProxyEndpoint:
         :raises cherrypy.HTTPError 404: If the passed hostname is not found.
         """
         try:
-            results = self.mgr.node_proxy_cache.firmwares(**kw)
+            results = self.mgr.node_proxy_cache.firmware(**kw)
         except (KeyError, OrchestratorError):
             raise cherrypy.HTTPError(404, f"{kw.get('hostname')} not found.")
         return results
index 3f6f75df5acc5a4d6684f19485dd7840f09da697..7e396401e38e9e0b5f5912bf86e23560ce3481c7 100644 (file)
@@ -1636,6 +1636,12 @@ class NodeProxyCache:
         self.oob: Dict[str, Any] = {}
         self.keyrings: Dict[str, str] = {}
 
+    @staticmethod
+    def _host_firmware(host_data: Dict[str, Any]) -> Any:
+        if 'firmware' in host_data:
+            return host_data['firmware']
+        return host_data.get('firmwares', {})
+
     def load(self) -> None:
         _oob = self.mgr.get_store(f'{NODE_PROXY_CACHE_PREFIX}/oob', '{}')
         self.oob = json.loads(_oob)
@@ -1755,7 +1761,7 @@ class NodeProxyCache:
                 _result[host]['status'][component] = state
             _result[host]['sn'] = data['sn']
             _result[host]['host'] = data['host']
-            _result[host]['status']['firmwares'] = data['firmwares']
+            _result[host]['status']['firmware'] = self._host_firmware(data)
         return _result
 
     def common(self, endpoint: str, **kw: Any) -> Dict[str, Any]:
@@ -1783,7 +1789,7 @@ class NodeProxyCache:
                 raise KeyError(f'Invalid host {host} or component {endpoint}.')
         return _result
 
-    def firmwares(self, **kw: Any) -> Dict[str, Any]:
+    def firmware(self, **kw: Any) -> Dict[str, Any]:
         """
         Retrieves firmware information for a specific hostname or all hosts.
 
@@ -1798,7 +1804,7 @@ class NodeProxyCache:
         :rtype: Dict[str, Any]
         """
         hosts = self._resolve_hosts(**kw)
-        return {host: self.data[host]['firmwares'] for host in hosts}
+        return {host: self._host_firmware(self.data[host]) for host in hosts}
 
     def get_critical_from_host(self, hostname: str) -> Dict[str, Any]:
         if hostname not in self.data:
index 3bd1bbfb73d191c6bab7263951be41a9f9e975e5..7edfe244d707ad347a6fdd66abbd1ea8a4b33554 100644 (file)
@@ -2300,9 +2300,13 @@ Then run the following:
     def node_proxy_summary(self, hostname: Optional[str] = None) -> Dict[str, Any]:
         return self.node_proxy_cache.summary(hostname=hostname)
 
+    @handle_orch_error
+    def node_proxy_firmware(self, hostname: Optional[str] = None) -> Dict[str, Any]:
+        return self.node_proxy_cache.firmware(hostname=hostname)
+
     @handle_orch_error
     def node_proxy_firmwares(self, hostname: Optional[str] = None) -> Dict[str, Any]:
-        return self.node_proxy_cache.firmwares(hostname=hostname)
+        return self.node_proxy_cache.firmware(hostname=hostname)
 
     @handle_orch_error
     def node_proxy_criticals(self, hostname: Optional[str] = None) -> Dict[str, Any]:
index 6f103dbeba1a74d2c96dd515c1ed64153a091564..3c01582d756e141b45a1c45fd3e408071235165c 100644 (file)
@@ -1,3 +1,3 @@
-full_set_with_critical = {'host': 'host01', 'sn': '12345', 'status': {'storage': {'1': {'disk.bay.0:enclosure.internal.0-1:raid.integrated.1-1': {'description': 'Solid State Disk 0:1:0', 'entity': 'RAID.Integrated.1-1', 'capacity_bytes': 959656755200, 'model': 'KPM5XVUG960G', 'protocol': 'SAS', 'serial_number': '8080A1CRTP5F', 'status': {'health': 'Critical', 'healthrollup': 'OK', 'state': 'Enabled'}, 'physical_location': {'partlocation': {'locationordinalvalue': 0, 'locationtype': 'Slot'}}}, 'disk.bay.9:enclosure.internal.0-1': {'description': 'PCIe SSD in Slot 9 in Bay 1', 'entity': 'CPU.1', 'capacity_bytes': 1600321314816, 'model': 'Dell Express Flash NVMe P4610 1.6TB SFF', 'protocol': 'PCIe', 'serial_number': 'PHLN035305MN1P6AGN', 'status': {'health': 'Critical', 'healthrollup': 'OK', 'state': 'Enabled'}, 'physical_location': {'partlocation': {'locationordinalvalue': 9, 'locationtype': 'Slot'}}}}}, 'processors': {'1': {'cpu.socket.2': {'description': 'Represents the properties of a Processor attached to this System', 'total_cores': 20, 'total_threads': 40, 'processor_type': 'CPU', 'model': 'Intel(R) Xeon(R) Gold 6230 CPU @ 2.10GHz', 'status': {'health': 'OK', 'state': 'Enabled'}, 'manufacturer': 'Intel'}}}, 'network': {'1': {'nic.slot.1-1-1': {'description': 'NIC in Slot 1 Port 1 Partition 1', 'name': 'System Ethernet Interface', 'speed_mbps': 0, 'status': {'health': 'OK', 'state': 'StandbyOffline'}}}}, 'memory': {'1': {'dimm.socket.a1': {'description': 'DIMM A1', 'memory_device_type': 'DDR4', 'capacity_mi_b': 31237, 'status': {'health': 'Critical', 'state': 'Enabled'}}}}}, 'firmwares': {}}
+full_set_with_critical = {'host': 'host01', 'sn': '12345', 'status': {'storage': {'1': {'disk.bay.0:enclosure.internal.0-1:raid.integrated.1-1': {'description': 'Solid State Disk 0:1:0', 'entity': 'RAID.Integrated.1-1', 'capacity_bytes': 959656755200, 'model': 'KPM5XVUG960G', 'protocol': 'SAS', 'serial_number': '8080A1CRTP5F', 'status': {'health': 'Critical', 'healthrollup': 'OK', 'state': 'Enabled'}, 'physical_location': {'partlocation': {'locationordinalvalue': 0, 'locationtype': 'Slot'}}}, 'disk.bay.9:enclosure.internal.0-1': {'description': 'PCIe SSD in Slot 9 in Bay 1', 'entity': 'CPU.1', 'capacity_bytes': 1600321314816, 'model': 'Dell Express Flash NVMe P4610 1.6TB SFF', 'protocol': 'PCIe', 'serial_number': 'PHLN035305MN1P6AGN', 'status': {'health': 'Critical', 'healthrollup': 'OK', 'state': 'Enabled'}, 'physical_location': {'partlocation': {'locationordinalvalue': 9, 'locationtype': 'Slot'}}}}}, 'processors': {'1': {'cpu.socket.2': {'description': 'Represents the properties of a Processor attached to this System', 'total_cores': 20, 'total_threads': 40, 'processor_type': 'CPU', 'model': 'Intel(R) Xeon(R) Gold 6230 CPU @ 2.10GHz', 'status': {'health': 'OK', 'state': 'Enabled'}, 'manufacturer': 'Intel'}}}, 'network': {'1': {'nic.slot.1-1-1': {'description': 'NIC in Slot 1 Port 1 Partition 1', 'name': 'System Ethernet Interface', 'speed_mbps': 0, 'status': {'health': 'OK', 'state': 'StandbyOffline'}}}}, 'memory': {'1': {'dimm.socket.a1': {'description': 'DIMM A1', 'memory_device_type': 'DDR4', 'capacity_mi_b': 31237, 'status': {'health': 'Critical', 'state': 'Enabled'}}}}}, 'firmware': {}}
 mgr_inventory_cache = {'host01': {'hostname': 'host01', 'addr': '10.10.10.11', 'labels': ['_admin'], 'status': '', 'oob': {'hostname': '10.10.10.11', 'username': 'root', 'password': 'ceph123'}}, 'host02': {'hostname': 'host02', 'addr': '10.10.10.12', 'labels': [], 'status': '', 'oob': {'hostname': '10.10.10.12', 'username': 'root', 'password': 'ceph123'}}}
-full_set = {'host01': {'host': 'host01', 'sn': 'FR8Y5X3', 'status': {'storage': {'1': {'disk.bay.8:enclosure.internal.0-1:nonraid.slot.2-1': {'description': 'Disk 8 in Backplane 1 of Storage Controller in Slot 2', 'entity': 'NonRAID.Slot.2-1', 'capacity_bytes': 20000588955136, 'model': 'ST20000NM008D-3D', 'protocol': 'SATA', 'serial_number': 'ZVT99QLL', 'status': {'health': 'OK', 'healthrollup': 'OK', 'state': 'Enabled'}, 'physical_location': {'partlocation': {'locationordinalvalue': 8, 'locationtype': 'Slot'}}}}}, 'processors': {'1': {'cpu.socket.2': {'description': 'Represents the properties of a Processor attached to this System', 'total_cores': 16, 'total_threads': 32, 'processor_type': 'CPU', 'model': 'Intel(R) Xeon(R) Silver 4314 CPU @ 2.40GHz', 'status': {'health': 'OK', 'state': 'Enabled'}, 'manufacturer': 'Intel'}, 'cpu.socket.1': {'description': 'Represents the properties of a Processor attached to this System', 'total_cores': 16, 'total_threads': 32, 'processor_type': 'CPU', 'model': 'Intel(R) Xeon(R) Silver 4314 CPU @ 2.40GHz', 'status': {'health': 'OK', 'state': 'Enabled'}, 'manufacturer': 'Intel'}}}, 'network': {'1': {'oslogicalnetwork.2': {'description': 'eno8303', 'name': 'eno8303', 'speed_mbps': 0, 'status': {'health': 'OK', 'state': 'Enabled'}}}}, 'memory': {'1': {'dimm.socket.a1': {'description': 'DIMM A1', 'memory_device_type': 'DDR4', 'capacity_mi_b': 16384, 'status': {'health': 'OK', 'state': 'Enabled'}}}}, 'power': {'1': {'0': {'name': 'PS1 Status', 'model': 'PWR SPLY,800W,RDNT,LTON', 'manufacturer': 'DELL', 'status': {'health': 'OK', 'state': 'Enabled'}}, '1': {'name': 'PS2 Status', 'model': 'PWR SPLY,800W,RDNT,LTON', 'manufacturer': 'DELL', 'status': {'health': 'OK', 'state': 'Enabled'}}}}, 'fans': {'1': {'0': {'name': 'System Board Fan1A', 'physical_context': 'SystemBoard', 'reading': 4800, 'reading_units': 'RPM', 'status': {'health': 'OK', 'state': 'Enabled'}}}}, 'temperatures': {'1': {'0': {'name': 'System Board Inlet Temp', 'physical_context': 'Intake', 'reading': 24, 'reading_units': 'Cel', 'status': {'health': 'OK', 'state': 'Enabled'}}}}}, 'firmwares': {'installed-28897-6.10.30.20__usc.embedded.1:lc.embedded.1': {'name': 'Lifecycle Controller', 'description': 'Represents Firmware Inventory', 'release_date': '00:00:00Z', 'version': '6.10.30.20', 'updateable': True, 'status': {'health': 'OK', 'state': 'Enabled'}}}}, 'host02': {'host': 'host02', 'sn': 'FR8Y5X4', 'status': {'storage': {'1': {'disk.bay.8:enclosure.internal.0-1:nonraid.slot.2-1': {'description': 'Disk 8 in Backplane 1 of Storage Controller in Slot 2', 'entity': 'NonRAID.Slot.2-1', 'capacity_bytes': 20000588955136, 'model': 'ST20000NM008D-3D', 'protocol': 'SATA', 'serial_number': 'ZVT99QLL', 'status': {'health': 'OK', 'healthrollup': 'OK', 'state': 'Enabled'}, 'physical_location': {'partlocation': {'locationordinalvalue': 8, 'locationtype': 'Slot'}}}}}, 'processors': {'1': {'cpu.socket.2': {'description': 'Represents the properties of a Processor attached to this System', 'total_cores': 16, 'total_threads': 32, 'processor_type': 'CPU', 'model': 'Intel(R) Xeon(R) Silver 4314 CPU @ 2.40GHz', 'status': {'health': 'OK', 'state': 'Enabled'}, 'manufacturer': 'Intel'}, 'cpu.socket.1': {'description': 'Represents the properties of a Processor attached to this System', 'total_cores': 16, 'total_threads': 32, 'processor_type': 'CPU', 'model': 'Intel(R) Xeon(R) Silver 4314 CPU @ 2.40GHz', 'status': {'health': 'OK', 'state': 'Enabled'}, 'manufacturer': 'Intel'}}}, 'network': {'1': {'oslogicalnetwork.2': {'description': 'eno8303', 'name': 'eno8303', 'speed_mbps': 0, 'status': {'health': 'OK', 'state': 'Enabled'}}}}, 'memory': {'1': {'dimm.socket.a1': {'description': 'DIMM A1', 'memory_device_type': 'DDR4', 'capacity_mi_b': 16384, 'status': {'health': 'OK', 'state': 'Enabled'}}}}, 'power': {'1': {'0': {'name': 'PS1 Status', 'model': 'PWR SPLY,800W,RDNT,LTON', 'manufacturer': 'DELL', 'status': {'health': 'OK', 'state': 'Enabled'}}, '1': {'name': 'PS2 Status', 'model': 'PWR SPLY,800W,RDNT,LTON', 'manufacturer': 'DELL', 'status': {'health': 'OK', 'state': 'Enabled'}}}}, 'fans': {'1': {'0': {'name': 'System Board Fan1A', 'physical_context': 'SystemBoard', 'reading': 4800, 'reading_units': 'RPM', 'status': {'health': 'OK', 'state': 'Enabled'}}}}, 'temperatures': {'1': {'0': {'name': 'System Board Inlet Temp', 'physical_context': 'Intake', 'reading': 24, 'reading_units': 'Cel', 'status': {'health': 'OK', 'state': 'Enabled'}}}}}, 'firmwares': {'installed-28897-6.10.30.20__usc.embedded.1:lc.embedded.1': {'name': 'Lifecycle Controller', 'description': 'Represents Firmware Inventory', 'release_date': '00:00:00Z', 'version': '6.10.30.20', 'updateable': True, 'status': {'health': 'OK', 'state': 'Enabled'}}}}}
+full_set = {'host01': {'host': 'host01', 'sn': 'FR8Y5X3', 'status': {'storage': {'1': {'disk.bay.8:enclosure.internal.0-1:nonraid.slot.2-1': {'description': 'Disk 8 in Backplane 1 of Storage Controller in Slot 2', 'entity': 'NonRAID.Slot.2-1', 'capacity_bytes': 20000588955136, 'model': 'ST20000NM008D-3D', 'protocol': 'SATA', 'serial_number': 'ZVT99QLL', 'status': {'health': 'OK', 'healthrollup': 'OK', 'state': 'Enabled'}, 'physical_location': {'partlocation': {'locationordinalvalue': 8, 'locationtype': 'Slot'}}}}}, 'processors': {'1': {'cpu.socket.2': {'description': 'Represents the properties of a Processor attached to this System', 'total_cores': 16, 'total_threads': 32, 'processor_type': 'CPU', 'model': 'Intel(R) Xeon(R) Silver 4314 CPU @ 2.40GHz', 'status': {'health': 'OK', 'state': 'Enabled'}, 'manufacturer': 'Intel'}, 'cpu.socket.1': {'description': 'Represents the properties of a Processor attached to this System', 'total_cores': 16, 'total_threads': 32, 'processor_type': 'CPU', 'model': 'Intel(R) Xeon(R) Silver 4314 CPU @ 2.40GHz', 'status': {'health': 'OK', 'state': 'Enabled'}, 'manufacturer': 'Intel'}}}, 'network': {'1': {'oslogicalnetwork.2': {'description': 'eno8303', 'name': 'eno8303', 'speed_mbps': 0, 'status': {'health': 'OK', 'state': 'Enabled'}}}}, 'memory': {'1': {'dimm.socket.a1': {'description': 'DIMM A1', 'memory_device_type': 'DDR4', 'capacity_mi_b': 16384, 'status': {'health': 'OK', 'state': 'Enabled'}}}}, 'power': {'1': {'0': {'name': 'PS1 Status', 'model': 'PWR SPLY,800W,RDNT,LTON', 'manufacturer': 'DELL', 'status': {'health': 'OK', 'state': 'Enabled'}}, '1': {'name': 'PS2 Status', 'model': 'PWR SPLY,800W,RDNT,LTON', 'manufacturer': 'DELL', 'status': {'health': 'OK', 'state': 'Enabled'}}}}, 'fans': {'1': {'0': {'name': 'System Board Fan1A', 'physical_context': 'SystemBoard', 'reading': 4800, 'reading_units': 'RPM', 'status': {'health': 'OK', 'state': 'Enabled'}}}}, 'temperatures': {'1': {'0': {'name': 'System Board Inlet Temp', 'physical_context': 'Intake', 'reading': 24, 'reading_units': 'Cel', 'status': {'health': 'OK', 'state': 'Enabled'}}}}}, 'firmware': {'installed-28897-6.10.30.20__usc.embedded.1:lc.embedded.1': {'name': 'Lifecycle Controller', 'description': 'Represents Firmware Inventory', 'release_date': '00:00:00Z', 'version': '6.10.30.20', 'updateable': True, 'status': {'health': 'OK', 'state': 'Enabled'}}}}, 'host02': {'host': 'host02', 'sn': 'FR8Y5X4', 'status': {'storage': {'1': {'disk.bay.8:enclosure.internal.0-1:nonraid.slot.2-1': {'description': 'Disk 8 in Backplane 1 of Storage Controller in Slot 2', 'entity': 'NonRAID.Slot.2-1', 'capacity_bytes': 20000588955136, 'model': 'ST20000NM008D-3D', 'protocol': 'SATA', 'serial_number': 'ZVT99QLL', 'status': {'health': 'OK', 'healthrollup': 'OK', 'state': 'Enabled'}, 'physical_location': {'partlocation': {'locationordinalvalue': 8, 'locationtype': 'Slot'}}}}}, 'processors': {'1': {'cpu.socket.2': {'description': 'Represents the properties of a Processor attached to this System', 'total_cores': 16, 'total_threads': 32, 'processor_type': 'CPU', 'model': 'Intel(R) Xeon(R) Silver 4314 CPU @ 2.40GHz', 'status': {'health': 'OK', 'state': 'Enabled'}, 'manufacturer': 'Intel'}, 'cpu.socket.1': {'description': 'Represents the properties of a Processor attached to this System', 'total_cores': 16, 'total_threads': 32, 'processor_type': 'CPU', 'model': 'Intel(R) Xeon(R) Silver 4314 CPU @ 2.40GHz', 'status': {'health': 'OK', 'state': 'Enabled'}, 'manufacturer': 'Intel'}}}, 'network': {'1': {'oslogicalnetwork.2': {'description': 'eno8303', 'name': 'eno8303', 'speed_mbps': 0, 'status': {'health': 'OK', 'state': 'Enabled'}}}}, 'memory': {'1': {'dimm.socket.a1': {'description': 'DIMM A1', 'memory_device_type': 'DDR4', 'capacity_mi_b': 16384, 'status': {'health': 'OK', 'state': 'Enabled'}}}}, 'power': {'1': {'0': {'name': 'PS1 Status', 'model': 'PWR SPLY,800W,RDNT,LTON', 'manufacturer': 'DELL', 'status': {'health': 'OK', 'state': 'Enabled'}}, '1': {'name': 'PS2 Status', 'model': 'PWR SPLY,800W,RDNT,LTON', 'manufacturer': 'DELL', 'status': {'health': 'OK', 'state': 'Enabled'}}}}, 'fans': {'1': {'0': {'name': 'System Board Fan1A', 'physical_context': 'SystemBoard', 'reading': 4800, 'reading_units': 'RPM', 'status': {'health': 'OK', 'state': 'Enabled'}}}}, 'temperatures': {'1': {'0': {'name': 'System Board Inlet Temp', 'physical_context': 'Intake', 'reading': 24, 'reading_units': 'Cel', 'status': {'health': 'OK', 'state': 'Enabled'}}}}}, 'firmware': {'installed-28897-6.10.30.20__usc.embedded.1:lc.embedded.1': {'name': 'Lifecycle Controller', 'description': 'Represents Firmware Inventory', 'release_date': '00:00:00Z', 'version': '6.10.30.20', 'updateable': True, 'status': {'health': 'OK', 'state': 'Enabled'}}}}}
index dd6593fd273682ba2fff410578ec87901a7638a6..ba311b19421ed5a418886bc6e331a65aaac9677e 100644 (file)
@@ -331,14 +331,18 @@ class TestNodeProxyEndpoint(helper.CPWebCase):
         self.getPage("/host03/temperatures", method="GET")
         self.assertStatus('404 Not Found')
 
-    def test_firmwares_with_valid_hostname(self):
-        self.getPage("/host02/firmwares", method="GET")
+    def test_firmware_with_valid_hostname(self):
+        self.getPage("/host02/firmware", method="GET")
         self.assertStatus('200 OK')
 
-    def test_firmwares_no_hostname(self):
-        self.getPage("/firmwares", method="GET")
+    def test_firmware_no_hostname(self):
+        self.getPage("/firmware", method="GET")
         self.assertStatus('200 OK')
 
-    def test_firmwares_with_invalid_hostname(self):
-        self.getPage("/host03/firmwares", method="GET")
+    def test_firmware_with_invalid_hostname(self):
+        self.getPage("/host03/firmware", method="GET")
         self.assertStatus('404 Not Found')
+
+    def test_firmwares_legacy_endpoint(self):
+        self.getPage("/host02/firmwares", method="GET")
+        self.assertStatus('200 OK')
index a17d20f0961c11d098f63a3f7be73e96122acf47..1266b84df55cd868cc36678f1dd8488d75da8915 100644 (file)
@@ -367,9 +367,17 @@ class Orchestrator(object):
         """
         raise NotImplementedError()
 
+    def node_proxy_firmware(self, hostname: Optional[str] = None) -> OrchResult[Dict[str, Any]]:
+        """
+        Return node-proxy firmware report
+
+        :param hostname: hostname
+        """
+        raise NotImplementedError()
+
     def node_proxy_firmwares(self, hostname: Optional[str] = None) -> OrchResult[Dict[str, Any]]:
         """
-        Return node-proxy firmwares report
+        Return node-proxy firmware report (deprecated alias)
 
         :param hostname: hostname
         """
index 0930d3f267b2fd2702bea68a118a056c10ac10f8..c602454961819bdac2302b50871d76caeeb531d1 100644 (file)
@@ -527,7 +527,7 @@ class OrchestratorCli(OrchestratorClientMixin, MgrModule):
         table_heading_mapping = {
             'summary': ['HOST', 'SN', 'STORAGE', 'CPU', 'NET', 'MEMORY', 'POWER', 'FANS'],
             'fullreport': [],
-            'firmwares': ['HOST', 'COMPONENT', 'NAME', 'DATE', 'VERSION', 'STATUS'],
+            'firmware': ['HOST', 'COMPONENT', 'NAME', 'DATE', 'VERSION', 'STATUS'],
             'criticals': ['HOST', 'SYS_ID', 'COMPONENT', 'NAME', 'STATUS', 'STATE'],
             'memory': ['HOST', 'SYS_ID', 'NAME', 'STATUS', 'STATE'],
             'storage': ['HOST', 'SYS_ID', 'NAME', 'MODEL', 'SIZE', 'PROTOCOL', 'SN', 'SLOT', 'FW', 'STATUS', 'STATE'],
@@ -538,6 +538,9 @@ class OrchestratorCli(OrchestratorClientMixin, MgrModule):
             'temperatures': ['HOST', 'CHASSIS_ID', 'ID', 'NAME', 'READING', 'UNITS', 'STATUS', 'STATE'],
         }
 
+        if category == 'firmwares':
+            category = 'firmware'
+
         if category not in table_heading_mapping.keys():
             return HandleCommandResult(stdout=f"'{category}' is not a valid category.")
 
@@ -567,8 +570,8 @@ class OrchestratorCli(OrchestratorClientMixin, MgrModule):
                 completion = self.node_proxy_fullreport(hostname=hostname)
                 fullreport: Dict[str, Any] = raise_if_exception(completion)
                 output = json.dumps(fullreport)
-        elif category == 'firmwares':
-            output = 'Missing host name' if hostname is None else self._firmwares_table(hostname, table, format)
+        elif category == 'firmware':
+            output = 'Missing host name' if hostname is None else self._firmware_table(hostname, table, format)
         elif category == 'criticals':
             output = self._criticals_table(hostname, table, format)
         else:
@@ -576,8 +579,8 @@ class OrchestratorCli(OrchestratorClientMixin, MgrModule):
 
         return HandleCommandResult(stdout=output)
 
-    def _firmwares_table(self, hostname: Optional[str], table: PrettyTable, format: Format) -> str:
-        completion = self.node_proxy_firmwares(hostname=hostname)
+    def _firmware_table(self, hostname: Optional[str], table: PrettyTable, format: Format) -> str:
+        completion = self.node_proxy_firmware(hostname=hostname)
         data = raise_if_exception(completion)
         # data = self.node_proxy_firmware(hostname=hostname)
         if format == Format.json: