🏡 index : quine-global-labs/mirror-cockpit-project-cockpit.git

/*
 * Copyright (C) 2021 Red Hat, Inc.
 * SPDX-License-Identifier: LGPL-2.1-or-later
 */

import React, { useState, useContext } from 'react';
import cockpit from 'cockpit';
import { FormGroup } from "@patternfly/react-core/dist/esm/components/Form/index.js";
import { FormSelect, FormSelectOption } from "@patternfly/react-core/dist/esm/components/FormSelect/index.js";
import { TextInput } from "@patternfly/react-core/dist/esm/components/TextInput/index.js";

import { MemberInterfaceChoices, NetworkModal, Name, dialogSave } from './dialogs-common.jsx';
import { ModelContext } from './model-context.jsx';
import { useDialogs } from "dialogs.jsx";

import { v4 as uuidv4 } from 'uuid';
import {
    member_connection_for_interface,
    member_interface_choices,
} from './interfaces.js';

const _ = cockpit.gettext;

export const team_runner_choices =
    [
        { choice: 'roundrobin', title: _("Round robin") },
        { choice: 'activebackup', title: _("Active backup") },
        { choice: 'loadbalance', title: _("Load balancing") },
        { choice: 'broadcast', title: _("Broadcast") },
        { choice: 'lacp', title: _("802.3ad LACP") },
    ];

export const team_balancer_choices =
    [
        { choice: 'none', title: _("Passive") },
        { choice: 'basic', title: _("Active") }
    ];

export const team_watch_choices =
    [
        { choice: 'ethtool', title: _("Ethtool") },
        { choice: 'arp-ping', title: _("ARP ping") },
        { choice: 'nsna-ping', title: _("NSNA ping") }
    ];

