Skip to content

Commit

Permalink
[INLONG-11187][Dashboard] Agent batch upgrade (#11437)
Browse files Browse the repository at this point in the history
  • Loading branch information
wohainilaodou authored Oct 30, 2024
1 parent 43f690b commit e49d69e
Show file tree
Hide file tree
Showing 10 changed files with 1,068 additions and 166 deletions.
16 changes: 16 additions & 0 deletions inlong-dashboard/src/ui/locales/cn.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"basic.ConnectionSuccess": "连接成功",
"basic.Save": "保存",
"basic.Cancel": "取消",
"basic.Confirm": "确定",
"basic.Create": "新建",
"basic.Delete": "删除",
"basic.DeleteConfirm": "确认删除吗?",
Expand Down Expand Up @@ -815,6 +816,7 @@
"pages.Clusters.Name": "集群名称",
"pages.Clusters.Tag": "集群标签",
"pages.Clusters.InCharges": "责任人",
"pages.Clusters.Node.Description": "节点描述",
"pages.Clusters.Description": "集群描述",
"pages.Clusters.TestConnection": "测试连接",
"pages.Clusters.Node.Name": "节点",
Expand Down Expand Up @@ -847,15 +849,29 @@
"pages.Clusters.Node.SSHKey": "SSH 密钥",
"pages.Clusters.Node.SSHPort": "SSH 端口",
"pages.Clusters.Node.SSHKeyHelper": "请将公钥上传至 Agent 节点的 ~/.ssh/authorized_keys 文件中",
"pages.Clusters.Node.Version": "版本",
"pages.Clusters.Node.Status": "状态",
"pages.Clusters.Node.Status.Normal": "正常",
"pages.Clusters.Node.Status.Timeout": "心跳超时",
"pages.Clusters.Node.Status.INSTALLING": "安装中",
"pages.Clusters.Node.Status.INSTALLFAILED": "安装失败",
"pages.Clusters.Node.Status.INSTALLSUCCESS": "安装成功",
"pages.Clusters.Node.LastModifier": "最后操作",
"pages.Clusters.Node.Creator": "创建人",
"pages.Clusters.Node.Create": "新建节点",
"pages.Clusters.Node.IpRule": "请输入正确的IP地址",
"pages.Clusters.Node.PortRule": "请输入正确的端口",
"pages.Clusters.Node.ProtocolTypeRule": "请输入正确的协议类型",
"pages.Clusters.Node.BatchUpdate": "批量操作",
"pages.Clusters.Node.BatchNum": "分批数",
"pages.Clusters.Node.Interval": "间隔",
"pages.Clusters.Node.Minute": "分钟",
"pages.Clusters.Node.OperationStatusQuery": "操作状态查询",
"pages.Clusters.Node.OperationType": "操作类型",
"pages.Clusters.Node.UpgradeAgentAndInstaller": "更新 Agent 和 Installer",
"pages.Clusters.Node.UpgradeAgent": "更新 Agent",
"pages.Clusters.Node.UpgradeInstaller": "更新 Installer",
"pages.Clusters.Node.RestartAgent": "重启 Agent",
"pages.Clusters.Node.Online": "在线",
"pages.Clusters.Pulsar.PulsarTenant": "默认租户",
"pages.Clusters.Pulsar.TokenPlaceholder": "如果群集配置了令牌,则为必需",
Expand Down
16 changes: 16 additions & 0 deletions inlong-dashboard/src/ui/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"basic.ConnectionSuccess": "Connection success",
"basic.Save": "Save",
"basic.Cancel": "Cancel",
"basic.Confirm": "Confirm",
"basic.Create": "Create",
"basic.Delete": "Delete",
"basic.DeleteConfirm": "Are you sure to delete?",
Expand Down Expand Up @@ -815,6 +816,7 @@
"pages.Clusters.Name": "Cluster Name",
"pages.Clusters.Tag": "Cluster Tag",
"pages.Clusters.InCharges": "Owners",
"pages.Clusters.Node.Description": "Description",
"pages.Clusters.Description": "Description",
"pages.Clusters.TestConnection": "Test connection",
"pages.Clusters.Node.Name": "Node",
Expand Down Expand Up @@ -847,15 +849,29 @@
"pages.Clusters.Node.Password": "SSH Password",
"pages.Clusters.Node.SSHPort": "SSH Port",
"pages.Clusters.Node.SSHKeyHelper": "Please upload the public key to the ~/.ssh/authorized_keys file of the Agent node",
"pages.Clusters.Node.Version":"Version",
"pages.Clusters.Node.Status": "Status",
"pages.Clusters.Node.Status.Normal": "Normal",
"pages.Clusters.Node.Status.Timeout": "Timeout",
"pages.Clusters.Node.Status.INSTALLING": "Installing",
"pages.Clusters.Node.Status.INSTALLFAILED": "Install Failed",
"pages.Clusters.Node.Status.INSTALLSUCCESS": "Install Success",
"pages.Clusters.Node.LastModifier": "Last modifier",
"pages.Clusters.Node.Creator": "Creator",
"pages.Clusters.Node.Create": "Create",
"pages.Clusters.Node.IpRule": "Please enter the IP address correctly",
"pages.Clusters.Node.PortRule": "Please enter the port address correctly",
"pages.Clusters.Node.ProtocolTypeRule": "Please enter the protocol type correctly",
"pages.Clusters.Node.BatchUpdate": "Batch Operation",
"pages.Clusters.Node.BatchNum": "Batch Num",
"pages.Clusters.Node.Interval": "Interval",
"pages.Clusters.Node.Minute": "Minute",
"pages.Clusters.Node.OperationStatusQuery": "Operation Status Query",
"pages.Clusters.Node.OperationType": "Operation Type",
"pages.Clusters.Node.UpgradeAgentAndInstaller": "Upgrade Agent and Installer",
"pages.Clusters.Node.UpgradeAgent": "Upgrade Agent",
"pages.Clusters.Node.UpgradeInstaller": "Upgrade Installer",
"pages.Clusters.Node.RestartAgent": "Restart Agent",
"pages.Clusters.Node.Online": "Online",
"pages.Clusters.Pulsar.PulsarTenant": "Default tenant",
"pages.Clusters.Pulsar.TokenPlaceholder": "Required if the cluster is configured with Token",
Expand Down
273 changes: 273 additions & 0 deletions inlong-dashboard/src/ui/pages/Clusters/AgentBatchUpdateModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,273 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

import React, { useMemo, useState } from 'react';
import { Button, message, Modal } from 'antd';
import { ModalProps } from 'antd/es/modal';
import i18n from '@/i18n';
import FormGenerator, { useForm } from '@/ui/components/FormGenerator';
import dayjs, { Dayjs } from 'dayjs';

export interface Props extends ModalProps {
agentList?: [];
agentTotal?: number;
parentId?: number;
openStatusModal?: () => void;
getArgs?: (args) => void;
}

const Comp: React.FC<Props> = ({ agentList, agentTotal, parentId, ...modalProps }) => {
const [form] = useForm();
const content = () => [
{
type: 'inputnumber',
label: i18n.t('pages.Clusters.Node.BatchNum'),
name: 'batchNum',
initialValue: 2,
rules: [
{
required: true,
},
],
},
{
type: 'inputnumber',
label: i18n.t('pages.Clusters.Node.Interval'),
name: 'interval',
initialValue: 5,
rules: [
{
required: true,
},
],
suffix: i18n.t('pages.Clusters.Node.Minute'),
},
{
type: 'radio',
label: i18n.t('pages.Clusters.Node.OperationType'),
name: 'operationType',
initialValue: 0,
rules: [{ required: true }],
props: {
style: {
display: 'flex',
flexDirection: 'column',
justifyContent: 'start',
alignItems: 'start',
},
options: [
{
label: i18n.t('pages.Clusters.Node.UpgradeAgentAndInstaller'),
value: 0,
},
{
label: i18n.t('pages.Clusters.Node.UpgradeAgent'),
value: 1,
},
{
label: i18n.t('pages.Clusters.Node.UpgradeInstaller'),
value: 2,
},
{
label: i18n.t('pages.Clusters.Node.RestartAgent'),
value: 3,
},
],
},
},
{
type: 'select',
label: i18n.t('pages.Clusters.Node.Agent.Version'),
name: 'moduleIdList',
visible: values => values.operationType !== 2 && values.operationType !== 3,
props: {
options: {
requestAuto: true,
requestTrigger: ['onOpen'],
requestService: keyword => ({
url: '/module/list',
method: 'POST',
data: {
keyword,
pageNum: 1,
pageSize: 9999,
},
}),
requestParams: {
formatResult: result =>
result?.list
?.filter(item => item.type === 'AGENT')
.map(item => ({
...item,
label: `${item.name} ${item.version}`,
value: item.id,
})),
},
},
},
rules: [
{
required: true,
},
],
},
{
type: 'select',
label: i18n.t('pages.Clusters.Node.AgentInstaller'),
name: 'installer',
visible: values => values.operationType === 0 || values.operationType === 2,
props: {
options: {
requestAuto: true,
requestTrigger: ['onOpen'],
requestService: keyword => ({
url: '/module/list',
method: 'POST',
data: {
keyword,
pageNum: 1,
pageSize: 9999,
},
}),
requestParams: {
formatResult: result =>
result?.list
?.filter(item => item.type === 'INSTALLER')
.map(item => ({
...item,
label: `${item.name} ${item.version}`,
value: item.id,
})),
},
},
},
rules: [
{
required: true,
},
],
},
];

const valuesToSubmitList = (agentList, values, submitList) => {
switch (values.operationType) {
case 0:
agentList.forEach(item => {
delete item.protocolType;
item.moduleIdList = [values.moduleIdList, values.installer];
submitList.push({
...item,
moduleIdList: [values.moduleIdList, values.installer],
isInstall: true,
});
});
break;
case 1:
agentList.forEach(item => {
delete item.protocolType;
item.moduleIdList = [values.moduleIdList, item.moduleIdList[1]];
submitList.push({
...item,
});
delete item.isInstall;
});
break;

case 2:
agentList.forEach(item => {
delete item.protocolType;
item.isInstall = true;
item.moduleIdList = [item.moduleIdList[0], values.installer];
submitList.push({
...item,
});
});
break;

case 3:
agentList.forEach(item => {
delete item.protocolType;
submitList.push({
...item,
});
delete item.isInstall;
});
break;
}
};
const batchUpdate = async (agentList, onOk: (e: React.MouseEvent<HTMLButtonElement>) => void) => {
const values = await form.validateFields();
const submitList = [];

valuesToSubmitList(agentList, values, submitList);
console.log('submitList', submitList);
const baseBatchSize =
Math.floor(submitList.length / values.batchNum) === 0
? 1
: Math.floor(submitList.length / values.batchNum);
const remainder = submitList.length % values.batchNum;
const map = new Map();
const batchNum = agentList.length < values.batchNum ? agentList.length : values.batchNum;
console.log('batchNum', batchNum, 'baseBatchSize', baseBatchSize, 'remainder', remainder);

for (let i = 1; i <= batchNum; i++) {
if (i === batchNum) {
map.set(i, submitList.slice((i - 1) * baseBatchSize, submitList.length));
} else {
map.set(i, submitList.slice((i - 1) * baseBatchSize, i * baseBatchSize));
}
}
const args = {
map: map,
interval: values.interval,
operationType: values.operationType,
ids: agentList.map(item => item.id),
submitDataList: submitList,
};
modalProps.getArgs(args);
modalProps?.onOk(values);
modalProps?.openStatusModal();
};

return (
<Modal
{...modalProps}
title={i18n.t('pages.Clusters.Node.BatchUpdate')}
width={600}
footer={[
<Button key="cancel" onClick={e => modalProps.onCancel(e)}>
{i18n.t('basic.Cancel')}
</Button>,
<Button
key="update"
type="primary"
onClick={async e => {
await batchUpdate(agentList, modalProps.onOk);
}}
>
{i18n.t('basic.Confirm')}
</Button>,
]}
>
<FormGenerator content={content()} form={form} useMaxWidth labelWrap />
</Modal>
);
};

export default Comp;
Loading

0 comments on commit e49d69e

Please sign in to comment.