chore: add credential settings for agent in enrolling agent (#116)
* chore: add collection mode to cluster editing * chore: add credential settings for agent when enroll agent * chore: update release notes --------- Co-authored-by: yaojiping <yaojiping@infini.ltd>
This commit is contained in:
parent
8762255322
commit
d2a3ec0e7a
|
@ -19,6 +19,9 @@ Information about release notes of INFINI Console is provided here.
|
|||
|
||||
## 1.28.1 (2025-01-24)
|
||||
|
||||
- add credential settings for agent in enrolling agent
|
||||
- add collection mode to cluster editing
|
||||
|
||||
### Features
|
||||
|
||||
- Support function-format parameters in Insight Data API
|
||||
|
|
|
@ -17,6 +17,9 @@ title: "版本历史"
|
|||
|
||||
### Improvements
|
||||
|
||||
- 在注册 Agent 中新增 Agent 凭据设置
|
||||
- 在集群编辑中新增采集模式
|
||||
|
||||
## 1.28.1 (2025-01-24)
|
||||
|
||||
### Features
|
||||
|
|
|
@ -151,8 +151,11 @@ const Monitor = (props) => {
|
|||
const breadcrumbList = getBreadcrumbList(state);
|
||||
|
||||
const isAgent = useMemo(() => {
|
||||
const { monitor_configs = {} } = selectedCluster || {}
|
||||
return monitor_configs?.node_stats?.enabled === false && monitor_configs?.index_stats?.enabled === false
|
||||
const { metric_collection_mode, monitor_configs = {} } = selectedCluster || {}
|
||||
if (typeof metric_collection_mode === 'undefined') {
|
||||
return monitor_configs?.node_stats?.enabled === false && monitor_configs?.index_stats?.enabled === false
|
||||
}
|
||||
return metric_collection_mode === 'agent'
|
||||
}, [JSON.stringify(selectedCluster?.monitor_configs)])
|
||||
|
||||
return (
|
||||
|
|
|
@ -6,7 +6,12 @@ export default {
|
|||
"agent.instance.associate.labels.select_cluster": "Select Cluster",
|
||||
"agent.instance.associate.tips.associate":
|
||||
"Please select cluster(s) to enroll !",
|
||||
"agent.instance.associate.set_credential": "Set credential for agent",
|
||||
"agent.instance.associate.set_credential.tips":
|
||||
"This permission will be used for metrics and log collection. It is recommended to use a user with a reasonable permission range.",
|
||||
"agent.instance.associate.tips.connected": "Connection succeeded!",
|
||||
"agent.instance.associate.tips.connected.check": "please set a credential for agent",
|
||||
"agent.instance.associate.auth.error": "The following clusters need to set credentials for the agent:",
|
||||
"agent.instance.associate.tips.metric":
|
||||
"After enroll, the agent will collect metrics for the enrolled cluster",
|
||||
"agent.instance.associate.tips.unregister":
|
||||
|
@ -35,4 +40,7 @@ export default {
|
|||
"agent.install.setup.copy.success": "Copied to clipboard successfully!",
|
||||
"agent.instance.auto_associate.title": "Auto Enroll",
|
||||
"agent.instance.install.title": "Install Agent",
|
||||
|
||||
"agent.label.agent_credential": "Agent Credential",
|
||||
"agent.credential.tip": "No credential required",
|
||||
};
|
||||
|
|
|
@ -207,6 +207,8 @@ export default {
|
|||
"alert.rule.table.columnns.schedule": "Schedule",
|
||||
"alert.rule.table.columnns.expression": "Expression",
|
||||
"alert.rule.table.columnns.status": "Status",
|
||||
"alert.rule.table.columnns.status.failed": "Connect failed",
|
||||
"alert.rule.table.columnns.status.succeeded": "Connect succeeded",
|
||||
"alert.rule.table.columnns.enabled": "Enabled",
|
||||
"alert.rule.table.columnns.updated": "Updated time",
|
||||
"alert.rule.table.columnns.category": "Category",
|
||||
|
|
|
@ -35,6 +35,7 @@ export default {
|
|||
"cluster.manage.table.column.location": "Location",
|
||||
"cluster.manage.monitored.on": "ON",
|
||||
"cluster.manage.monitored.off": "OFF",
|
||||
"cluster.manage.metric_collection_mode": "Collect Mode",
|
||||
"cluster.manage.monitor_configs.cluster_health": "Cluster health",
|
||||
"cluster.manage.monitor_configs.cluster_stats": "Cluster stats",
|
||||
"cluster.manage.monitor_configs.node_stats": "Node stats",
|
||||
|
|
|
@ -5,7 +5,11 @@ export default {
|
|||
"agent.instance.associate.labels.cluster_version": "版本",
|
||||
"agent.instance.associate.labels.select_cluster": "关联到集群",
|
||||
"agent.instance.associate.tips.associate": "请选择要关联的集群!",
|
||||
"agent.instance.associate.set_credential": "为代理设置凭据",
|
||||
"agent.instance.associate.set_credential.tips": "此权限将用于度量和日志收集。建议使用具有合理权限范围的用户。",
|
||||
"agent.instance.associate.tips.connected": "连接成功!",
|
||||
"agent.instance.associate.tips.connected.check": "请设置凭据",
|
||||
"agent.instance.associate.auth.error": "以下集群需要为 Agent 设置凭据:",
|
||||
"agent.instance.associate.tips.metric":
|
||||
"关联后 Agent 会对关联的集群进行指标采集操作",
|
||||
"agent.instance.associate.tips.unregister":
|
||||
|
@ -33,4 +37,7 @@ export default {
|
|||
"agent.install.setup.copy.success": "已成功复制到剪贴板!",
|
||||
"agent.instance.auto_associate.title": "自动关联集群",
|
||||
"agent.instance.install.title": "安装 Agent",
|
||||
|
||||
"agent.label.agent_credential": "代理凭据",
|
||||
"agent.credential.tip": "不需要凭据",
|
||||
};
|
||||
|
|
|
@ -195,6 +195,8 @@ export default {
|
|||
"alert.rule.table.columnns.schedule": "计划周期",
|
||||
"alert.rule.table.columnns.expression": "告警规则",
|
||||
"alert.rule.table.columnns.status": "运行状态",
|
||||
"alert.rule.table.columnns.status.failed": "连接失败",
|
||||
"alert.rule.table.columnns.status.succeeded": "连接成功",
|
||||
"alert.rule.table.columnns.enabled": "告警启停",
|
||||
"alert.rule.table.columnns.updated": "更新时间",
|
||||
"alert.rule.table.columnns.category": "分类",
|
||||
|
|
|
@ -35,6 +35,7 @@ export default {
|
|||
"cluster.manage.table.column.location": "位置",
|
||||
"cluster.manage.monitored.on": "启用",
|
||||
"cluster.manage.monitored.off": "关闭",
|
||||
"cluster.manage.metric_collection_mode": "采集模式",
|
||||
"cluster.manage.monitor_configs.cluster_health": "集群健康状态指标",
|
||||
"cluster.manage.monitor_configs.cluster_stats": "集群指标",
|
||||
"cluster.manage.monitor_configs.node_stats": "节点指标",
|
||||
|
|
|
@ -0,0 +1,117 @@
|
|||
import { Alert, Button, Form, message } from "antd";
|
||||
import { useState } from "react";
|
||||
import { formatMessage } from "umi/locale";
|
||||
|
||||
import request from "@/utils/request";
|
||||
import AgentCredentialForm, { MANUAL_VALUE } from "./AgentCredentialForm";
|
||||
import { ESPrefix } from "@/services/common";
|
||||
|
||||
const formItemLayout = {
|
||||
labelCol: {
|
||||
xs: { span: 24 },
|
||||
sm: { span: 5 },
|
||||
},
|
||||
wrapperCol: {
|
||||
xs: { span: 24 },
|
||||
sm: { span: 18 },
|
||||
},
|
||||
};
|
||||
|
||||
export default Form.create()((props) => {
|
||||
const { form, record, loading, tryConnect, onAgentCredentialSave } = props;
|
||||
|
||||
const [isManual, setIsManual] = useState(false);
|
||||
const [saveLoading, setSaveLoading] = useState(false);
|
||||
|
||||
const needAuth = !!(record.credential_id || record.basic_auth?.username);
|
||||
|
||||
const onConfirm = async () => {
|
||||
form.validateFields(async (errors, values) => {
|
||||
if (errors) return;
|
||||
setSaveLoading(true);
|
||||
const { credential_id, basic_auth, metric_collection_mode } = record;
|
||||
const res = await request(`${ESPrefix}/${record.id}`, {
|
||||
method: "PUT",
|
||||
body: {
|
||||
credential_id,
|
||||
basic_auth,
|
||||
metric_collection_mode,
|
||||
agent_credential_id:
|
||||
values.agent_credential_id !== MANUAL_VALUE
|
||||
? values.agent_credential_id
|
||||
: undefined,
|
||||
agent_basic_auth: {
|
||||
username: values.agent_username,
|
||||
password: values.agent_password,
|
||||
},
|
||||
},
|
||||
});
|
||||
if (res?.result === "updated") {
|
||||
message.success(
|
||||
formatMessage({
|
||||
id: "app.message.update.success",
|
||||
})
|
||||
);
|
||||
const res = await request(`/elasticsearch/${record.id}`);
|
||||
if (res?.found) {
|
||||
onAgentCredentialSave(res._source);
|
||||
if (res._source?.agent_credential_id) {
|
||||
setIsManual(false);
|
||||
}
|
||||
form.setFieldsValue({
|
||||
agent_credential_id: res._source?.agent_credential_id
|
||||
? res._source?.agent_credential_id
|
||||
: res._source?.agent_basic_auth?.username
|
||||
? MANUAL_VALUE
|
||||
: undefined,
|
||||
agent_username: res._source.agent_basic_auth?.username,
|
||||
agent_password: res._source.agent_basic_auth?.password,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
message.error(
|
||||
formatMessage({
|
||||
id: "app.message.update.failed",
|
||||
})
|
||||
);
|
||||
}
|
||||
setSaveLoading(false);
|
||||
});
|
||||
};
|
||||
|
||||
if (!needAuth) {
|
||||
return (
|
||||
<Alert
|
||||
message={formatMessage({ id: "agent.credential.tip" })}
|
||||
type="success"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Form {...formItemLayout} colon={false}>
|
||||
<AgentCredentialForm
|
||||
btnLoading={loading}
|
||||
needAuth={needAuth}
|
||||
form={form}
|
||||
initialValue={{
|
||||
...record,
|
||||
username: record.agent_basic_auth?.username,
|
||||
password: record.agent_basic_auth?.password,
|
||||
}}
|
||||
isManual={isManual}
|
||||
setIsManual={setIsManual}
|
||||
isEdit={true}
|
||||
tryConnect={tryConnect}
|
||||
credentialRequired={false}
|
||||
/>
|
||||
<Form.Item label=" " colon={false}>
|
||||
<div style={{ textAlign: "right" }}>
|
||||
<Button loading={loading} type="primary" onClick={() => onConfirm()}>
|
||||
{formatMessage({ id: "cluster.regist.step.confirm.title" })}
|
||||
</Button>
|
||||
</div>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
);
|
||||
});
|
|
@ -0,0 +1,143 @@
|
|||
import React, { useEffect, useMemo, useState } from "react";
|
||||
import { Button, Divider, Form, Input, Select, Row, Col } from "antd";
|
||||
import { formatMessage } from "umi/locale";
|
||||
|
||||
import useFetch from "@/lib/hooks/use_fetch";
|
||||
import { formatESSearchResult } from "@/lib/elasticsearch/util";
|
||||
|
||||
export const MANUAL_VALUE = "manual";
|
||||
|
||||
export default (props) => {
|
||||
const {
|
||||
btnLoading = false,
|
||||
needAuth,
|
||||
form: { getFieldDecorator },
|
||||
initialValue,
|
||||
isEdit,
|
||||
tryConnect,
|
||||
credentialRequired = false,
|
||||
isManual,
|
||||
setIsManual,
|
||||
} = props;
|
||||
|
||||
const { loading, error, value, run } = useFetch(
|
||||
"/credential/_search",
|
||||
{
|
||||
queryParams: {
|
||||
from: 0,
|
||||
size: 1000,
|
||||
},
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
const onCredentialChange = (value) => {
|
||||
if (value === "manual") {
|
||||
setIsManual(true);
|
||||
} else {
|
||||
setIsManual(false);
|
||||
}
|
||||
};
|
||||
|
||||
const { data, total } = useMemo(() => {
|
||||
return formatESSearchResult(value);
|
||||
}, [value]);
|
||||
|
||||
if (!needAuth) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Form.Item
|
||||
label={formatMessage({
|
||||
id: "cluster.regist.step.connect.label.agent_credential",
|
||||
})}
|
||||
>
|
||||
{getFieldDecorator("agent_credential_id", {
|
||||
initialValue: initialValue?.agent_credential_id
|
||||
? initialValue?.agent_credential_id
|
||||
: initialValue?.username
|
||||
? MANUAL_VALUE
|
||||
: undefined,
|
||||
rules: [
|
||||
{
|
||||
required: credentialRequired,
|
||||
message: formatMessage({
|
||||
id: "cluster.regist.form.verify.required.agent_credential",
|
||||
}),
|
||||
},
|
||||
],
|
||||
})(
|
||||
<Select loading={loading} onChange={onCredentialChange} allowClear>
|
||||
<Select.Option value={MANUAL_VALUE}>
|
||||
{formatMessage({
|
||||
id: "cluster.regist.step.connect.credential.manual",
|
||||
})}
|
||||
</Select.Option>
|
||||
{data.map((item) => (
|
||||
<Select.Option value={item.id}>{item.name}</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
)}
|
||||
</Form.Item>
|
||||
{isManual && (
|
||||
<>
|
||||
<Form.Item
|
||||
label={formatMessage({
|
||||
id: "cluster.regist.step.connect.label.username",
|
||||
})}
|
||||
>
|
||||
{getFieldDecorator("agent_username", {
|
||||
initialValue: initialValue?.username || "",
|
||||
rules: [
|
||||
{
|
||||
required: credentialRequired,
|
||||
message: formatMessage({
|
||||
id: "cluster.regist.form.verify.required.auth_username",
|
||||
}),
|
||||
},
|
||||
],
|
||||
})(<Input autoComplete="off" placeholder={formatMessage({id: "agent.form.placeholder.auth.username"})} />)}
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={formatMessage({
|
||||
id: "cluster.regist.step.connect.label.password",
|
||||
})}
|
||||
hasFeedback
|
||||
>
|
||||
{getFieldDecorator("agent_password", {
|
||||
initialValue: initialValue?.password || "",
|
||||
rules: [
|
||||
{
|
||||
required: credentialRequired,
|
||||
message: formatMessage({
|
||||
id: "cluster.regist.form.verify.required.auth_password",
|
||||
}),
|
||||
},
|
||||
],
|
||||
})(
|
||||
<Input.Password
|
||||
autoComplete="off"
|
||||
placeholder={formatMessage({
|
||||
id: "cluster.regist.form.verify.required.auth_password",
|
||||
})}
|
||||
/>
|
||||
)}
|
||||
</Form.Item>
|
||||
{isEdit && (
|
||||
<>
|
||||
<Form.Item label={" "}>
|
||||
<div style={{ lineHeight: "20px" }}>
|
||||
{formatMessage({
|
||||
id: "cluster.regist.form.credential.manual.desc",
|
||||
})}
|
||||
</div>
|
||||
</Form.Item>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
|
@ -1,22 +1,36 @@
|
|||
import { useGlobal } from "@/layouts/GlobalContext";
|
||||
import request from "@/utils/request";
|
||||
import { Form, Input, Switch, Icon, Button, Select } from "antd";
|
||||
import { useMemo, useRef, useState } from "react";
|
||||
import { Form, Input, Switch, Icon, Button, Select, Alert } from "antd";
|
||||
import { useEffect, useMemo, useRef, useState } from "react";
|
||||
import { Link, router } from "umi";
|
||||
import { formatMessage } from "umi/locale";
|
||||
import ClusterSelect from "@/components/ClusterSelect";
|
||||
import SetAgentCredential from "./SetAgentCredential";
|
||||
|
||||
export default ({ onEnroll, loading }) => {
|
||||
const { clusterList = [], clusterStatus } = useGlobal();
|
||||
|
||||
const [selectedCluster, setSelectedCluster] = useState([]);
|
||||
const [auths, setAuths] = useState([]);
|
||||
|
||||
const onEnrollClick = () => {
|
||||
if (typeof onEnroll === "function") {
|
||||
if (selectedCluster.length === 0) return;
|
||||
const newAuths = [...auths]
|
||||
selectedCluster.forEach((item) => {
|
||||
if (item.credential_id && !item.agent_credential_id) {
|
||||
newAuths.push(item)
|
||||
}
|
||||
})
|
||||
setAuths(newAuths)
|
||||
if (newAuths.length === 0 && typeof onEnroll === "function") {
|
||||
onEnroll(selectedCluster.map((item) => item.id));
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setAuths([])
|
||||
}, [JSON.stringify(selectedCluster)])
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div
|
||||
|
@ -37,6 +51,27 @@ export default ({ onEnroll, loading }) => {
|
|||
}}
|
||||
/>
|
||||
</div>
|
||||
<SetAgentCredential selectedCluster={selectedCluster} setSelectedCluster={setSelectedCluster}/>
|
||||
{
|
||||
auths.length > 0 && (
|
||||
<Alert style={{ marginTop: 10 }} type="error" message={(
|
||||
<div>
|
||||
<div>
|
||||
{formatMessage({
|
||||
id: "agent.instance.associate.auth.error",
|
||||
})}
|
||||
</div>
|
||||
<div>
|
||||
{ auths.map((item) => (
|
||||
<div key={item.id}>
|
||||
- {item.name}
|
||||
</div>
|
||||
)) }
|
||||
</div>
|
||||
</div>
|
||||
)}/>
|
||||
)
|
||||
}
|
||||
<div style={{ marginTop: 10, textAlign: "right" }}>
|
||||
<div style={{ marginBottom: 15, color: "rgba(130,129,136,1)" }}>
|
||||
<span>
|
||||
|
|
|
@ -228,7 +228,7 @@ export const AgentRowDetail = ({ agentID, t }) => {
|
|||
})
|
||||
}
|
||||
>
|
||||
<Button type="link" loading={btnLoading}>
|
||||
<Button style={{padding: 0}} type="link" loading={btnLoading}>
|
||||
Revoke
|
||||
</Button>
|
||||
</Popconfirm>
|
||||
|
|
|
@ -0,0 +1,174 @@
|
|||
import request from "@/utils/request";
|
||||
import { message, Table, Tooltip, Spin } from "antd";
|
||||
import { useState } from "react";
|
||||
import { formatMessage } from "umi/locale";
|
||||
|
||||
import { MANUAL_VALUE } from "./AgentCredentialForm";
|
||||
import styles from "./SetAgentCredential.less";
|
||||
import AgentCredential from "./AgentCredential";
|
||||
import { ESPrefix } from "@/services/common";
|
||||
import { cloneDeep } from "lodash";
|
||||
import { connect } from "dva";
|
||||
|
||||
export default connect()((props) => {
|
||||
const { selectedCluster, setSelectedCluster, dispatch } = props
|
||||
|
||||
const [status, setStatus] = useState({});
|
||||
const [testLoading, setTestLoading] = useState(false);
|
||||
|
||||
const onAgentCredentialSave = async (values) => {
|
||||
const newSelectedCluster = cloneDeep(selectedCluster);
|
||||
const index = newSelectedCluster.findIndex((item) => item.id === values.id);
|
||||
if (index !== -1) {
|
||||
newSelectedCluster[index] = values;
|
||||
setSelectedCluster(newSelectedCluster);
|
||||
}
|
||||
dispatch({
|
||||
type: "global/fetchClusterList",
|
||||
payload: {
|
||||
size: 200,
|
||||
name: "",
|
||||
},
|
||||
});
|
||||
dispatch({
|
||||
type: "global/fetchClusterStatus",
|
||||
})
|
||||
};
|
||||
|
||||
const expandedRowRender = (record) => {
|
||||
return (
|
||||
<AgentCredential
|
||||
record={record}
|
||||
onAgentCredentialSave={(values) => onAgentCredentialSave(values)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const tryConnect = async (values) => {
|
||||
setTestLoading(true);
|
||||
const body = {
|
||||
basic_auth: {
|
||||
username: values.agent_basic_auth?.username,
|
||||
password: values.agent_basic_auth?.password,
|
||||
},
|
||||
host: values.host,
|
||||
credential_id:
|
||||
values.agent_credential_id !== MANUAL_VALUE
|
||||
? values.agent_credential_id
|
||||
: undefined,
|
||||
schema: values.schema || "http",
|
||||
};
|
||||
if (
|
||||
values.credential_id &&
|
||||
!body.credential_id &&
|
||||
(!body.basic_auth.username || !body.basic_auth.password)
|
||||
) {
|
||||
message.warning(formatMessage({ id: "agent.instance.associate.tips.connected.check" }));
|
||||
setTestLoading(false);
|
||||
return;
|
||||
}
|
||||
const res = await request(`${ESPrefix}/try_connect`, {
|
||||
method: "POST",
|
||||
body,
|
||||
showErrorInner: true,
|
||||
}, false, false);
|
||||
setStatus({
|
||||
...status,
|
||||
[values.id]: {
|
||||
status: res?.status,
|
||||
error: res?.error,
|
||||
},
|
||||
});
|
||||
setTestLoading(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div style={{ marginTop: 32 }}>
|
||||
<div
|
||||
style={{
|
||||
fontSize: 16,
|
||||
color: "rgba(16, 16, 16, 1)",
|
||||
fontWeight: 600,
|
||||
marginBottom: 8,
|
||||
}}
|
||||
>
|
||||
{formatMessage({ id: "agent.instance.associate.set_credential" })}
|
||||
</div>
|
||||
<div>{formatMessage({ id: "agent.instance.associate.set_credential.tips" })}</div>
|
||||
</div>
|
||||
|
||||
<div style={{ marginTop: 15 }}>
|
||||
<Table
|
||||
size="small"
|
||||
rowKey={"id"}
|
||||
dataSource={selectedCluster || []}
|
||||
className={styles.table}
|
||||
columns={[
|
||||
{
|
||||
title: formatMessage({
|
||||
id: "agent.instance.associate.labels.cluster_name",
|
||||
}),
|
||||
dataIndex: "name",
|
||||
key: "name",
|
||||
},
|
||||
{
|
||||
title: formatMessage({ id: "guide.cluster.auth" }),
|
||||
dataIndex: "credential_id",
|
||||
key: "credential_id",
|
||||
render: (text, record) => {
|
||||
return record.credential_id || record.basic_auth?.username
|
||||
? formatMessage({
|
||||
id: "cluster.regist.step.complete.tls.yes",
|
||||
})
|
||||
: formatMessage({
|
||||
id: "cluster.regist.step.complete.tls.no",
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
title: formatMessage({ id: "agent.label.agent_credential" }),
|
||||
dataIndex: "agent_credential_id",
|
||||
key: "agent_credential_id",
|
||||
render: (text, record) => {
|
||||
return record.agent_credential_id ? "Set" : "No set";
|
||||
},
|
||||
},
|
||||
{
|
||||
title: formatMessage({ id: "alert.rule.table.columnns.status" }),
|
||||
dataIndex: "status",
|
||||
key: "Status",
|
||||
render: (text, record) => {
|
||||
if (!status[record.id]) return "-";
|
||||
if (status[record.id].error) {
|
||||
return (
|
||||
<Tooltip title={status[record.id].error}>
|
||||
<span style={{ color: "red" }}>{formatMessage({ id: "alert.rule.table.columnns.status.failed"})}</span>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<span style={{ color: "green" }}>{formatMessage({ id: "alert.rule.table.columnns.status.succeeded"})}</span>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: formatMessage({ id: "table.field.actions" }),
|
||||
dataIndex: "",
|
||||
key: "",
|
||||
render: (record) =>
|
||||
testLoading ? (
|
||||
<Spin />
|
||||
) : (
|
||||
<a onClick={() => tryConnect(record)}>
|
||||
{formatMessage({ id: "guide.cluster.test.connection" })}
|
||||
</a>
|
||||
),
|
||||
},
|
||||
]}
|
||||
expandedRowRender={expandedRowRender}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
});
|
|
@ -0,0 +1,7 @@
|
|||
.table {
|
||||
:global {
|
||||
tr.ant-table-expanded-row, tr.ant-table-expanded-row:hover {
|
||||
background: #fff;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,22 +1,35 @@
|
|||
import { useGlobal } from "@/layouts/GlobalContext";
|
||||
import request from "@/utils/request";
|
||||
import { Form, Input, Switch, Icon, Button, Select } from "antd";
|
||||
import { useMemo, useRef, useState } from "react";
|
||||
import { Form, Input, Switch, Icon, Button, Alert } from "antd";
|
||||
import { useEffect, useMemo, useRef, useState } from "react";
|
||||
import { Link, router } from "umi";
|
||||
import { formatMessage } from "umi/locale";
|
||||
import ClusterSelect from "@/components/ClusterSelect";
|
||||
import SetAgentCredential from "./SetAgentCredential";
|
||||
|
||||
export default ({ onBatchEnroll, loading }) => {
|
||||
const { clusterList = [], clusterStatus } = useGlobal();
|
||||
|
||||
const [selectedCluster, setSelectedCluster] = useState([]);
|
||||
const [auths, setAuths] = useState([]);
|
||||
|
||||
const onBatchEnrollClick = () => {
|
||||
if (typeof onBatchEnroll === "function") {
|
||||
if (selectedCluster.length === 0) return;
|
||||
const newAuths = [...auths]
|
||||
selectedCluster.forEach((item) => {
|
||||
if (item.credential_id && !item.agent_credential_id) {
|
||||
newAuths.push(item)
|
||||
}
|
||||
})
|
||||
setAuths(newAuths)
|
||||
if (newAuths.length === 0 && typeof onBatchEnroll === "function") {
|
||||
onBatchEnroll(selectedCluster.map((item) => item.id));
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setAuths([])
|
||||
}, [JSON.stringify(selectedCluster)])
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div
|
||||
|
@ -38,6 +51,27 @@ export default ({ onBatchEnroll, loading }) => {
|
|||
}}
|
||||
/>
|
||||
</div>
|
||||
<SetAgentCredential selectedCluster={selectedCluster} setSelectedCluster={setSelectedCluster}/>
|
||||
{
|
||||
auths.length > 0 && (
|
||||
<Alert style={{ marginTop: 10 }} type="error" message={(
|
||||
<div>
|
||||
<div>
|
||||
{formatMessage({
|
||||
id: "agent.instance.associate.auth.error",
|
||||
})}
|
||||
</div>
|
||||
<div>
|
||||
{ auths.map((item) => (
|
||||
<div key={item.id}>
|
||||
- {item.name}
|
||||
</div>
|
||||
)) }
|
||||
</div>
|
||||
</div>
|
||||
)}/>
|
||||
)
|
||||
}
|
||||
<div style={{ marginTop: 10, textAlign: "right" }}>
|
||||
<div style={{ marginBottom: 15, color: "rgba(130,129,136,1)" }}>
|
||||
<span>
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
.table {
|
||||
:global {
|
||||
tr.ant-table-expanded-row, tr.ant-table-expanded-row:hover {
|
||||
background: #fff;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
import { Form, Radio } from "antd";
|
||||
import { useEffect, useState } from "react";
|
||||
import { formatMessage } from "umi/locale";
|
||||
import styles from "./CollectMode.less";
|
||||
|
||||
export default (props) => {
|
||||
const { editValue, form, onChange } = props;
|
||||
const { getFieldDecorator } = form;
|
||||
|
||||
const [mode, setMode] = useState(
|
||||
editValue?.metric_collection_mode || "agentless"
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const monitor_configs = form.getFieldValue("monitor_configs");
|
||||
if (mode === "agent") {
|
||||
monitor_configs["node_stats"] = { enabled: false };
|
||||
monitor_configs["index_stats"] = { enabled: false };
|
||||
} else {
|
||||
monitor_configs["node_stats"] = { enabled: true };
|
||||
monitor_configs["index_stats"] = { enabled: true };
|
||||
}
|
||||
form.setFieldsValue({ monitor_configs });
|
||||
}, [mode]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Form.Item
|
||||
label={formatMessage({ id: "cluster.manage.metric_collection_mode" })}
|
||||
>
|
||||
{getFieldDecorator(`metric_collection_mode`, {
|
||||
initialValue: mode,
|
||||
})(
|
||||
<Radio.Group
|
||||
className={styles.mode}
|
||||
onChange={(e) => {
|
||||
setMode(e.target.value)
|
||||
onChange(e.target.value)
|
||||
}}
|
||||
>
|
||||
<Radio.Button value="agentless">Agentless</Radio.Button>
|
||||
<Radio.Button value="agent">Agent</Radio.Button>
|
||||
</Radio.Group>
|
||||
)}
|
||||
</Form.Item>
|
||||
</>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,8 @@
|
|||
.mode {
|
||||
:global {
|
||||
.ant-radio-button-wrapper {
|
||||
margin-right: 12px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -27,6 +27,7 @@ import { MANUAL_VALUE } from "./steps";
|
|||
import SearchEngines from "./components/SearchEngines";
|
||||
import Providers from "./components/Providers";
|
||||
import TrimSpaceInput from "@/components/TrimSpaceInput";
|
||||
import CollectMode from "./CollectMode";
|
||||
|
||||
const InputGroup = Input.Group;
|
||||
@Form.create()
|
||||
|
@ -51,7 +52,7 @@ class ClusterForm extends React.Component {
|
|||
|
||||
validateFieldNames = [
|
||||
"name",
|
||||
"host",
|
||||
"hosts",
|
||||
"isTLS",
|
||||
"credential_id",
|
||||
"username",
|
||||
|
@ -59,7 +60,7 @@ class ClusterForm extends React.Component {
|
|||
];
|
||||
agentValidateFieldNames = [
|
||||
"name",
|
||||
"host",
|
||||
"hosts",
|
||||
"isTLS",
|
||||
"agent_credential_id",
|
||||
"agent_username",
|
||||
|
@ -70,6 +71,7 @@ class ClusterForm extends React.Component {
|
|||
//console.log(this.props.clusterConfig.editMode)
|
||||
const { match, dispatch, clusterConfig } = this.props;
|
||||
if (clusterConfig?.editValue) {
|
||||
|
||||
this.setState({
|
||||
monitored: clusterConfig?.editValue.hasOwnProperty("monitored")
|
||||
? clusterConfig?.editValue?.monitored
|
||||
|
@ -98,9 +100,14 @@ class ClusterForm extends React.Component {
|
|||
isManual = true;
|
||||
}
|
||||
}
|
||||
let collectMode = editValue?.metric_collection_mode || 'agentless'
|
||||
if (typeof editValue?.metric_collection_mode === 'undefined' && editValue?.monitor_configs?.node_stats?.enabled === false && editValue?.monitor_configs?.index_stats?.enabled === false) {
|
||||
collectMode = 'agent'
|
||||
}
|
||||
this.setState({
|
||||
needAuth,
|
||||
isManual,
|
||||
collectMode
|
||||
});
|
||||
}
|
||||
});
|
||||
|
@ -154,6 +161,7 @@ class ClusterForm extends React.Component {
|
|||
}
|
||||
const monitor_configs_new = formatConfigsValues(values.monitor_configs);
|
||||
const metadata_configs_new = formatConfigsValues(values.metadata_configs);
|
||||
const isAgentMode = values.metric_collection_mode === "agent";
|
||||
|
||||
let newVals = {
|
||||
name: values.name,
|
||||
|
@ -169,13 +177,14 @@ class ClusterForm extends React.Component {
|
|||
},
|
||||
|
||||
agent_credential_id:
|
||||
values.agent_credential_id !== MANUAL_VALUE
|
||||
values.agent_credential_id !== MANUAL_VALUE && isAgentMode
|
||||
? values.agent_credential_id
|
||||
: undefined,
|
||||
agent_basic_auth: {
|
||||
: agent_credential_id,
|
||||
agent_basic_auth: isAgentMode ? {
|
||||
username: values.agent_username,
|
||||
password: values.agent_password,
|
||||
},
|
||||
} : agent_basic_auth,
|
||||
metric_collection_mode: values.metric_collection_mode || 'agentless',
|
||||
|
||||
description: values.description,
|
||||
enabled: values.enabled,
|
||||
|
@ -267,9 +276,10 @@ class ClusterForm extends React.Component {
|
|||
if (!values) {
|
||||
return;
|
||||
}
|
||||
debugger
|
||||
let newVals = {
|
||||
name: values.name,
|
||||
host: values.host,
|
||||
hosts: values.hosts,
|
||||
|
||||
schema: values.isTLS === true ? "https" : "http",
|
||||
};
|
||||
|
@ -417,6 +427,15 @@ class ClusterForm extends React.Component {
|
|||
],
|
||||
})(<Input autoComplete="off" placeholder="Cluster name" />)}
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={formatMessage({
|
||||
id: "cluster.manage.table.column.description",
|
||||
})}
|
||||
>
|
||||
{getFieldDecorator("description", {
|
||||
initialValue: editValue.description,
|
||||
})(<Input.TextArea placeholder="Cluster Descirption" />)}
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={formatMessage({
|
||||
id: "cluster.manage.label.distribution",
|
||||
|
@ -508,35 +527,12 @@ class ClusterForm extends React.Component {
|
|||
tryConnect={this.tryConnect}
|
||||
credentialRequired={this.state.credentialRequired}
|
||||
/>
|
||||
<AgentCredentialForm
|
||||
btnLoading={this.state.btnLoadingAgent}
|
||||
needAuth={this.state.needAuth}
|
||||
form={this.props.form}
|
||||
initialValue={{
|
||||
...editValue,
|
||||
username: editValue.agent_basic_auth?.username,
|
||||
password: editValue.agent_basic_auth?.password,
|
||||
}}
|
||||
isManual={this.state.isManual}
|
||||
isEdit={true}
|
||||
tryConnect={this.tryConnect}
|
||||
credentialRequired={this.state.agentCredentialRequired}
|
||||
/>
|
||||
|
||||
{/* <Form.Item label="排序权重">
|
||||
{getFieldDecorator('order', {
|
||||
initialValue: editValue.order || 0,
|
||||
})(<InputNumber />)}
|
||||
</Form.Item> */}
|
||||
<Form.Item
|
||||
label={formatMessage({
|
||||
id: "cluster.manage.table.column.description",
|
||||
})}
|
||||
>
|
||||
{getFieldDecorator("description", {
|
||||
initialValue: editValue.description,
|
||||
})(<Input.TextArea placeholder="Cluster Descirption" />)}
|
||||
</Form.Item>
|
||||
{/* <Form.Item label="是否启用">
|
||||
{getFieldDecorator('enabled', {
|
||||
valuePropName: 'checked',
|
||||
|
@ -581,10 +577,36 @@ class ClusterForm extends React.Component {
|
|||
/>
|
||||
)}
|
||||
</Form.Item>
|
||||
<CollectMode
|
||||
form={this.props.form}
|
||||
editValue={editValue}
|
||||
onChange={(mode) => {
|
||||
this.setState({ collectMode: mode })
|
||||
}}
|
||||
/>
|
||||
{
|
||||
this.state.collectMode === 'agent' && (
|
||||
<AgentCredentialForm
|
||||
btnLoading={this.state.btnLoadingAgent}
|
||||
needAuth={this.state.needAuth}
|
||||
form={this.props.form}
|
||||
initialValue={{
|
||||
...editValue,
|
||||
username: editValue.agent_basic_auth?.username,
|
||||
password: editValue.agent_basic_auth?.password,
|
||||
}}
|
||||
isManual={this.state.isManual}
|
||||
isEdit={true}
|
||||
tryConnect={this.tryConnect}
|
||||
credentialRequired={this.state.agentCredentialRequired}
|
||||
/>
|
||||
)
|
||||
}
|
||||
<MonitorConfigsForm
|
||||
form={this.props.form}
|
||||
editValue={editValue}
|
||||
visible={this.state.monitored}
|
||||
collectMode={this.state.collectMode}
|
||||
/>
|
||||
<MetadataConfigsForm
|
||||
form={this.props.form}
|
||||
|
|
|
@ -24,7 +24,10 @@ const MonitorConfigsForm = (props) => {
|
|||
display: props.visible ? "block" : "none",
|
||||
}}
|
||||
>
|
||||
{configs.map((item) => {
|
||||
{configs.filter((item) => {
|
||||
if (props.collectMode !== "agent") return true;
|
||||
return !["node_stats", "index_stats"].includes(item.key);
|
||||
}).map((item) => {
|
||||
return (
|
||||
<Form.Item
|
||||
key={item.key}
|
||||
|
|
Loading…
Reference in New Issue