LCOV - code coverage report
Current view: top level - pkg/networkmanager - team.jsx Coverage Total Hit
Test: cockpit Lines: 12.4 % 177 22
Test Date: 2026-07-02 14:11:36

            Line data    Source code
       1              : /*
       2              :  * Copyright (C) 2021 Red Hat, Inc.
       3              :  * SPDX-License-Identifier: LGPL-2.1-or-later
       4              :  */
       5              : 
       6            1 : import React, { useState, useContext } from 'react';
       7            1 : import cockpit from 'cockpit';
       8              : import { FormGroup } from "@patternfly/react-core/dist/esm/components/Form/index.js";
       9              : import { FormSelect, FormSelectOption } from "@patternfly/react-core/dist/esm/components/FormSelect/index.js";
      10              : import { TextInput } from "@patternfly/react-core/dist/esm/components/TextInput/index.js";
      11              : 
      12              : import { MemberInterfaceChoices, NetworkModal, Name, dialogSave } from './dialogs-common.jsx';
      13              : import { ModelContext } from './model-context.jsx';
      14              : import { useDialogs } from "dialogs.jsx";
      15              : 
      16              : import { v4 as uuidv4 } from 'uuid';
      17              : import {
      18              :     member_connection_for_interface,
      19              :     member_interface_choices,
      20              : } from './interfaces.js';
      21              : 
      22            1 : const _ = cockpit.gettext;
      23              : 
      24            1 : export const team_runner_choices =
      25            1 :     [
      26            1 :         { choice: 'roundrobin', title: _("Round robin") },
      27            1 :         { choice: 'activebackup', title: _("Active backup") },
      28            1 :         { choice: 'loadbalance', title: _("Load balancing") },
      29            1 :         { choice: 'broadcast', title: _("Broadcast") },
      30            1 :         { choice: 'lacp', title: _("802.3ad LACP") },
      31            1 :     ];
      32              : 
      33            1 : export const team_balancer_choices =
      34            1 :     [
      35            1 :         { choice: 'none', title: _("Passive") },
      36            1 :         { choice: 'basic', title: _("Active") }
      37            1 :     ];
      38              : 
      39            1 : export const team_watch_choices =
      40            1 :     [
      41            1 :         { choice: 'ethtool', title: _("Ethtool") },
      42            1 :         { choice: 'arp-ping', title: _("ARP ping") },
      43            1 :         { choice: 'nsna-ping', title: _("NSNA ping") }
      44            1 :     ];
      45              : 
      46            0 : export const TeamDialog = ({ connection, dev, settings }) => {
      47            0 :     const Dialogs = useDialogs();
      48            0 :     const idPrefix = "network-team-settings";
      49            0 :     const model = useContext(ModelContext);
      50            0 :     const config = settings.team.config || {};
      51            0 :     if (!config.runner)
      52            0 :         config.runner = { };
      53            0 :     if (!config.runner.name)
      54            0 :         config.runner.name = "activebackup";
      55            0 :     if (!config.link_watch)
      56            0 :         config.link_watch = { };
      57            0 :     if (!config.link_watch.name)
      58            0 :         config.link_watch.name = "ethtool";
      59            0 :     if (config.link_watch.interval === undefined)
      60            0 :         config.link_watch.interval = 100;
      61            0 :     if (config.link_watch.delay_up === undefined)
      62            0 :         config.link_watch.delay_up = 0;
      63            0 :     if (config.link_watch.delay_down === undefined)
      64            0 :         config.link_watch.delay_down = 0;
      65              : 
      66            0 :     const memberChoicesInit = {};
      67              : 
      68            0 :     member_interface_choices(model, connection).forEach((iface) => {
      69            0 :         memberChoicesInit[iface.Name] = !!member_connection_for_interface(connection, iface);
      70            0 :     });
      71              : 
      72            0 :     const [balancer, setBalancer] = useState(config.balancer);
      73            0 :     const [dialogError, setDialogError] = useState(undefined);
      74            0 :     const [iface, setIface] = useState(settings.connection.interface_name);
      75            0 :     const [linkDownDelay, setLinkDownDelay] = useState(config.link_watch.delay_down);
      76            0 :     const [linkWatch, setLinkWatch] = useState(config.link_watch.name);
      77            0 :     const [linkUpDelay, setLinkUpDelay] = useState(config.link_watch.delay_up);
      78            0 :     const [memberChoices, setMemberChoices] = useState(memberChoicesInit);
      79            0 :     const [runner, setRunner] = useState(config.runner.name);
      80            0 :     const [pingTarget, setPingTarget] = useState(config.link_watch.target_host);
      81            0 :     const [pingInterval, setPingInterval] = useState(config.link_watch.interval);
      82            0 :     const [primary, setPrimary] = useState(undefined);
      83              : 
      84            0 :     const onSubmit = (ev) => {
      85            0 :         const createSettingsObj = () => ({
      86            0 :             ...settings,
      87            0 :             connection: {
      88            0 :                 ...settings.connection,
      89            0 :                 id: iface,
      90            0 :                 interface_name: iface,
      91            0 :             },
      92            0 :             team: {
      93            0 :                 ...settings.team,
      94            0 :                 interface_name: iface,
      95            0 :                 config: {
      96            0 :                     ...settings.team.config,
      97            0 :                     link_watch: {
      98            0 :                         name: linkWatch,
      99            0 :                         ...(linkWatch == 'ethtool' && {
     100            0 :                             delay_up: linkUpDelay,
     101            0 :                             delay_down: linkDownDelay,
     102            0 :                         }),
     103            0 :                         ...(linkWatch != 'ethtool' && {
     104            0 :                             interval: pingInterval,
     105            0 :                             target_host: pingTarget,
     106            0 :                         }),
     107            0 :                     },
     108            0 :                     runner: {
     109            0 :                         ...config.runner,
     110            0 :                         name: runner,
     111            0 :                         ...((runner == "loadbalance" || runner == "lacp") && { tx_balancer: balancer == 'none' ? {} : balancer })
     112            0 :                     }
     113            0 :                 }
     114            0 :             }
     115            0 :         });
     116              : 
     117            0 :         dialogSave({
     118            0 :             model,
     119            0 :             dev,
     120            0 :             connection,
     121            0 :             members: memberChoices,
     122            0 :             membersInit: memberChoicesInit,
     123            0 :             settings: createSettingsObj(),
     124            0 :             setDialogError,
     125            0 :             onClose: Dialogs.close,
     126            0 :         });
     127              : 
     128              :         // Prevent dialog from closing because of <form> onsubmit event
     129            0 :         if (ev)
     130            0 :             ev.preventDefault();
     131              : 
     132            0 :         return false;
     133            0 :     };
     134              : 
     135            0 :     return (
     136            0 :         <NetworkModal dialogError={dialogError}
     137            0 :                       idPrefix={idPrefix}
     138            0 :                       onSubmit={onSubmit}
     139            0 :                       title={!connection ? _("Add team") : _("Edit team settings")}
     140            0 :                       isCreateDialog={!connection}
     141              :         >
     142            0 :             <>
     143            0 :                 <Name idPrefix={idPrefix} iface={iface} setIface={setIface} />
     144            0 :                 <FormGroup label={_("Ports")} fieldId={idPrefix + "-interface-members-list"} hasNoPaddingTop>
     145            0 :                     <MemberInterfaceChoices idPrefix={idPrefix} memberChoices={memberChoices} setMemberChoices={setMemberChoices} model={model} group={connection} />
     146            0 :                 </FormGroup>
     147            0 :                 <FormGroup fieldId={idPrefix + "-runner-select"} label={_("Runner")}>
     148            0 :                     <FormSelect id={idPrefix + "-runner-select"} onChange={(_, val) => setRunner(val)}
     149            0 :                                 value={runner}>
     150            0 :                         {team_runner_choices.map(choice => <FormSelectOption value={choice.choice} label={choice.title} key={choice.choice} />)}
     151            0 :                     </FormSelect>
     152            0 :                 </FormGroup>
     153            0 :                 {(runner == "loadbalance" || runner == "lacp") && <FormGroup fieldId={idPrefix + "-balancer-select"} label={_("Balancer")}>
     154            0 :                     <FormSelect id={idPrefix + "-balancer-select"} onChange={(_, val) => setBalancer(val)}
     155            0 :                                 value={balancer}>
     156            0 :                         {team_balancer_choices.map(choice => <FormSelectOption value={choice.choice} label={choice.title} key={choice.choice} />)}
     157            0 :                     </FormSelect>
     158            0 :                 </FormGroup>}
     159            0 :                 {runner == "activebackup" && <FormGroup fieldId={idPrefix + "-primary-select"} label={_("Primary")}>
     160            0 :                     <FormSelect id={idPrefix + "-primary-select"} onChange={(_, val) => setPrimary(val)}
     161            0 :                                 value={primary}>
     162            0 :                         <>
     163            0 :                             <FormSelectOption key='-' value={null} label='-' />
     164            0 :                             {Object.keys(memberChoices)
     165            0 :                                     .filter(iface => memberChoices[iface])
     166            0 :                                     .map(iface => <FormSelectOption key={iface} label={iface} value={iface} />)}
     167            0 :                         </>
     168            0 :                     </FormSelect>
     169            0 :                 </FormGroup>}
     170            0 :                 <FormGroup fieldId={idPrefix + "-link-watch-select"} label={_("Link watch")}>
     171            0 :                     <FormSelect id={idPrefix + "-link-watch-select"} onChange={(_, val) => setLinkWatch(val)}
     172            0 :                                 value={linkWatch}>
     173            0 :                         {team_watch_choices.map(choice => <FormSelectOption value={choice.choice} label={choice.title} key={choice.choice} />)}
     174            0 :                     </FormSelect>
     175            0 :                 </FormGroup>
     176            0 :                 {linkWatch == 'ethtool' && <>
     177            0 :                     <FormGroup fieldId={idPrefix + "-link-up-delay-input"} label={_("Link up delay")}>
     178            0 :                         <TextInput id={idPrefix + "-link-up-delay-input"} className="network-number-field" value={linkUpDelay} onChange={(_event, value) => setLinkUpDelay(value)} />
     179            0 :                     </FormGroup>
     180            0 :                     <FormGroup fieldId={idPrefix + "-link-down-delay-input"} label={_("Link down delay")}>
     181            0 :                         <TextInput id={idPrefix + "-link-down-delay-input"} className="network-number-field" value={linkDownDelay} onChange={(_event, value) => setLinkDownDelay(value)} />
     182            0 :                     </FormGroup>
     183            0 :                 </>}
     184            0 :                 {linkWatch != 'ethtool' && <>
     185            0 :                     <FormGroup fieldId={idPrefix + "-ping-interval-input"} label={_("Ping interval")}>
     186            0 :                         <TextInput id={idPrefix + "-ping-interval-input"} className="network-number-field" value={pingInterval} onChange={(_event, value) => setPingInterval(value)} />
     187            0 :                     </FormGroup>
     188            0 :                     <FormGroup fieldId={idPrefix + "-ping-target-input"} label={_("Ping target")}>
     189            0 :                         <TextInput id={idPrefix + "-ping-target-input"} value={pingTarget} onChange={(_event, value) => setPingTarget(value)} />
     190            0 :                     </FormGroup>
     191            0 :                 </>}
     192            0 :             </>
     193            0 :         </NetworkModal>
     194              :     );
     195            0 : };
     196              : 
     197            0 : export const getGhostSettings = ({ newIfaceName }) => {
     198            0 :     return (
     199            0 :         {
     200            0 :             connection: {
     201            0 :                 id: newIfaceName,
     202            0 :                 autoconnect: true,
     203            0 :                 type: "team",
     204            0 :                 uuid: uuidv4(),
     205            0 :                 interface_name: newIfaceName,
     206            0 :             },
     207            0 :             team: {
     208            0 :                 config: {},
     209            0 :                 interface_name: newIfaceName
     210            0 :             }
     211            0 :         }
     212              :     );
     213            0 : };
        

Generated by: LCOV version 2.0-1