LCOV - code coverage report
Current view: top level - pkg/networkmanager - team.jsx Coverage Total Hit
Test: cockpit Lines: 85.3 % 177 151
Test Date: 2026-07-17 14:32:59

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

Generated by: LCOV version 2.0-1