LCOV - code coverage report
Current view: top level - pkg/systemd/overview-cards - smart-status.jsx Coverage Total Hit
Test: cockpit Lines: 86.9 % 61 53
Test Date: 2026-07-17 07:33:01

            Line data    Source code
       1              : /*
       2              :  * Copyright (C) 2025 Red Hat, Inc.
       3              :  * SPDX-License-Identifier: LGPL-2.1-or-later
       4              :  */
       5              : 
       6           92 : import React, { useState } from 'react';
       7              : 
       8              : import { Button } from "@patternfly/react-core/dist/esm/components/Button/index.js";
       9              : import { ExclamationCircleIcon } from "@patternfly/react-icons";
      10              : import { Flex } from "@patternfly/react-core/dist/esm/layouts/Flex/index.js";
      11              : import { Icon } from '@patternfly/react-core/dist/esm/components/Icon/index.js';
      12              : 
      13           92 : import cockpit from "cockpit";
      14              : import { useEvent, useInit } from 'hooks';
      15              : 
      16           76 : function countFailingDisks(proxies) {
      17           76 :     const sataFail = Object.keys(proxies.drives_ata).reduce((acc, drive) => {
      18           76 :         const smart = proxies.drives_ata[drive];
      19           15 :         if (smart.SmartFailing) {
      20           15 :             return acc + 1;
      21           15 :         } else {
      22           76 :             return acc;
      23           76 :         }
      24           76 :     }, 0);
      25              : 
      26            0 :     const nvmeFail = Object.keys(proxies.nvme_controller).reduce((acc, drive) => {
      27            0 :         const smart = proxies.nvme_controller[drive];
      28            0 :         if (smart.SmartCriticalWarning.length > 0) {
      29            0 :             return acc + 1;
      30            0 :         } else {
      31            0 :             return acc;
      32            0 :         }
      33            0 :     }, 0);
      34              : 
      35           76 :     return sataFail + nvmeFail;
      36           76 : }
      37              : 
      38           92 : const proxies = {
      39           92 :     drives_ata: null,
      40           92 :     nvme_controller: null,
      41           92 : };
      42              : 
      43           92 : export const SmartOverviewStatus = () => {
      44           92 :     useEvent(proxies.drives_ata, "changed");
      45           92 :     useEvent(proxies.nvme_controller, "changed");
      46           92 :     const [initDone, setInitDone] = useState(false);
      47              : 
      48           92 :     useInit(async () => {
      49           92 :         const udisksdbus = cockpit.dbus("org.freedesktop.UDisks2");
      50              : 
      51           92 :         const addProxy = (iface) => {
      52           92 :             return udisksdbus.proxies("org.freedesktop.UDisks2." + iface, "/org/freedesktop/UDisks2");
      53           92 :         };
      54              : 
      55           92 :         proxies.drives_ata = addProxy("Drive.Ata");
      56           92 :         proxies.nvme_controller = addProxy("NVMe.Controller");
      57           92 :         await Promise.all(Object.keys(proxies).map(proxy => proxies[proxy].wait()));
      58              : 
      59           77 :         setInitDone(true);
      60           92 :     });
      61              : 
      62           77 :     if (initDone === false || proxies.drives_ata === null || proxies.nvme_controller === null) {
      63           92 :         return null;
      64           92 :     }
      65              : 
      66           77 :     const failingDisks = countFailingDisks(proxies);
      67           77 :     if (failingDisks === 0) {
      68           77 :         return null;
      69           77 :     }
      70              : 
      71           17 :     return (
      72           17 :         <li id="smart-status">
      73           17 :             <Flex spaceItems={{ default: 'spaceItemsSm' }}>
      74           17 :                 <Icon status="danger">
      75           17 :                     <ExclamationCircleIcon />
      76           17 :                 </Icon>
      77           17 :                 <Button variant="link" component="a" isInline
      78            1 :                     onClick={() => cockpit.jump("/storage")}
      79              :                 >
      80           17 :                     {cockpit.format(cockpit.ngettext("$0 disk is failing",
      81           17 :                                                      "$0 disks are failing", failingDisks),
      82           17 :                                     failingDisks)}
      83           17 :                 </Button>
      84           17 :             </Flex>
      85           17 :         </li>
      86              :     );
      87           92 : };
        

Generated by: LCOV version 2.0-1