export const TeamDialog = ({ connection, dev, settings }) => {
    const Dialogs = useDialogs();
    const idPrefix = "network-team-settings";
    const model = useContext(ModelContext);
    const config = settings.team.config || {};
    if (!config.runner)
        config.runner = { };
    if (!config.runner.name)
        config.runner.name = "activebackup";
    if (!config.link_watch)
        config.link_watch = { };
    if (!config.link_watch.name)
        config.link_watch.name = "ethtool";
    if (config.link_watch.interval === undefined)
        config.link_watch.interval = 100;
    if (config.link_watch.delay_up === undefined)
        config.link_watch.delay_up = 0;
    if (config.link_watch.delay_down === undefined)
        config.link_watch.delay_down = 0;

    const memberChoicesInit = {};

    member_interface_choices(model, connection).forEach((iface) => {
        memberChoicesInit[iface.Name] = !!member_connection_for_interface(connection, iface);
    });

    const [balancer, setBalancer] = useState(config.balancer);
    const [dialogError, setDialogError] = useState(undefined);
    const [iface, setIface] = useState(settings.connection.interface_name);
    const [linkDownDelay, setLinkDownDelay] = useState(config.link_watch.delay_down);
    const [linkWatch, setLinkWatch] = useState(config.link_watch.name);
    const [linkUpDelay, setLinkUpDelay] = useState(config.link_watch.delay_up);
    const [memberChoices, setMemberChoices] = useState(memberChoicesInit);
    const [runner, setRunner] = useState(config.runner.name);
    const [pingTarget, setPingTarget] = useState(config.link_watch.target_host);
    const [pingInterval, setPingInterval] = useState(config.link_watch.interval);
    const [primary, setPrimary] = useState(undefined);

    const onSubmit = (ev) => {
        const createSettingsObj = () => ({
            ...settings,
            connection: {
                ...settings.connection,
                id: iface,
                interface_name: iface,
            },
            team: {
                ...settings.team,
                interface_name: iface,
                config: {
                    ...settings.team.config,
                    link_watch: {
                        name: linkWatch,
                        ...(linkWatch === 'ethtool' && {
                            delay_up: linkUpDelay,
                            delay_down: linkDownDelay,
                        }),
                        ...(linkWatch !== 'ethtool' && {
                            interval: pingInterval,
                            target_host: pingTarget,
                        }),
                    },
                    runner: {
                        ...config.runner,
                        name: runner,
                        ...((runner === "loadbalance" || runner === "lacp") && { tx_balancer: balancer === 'none' ? {} : balancer })
                    }
                }
            }
        });

        dialogSave({
            model,
            dev,
            connection,
            members: memberChoices,
            membersInit: memberChoicesInit,
            settings: createSettingsObj(),
            setDialogError,
            onClose: Dialogs.close,
        });

        // Prevent dialog from closing because of <form> onsubmit event
        if (ev)
            ev.preventDefault();

        return false;
    };

    return (
        <NetworkModal dialogError={dialogError}
                      idPrefix={idPrefix}
                      onSubmit={onSubmit}
                      title={!connection ? _("Add team") : _("Edit team settings")}
                      isCreateDialog={!connection}
        >
            <>
                <Name idPrefix={idPrefix} iface={iface} setIface={setIface} />
                <FormGroup label={_("Ports")} fieldId={idPrefix + "-interface-members-list"} hasNoPaddingTop>
                    <MemberInterfaceChoices idPrefix={idPrefix} memberChoices={memberChoices} setMemberChoices={setMemberChoices} model={model} group={connection} />
                </FormGroup>
                <FormGroup fieldId={idPrefix + "-runner-select"} label={_("Runner")}>
                    <FormSelect id={idPrefix + "-runner-select"} onChange={(_, val) => setRunner(val)}
                                value={runner}>
                        {team_runner_choices.map(choice => <FormSelectOption value={choice.choice} label={choice.title} key={choice.choice} />)}
                    </FormSelect>
                </FormGroup>
                {(runner === "loadbalance" || runner === "lacp") && <FormGroup fieldId={idPrefix + "-balancer-select"} label={_("Balancer")}>
                    <FormSelect id={idPrefix + "-balancer-select"} onChange={(_, val) => setBalancer(val)}
                                value={balancer}>
                        {team_balancer_choices.map(choice => <FormSelectOption value={choice.choice} label={choice.title} key={choice.choice} />)}
                    </FormSelect>
                </FormGroup>}
                {runner === "activebackup" && <FormGroup fieldId={idPrefix + "-primary-select"} label={_("Primary")}>
                    <FormSelect id={idPrefix + "-primary-select"} onChange={(_, val) => setPrimary(val)}
                                value={primary}>
                        <>
                            <FormSelectOption key='-' value={null} label='-' />
                            {Object.keys(memberChoices)
                                    .filter(iface => memberChoices[iface])
                                    .map(iface => <FormSelectOption key={iface} label={iface} value={iface} />)}
                        </>
                    </FormSelect>
                </FormGroup>}
                <FormGroup fieldId={idPrefix + "-link-watch-select"} label={_("Link watch")}>
                    <FormSelect id={idPrefix + "-link-watch-select"} onChange={(_, val) => setLinkWatch(val)}
                                value={linkWatch}>
                        {team_watch_choices.map(choice => <FormSelectOption value={choice.choice} label={choice.title} key={choice.choice} />)}
                    </FormSelect>
                </FormGroup>
                {linkWatch === 'ethtool' && <>
                    <FormGroup fieldId={idPrefix + "-link-up-delay-input"} label={_("Link up delay")}>
                        <TextInput id={idPrefix + "-link-up-delay-input"} className="network-number-field" value={linkUpDelay} onChange={(_event, value) => setLinkUpDelay(value)} />
                    </FormGroup>
                    <FormGroup fieldId={idPrefix + "-link-down-delay-input"} label={_("Link down delay")}>
                        <TextInput id={idPrefix + "-link-down-delay-input"} className="network-number-field" value={linkDownDelay} onChange={(_event, value) => setLinkDownDelay(value)} />
                    </FormGroup>
                </>}
                {linkWatch !== 'ethtool' && <>
                    <FormGroup fieldId={idPrefix + "-ping-interval-input"} label={_("Ping interval")}>
                        <TextInput id={idPrefix + "-ping-interval-input"} className="network-number-field" value={pingInterval} onChange={(_event, value) => setPingInterval(value)} />
                    </FormGroup>
                    <FormGroup fieldId={idPrefix + "-ping-target-input"} label={_("Ping target")}>
                        <TextInput id={idPrefix + "-ping-target-input"} value={pingTarget} onChange={(_event, value) => setPingTarget(value)} />
                    </FormGroup>
                </>}
            </>
        </NetworkModal>
    );
};

export const getGhostSettings = ({ newIfaceName }) => {
    return (
        {
            connection: {
                id: newIfaceName,
                autoconnect: true,
                type: "team",
                uuid: uuidv4(),
                interface_name: newIfaceName,
            },
            team: {
                config: {},
                interface_name: newIfaceName
            }
        }
    );
};