feat
This commit is contained in:
@@ -80,6 +80,8 @@ export interface HostHardwareDeviceUpsert {
|
|||||||
snmp_collect_enabled?: boolean
|
snmp_collect_enabled?: boolean
|
||||||
redfish_collect_enabled?: boolean
|
redfish_collect_enabled?: boolean
|
||||||
collect_interval?: number
|
collect_interval?: number
|
||||||
|
/** 是否启用监控调度 */
|
||||||
|
enabled?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface HostHardwareDeviceListPayload {
|
export interface HostHardwareDeviceListPayload {
|
||||||
@@ -191,9 +193,7 @@ export function isHostHardwareApiSuccess(res: HostHardwareApiEnvelope | null | u
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
export function unwrapHostHardwareDetails<T>(
|
export function unwrapHostHardwareDetails<T>(res: (HostHardwareApiEnvelope<T> & { data?: T }) | null | undefined): T | null {
|
||||||
res: (HostHardwareApiEnvelope<T> & { data?: T }) | null | undefined,
|
|
||||||
): T | null {
|
|
||||||
if (!res || !isHostHardwareApiSuccess(res)) return null
|
if (!res || !isHostHardwareApiSuccess(res)) return null
|
||||||
// 部分网关/SDK 将载荷放在 data 而非 details,与 logs 等模块一致做兼容
|
// 部分网关/SDK 将载荷放在 data 而非 details,与 logs 等模块一致做兼容
|
||||||
return res.details ?? res.data ?? null
|
return res.details ?? res.data ?? null
|
||||||
@@ -203,15 +203,13 @@ export function unwrapHostHardwareDetails<T>(
|
|||||||
export function fetchHostHardwareLatestCollection(serverIdentity: string) {
|
export function fetchHostHardwareLatestCollection(serverIdentity: string) {
|
||||||
return request.get<HostHardwareApiEnvelope<HostHardwareLatestCollectionPayload>>(
|
return request.get<HostHardwareApiEnvelope<HostHardwareLatestCollectionPayload>>(
|
||||||
`${HW_PREFIX}/devices/by-server-identity/collection/latest`,
|
`${HW_PREFIX}/devices/by-server-identity/collection/latest`,
|
||||||
{ params: { server_identity: serverIdentity } },
|
{ params: { server_identity: serverIdentity } }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 设备详情(含最新一条 status) */
|
/** 设备详情(含最新一条 status) */
|
||||||
export function fetchHostHardwareDevice(deviceId: string) {
|
export function fetchHostHardwareDevice(deviceId: string) {
|
||||||
return request.get<HostHardwareApiEnvelope<HostHardwareDeviceDetailPayload>>(
|
return request.get<HostHardwareApiEnvelope<HostHardwareDeviceDetailPayload>>(`${HW_PREFIX}/devices/${encodeURIComponent(deviceId)}`)
|
||||||
`${HW_PREFIX}/devices/${encodeURIComponent(deviceId)}`,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 分页列表(可按 type / status / asset_id 筛选) */
|
/** 分页列表(可按 type / status / asset_id 筛选) */
|
||||||
@@ -234,10 +232,7 @@ export function createHostHardwareDevice(data: HostHardwareDeviceUpsert) {
|
|||||||
|
|
||||||
/** 更新设备(全量必填字段;密码留空则不修改) */
|
/** 更新设备(全量必填字段;密码留空则不修改) */
|
||||||
export function updateHostHardwareDevice(deviceId: string, data: HostHardwareDeviceUpsert) {
|
export function updateHostHardwareDevice(deviceId: string, data: HostHardwareDeviceUpsert) {
|
||||||
return request.put<HostHardwareApiEnvelope<HostHardwareDevice>>(
|
return request.put<HostHardwareApiEnvelope<HostHardwareDevice>>(`${HW_PREFIX}/devices/${encodeURIComponent(deviceId)}`, data)
|
||||||
`${HW_PREFIX}/devices/${encodeURIComponent(deviceId)}`,
|
|
||||||
data,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 异步立即采集 */
|
/** 异步立即采集 */
|
||||||
@@ -256,12 +251,7 @@ export function disableHostHardwareDevice(deviceId: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** 单指标历史曲线(JWT) */
|
/** 单指标历史曲线(JWT) */
|
||||||
export function fetchHostHardwareMetricHistory(
|
export function fetchHostHardwareMetricHistory(deviceId: string, metricName: string, startTime?: string, endTime?: string) {
|
||||||
deviceId: string,
|
|
||||||
metricName: string,
|
|
||||||
startTime?: string,
|
|
||||||
endTime?: string,
|
|
||||||
) {
|
|
||||||
return request.get<HostHardwareApiEnvelope<HostHardwareMetricHistoryPayload>>(
|
return request.get<HostHardwareApiEnvelope<HostHardwareMetricHistoryPayload>>(
|
||||||
`${HW_PREFIX}/metrics/devices/${encodeURIComponent(deviceId)}/history`,
|
`${HW_PREFIX}/metrics/devices/${encodeURIComponent(deviceId)}/history`,
|
||||||
{
|
{
|
||||||
@@ -270,26 +260,19 @@ export function fetchHostHardwareMetricHistory(
|
|||||||
...(startTime ? { start_time: startTime } : {}),
|
...(startTime ? { start_time: startTime } : {}),
|
||||||
...(endTime ? { end_time: endTime } : {}),
|
...(endTime ? { end_time: endTime } : {}),
|
||||||
},
|
},
|
||||||
},
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 日汇总统计 */
|
/** 日汇总统计 */
|
||||||
export function fetchHostHardwareStatistics(
|
export function fetchHostHardwareStatistics(deviceId: string, startDate?: string, endDate?: string) {
|
||||||
deviceId: string,
|
return request.get<HostHardwareApiEnvelope<HostHardwareStatisticsRow[]>>(`${HW_PREFIX}/metrics/statistics`, {
|
||||||
startDate?: string,
|
params: {
|
||||||
endDate?: string,
|
device_id: deviceId,
|
||||||
) {
|
...(startDate ? { start_date: startDate } : {}),
|
||||||
return request.get<HostHardwareApiEnvelope<HostHardwareStatisticsRow[]>>(
|
...(endDate ? { end_date: endDate } : {}),
|
||||||
`${HW_PREFIX}/metrics/statistics`,
|
|
||||||
{
|
|
||||||
params: {
|
|
||||||
device_id: deviceId,
|
|
||||||
...(startDate ? { start_date: startDate } : {}),
|
|
||||||
...(endDate ? { end_date: endDate } : {}),
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
)
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -297,7 +280,7 @@ export function fetchHostHardwareStatistics(
|
|||||||
*/
|
*/
|
||||||
export function normalizeHostHardwareMetrics(
|
export function normalizeHostHardwareMetrics(
|
||||||
status: HostHardwareStatus | null | undefined,
|
status: HostHardwareStatus | null | undefined,
|
||||||
metricsFromApi: HostHardwareMetricsRow[] | null | undefined,
|
metricsFromApi: HostHardwareMetricsRow[] | null | undefined
|
||||||
): NormalizedHostHardwareMetric[] {
|
): NormalizedHostHardwareMetric[] {
|
||||||
const fromApi = metricsFromApi ?? []
|
const fromApi = metricsFromApi ?? []
|
||||||
if (fromApi.length > 0) {
|
if (fromApi.length > 0) {
|
||||||
|
|||||||
@@ -11,208 +11,171 @@
|
|||||||
unmount-on-close
|
unmount-on-close
|
||||||
@ok="handleSubmit"
|
@ok="handleSubmit"
|
||||||
@cancel="handleCancel"
|
@cancel="handleCancel"
|
||||||
@update:visible="(v: boolean) => emit('update:visible', v)"
|
|
||||||
>
|
>
|
||||||
<div class="modal-scroll">
|
<div class="modal-scroll">
|
||||||
<a-spin :loading="loading" style="width: 100%; min-height: 200px">
|
<a-spin :loading="loading" style="width: 98%; margin: 0 auto; min-height: 200px">
|
||||||
<div v-if="!loading && blockedNoIdentity" class="blocked-wrap">
|
<div v-if="!loading && blockedNoIdentity" class="blocked-wrap">
|
||||||
<a-alert type="warning" show-icon>
|
<a-alert type="warning" show-icon>
|
||||||
当前服务器未配置唯一标识。请先在「编辑服务器」中填写并保存,以便与 DC-Hardware 带外设备关联。
|
当前服务器未配置唯一标识。请先在「编辑服务器」中填写并保存,以便与 DC-Hardware 带外设备关联。
|
||||||
</a-alert>
|
</a-alert>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<template v-else-if="!loading">
|
<template v-else-if="!loading">
|
||||||
|
<a-form ref="formRef" :model="form" :rules="rules" layout="vertical">
|
||||||
|
<a-divider orientation="left">基础信息</a-divider>
|
||||||
|
<a-row :gutter="16">
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item label="管理 IP(BMC / 带外)">
|
||||||
|
<span class="readonly-field">{{ managementIp || '—' }}</span>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item field="collect_interval" label="采集间隔(秒)">
|
||||||
|
<a-input-number v-model="form.collect_interval" :min="0" style="width: 100%" />
|
||||||
|
<template #extra>
|
||||||
|
<span class="form-extra">0 或留空表示使用服务默认间隔</span>
|
||||||
|
</template>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="24">
|
||||||
|
<a-form-item field="description" label="备注">
|
||||||
|
<a-textarea v-model="form.description" :rows="2" placeholder="可选" allow-clear />
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="24">
|
||||||
|
<a-form-item field="tags" label="标签">
|
||||||
|
<a-input v-model="form.tags" placeholder="可为 JSON 字符串,如实体传感器配置等" allow-clear />
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
|
||||||
|
<a-divider orientation="left">采集协议与连接</a-divider>
|
||||||
<a-form ref="formRef" :model="form" :rules="rules" layout="vertical">
|
<a-form-item field="protocol" label="协议" required>
|
||||||
<a-collapse :default-active-key="['base', 'proto', 'collect']" :bordered="false">
|
<a-radio-group :model-value="form.protocol" type="button" @update:model-value="onProtocolModelUpdate">
|
||||||
<a-collapse-item key="base" header="基础信息">
|
<a-radio value="ipmi">IPMI</a-radio>
|
||||||
<a-row :gutter="16">
|
<a-radio value="snmp">SNMP</a-radio>
|
||||||
<a-col :span="24">
|
<a-radio value="redfish">Redfish</a-radio>
|
||||||
<a-form-item label="管理 IP(BMC / 带外)">
|
</a-radio-group>
|
||||||
<span class="readonly-field">{{ managementIp || '—' }}</span>
|
<template #extra>
|
||||||
</a-form-item>
|
<span class="form-extra">切换协议后请填写对应区域的认证与端口;默认值:IPMI 623、SNMP 161</span>
|
||||||
</a-col>
|
|
||||||
<a-col :span="24">
|
|
||||||
<a-form-item field="description" label="备注">
|
|
||||||
<a-textarea v-model="form.description" :rows="2" placeholder="可选" allow-clear />
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
<a-col :span="24">
|
|
||||||
<a-form-item field="tags" label="标签">
|
|
||||||
<a-input v-model="form.tags" placeholder="可为 JSON 字符串,如实体传感器配置等" allow-clear />
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
</a-row>
|
|
||||||
</a-collapse-item>
|
|
||||||
|
|
||||||
<a-collapse-item key="proto" header="采集协议与连接">
|
|
||||||
<a-form-item field="protocol" label="协议" required>
|
|
||||||
<a-radio-group
|
|
||||||
:model-value="form.protocol"
|
|
||||||
type="button"
|
|
||||||
@update:model-value="onProtocolModelUpdate"
|
|
||||||
>
|
|
||||||
<a-radio value="ipmi">IPMI</a-radio>
|
|
||||||
<a-radio value="snmp">SNMP</a-radio>
|
|
||||||
<a-radio value="redfish">Redfish</a-radio>
|
|
||||||
</a-radio-group>
|
|
||||||
<template #extra>
|
|
||||||
<span class="form-extra">切换协议后请填写对应区域的认证与端口;默认值:IPMI 623、SNMP 161</span>
|
|
||||||
</template>
|
|
||||||
</a-form-item>
|
|
||||||
|
|
||||||
<!-- IPMI / Redfish 共用账号 -->
|
|
||||||
<template v-if="form.protocol === 'ipmi' || form.protocol === 'redfish'">
|
|
||||||
<a-row :gutter="16">
|
|
||||||
<a-col :span="12">
|
|
||||||
<a-form-item field="username" label="用户名">
|
|
||||||
<a-input v-model="form.username" placeholder="BMC 用户名" allow-clear />
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
<a-col :span="12">
|
|
||||||
<a-form-item field="password" label="密码">
|
|
||||||
<a-input-password v-model="form.password" :placeholder="passwordPlaceholder" allow-clear />
|
|
||||||
<template #extra>
|
|
||||||
<span class="form-extra">{{ passwordHint }}</span>
|
|
||||||
</template>
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
</a-row>
|
|
||||||
</template>
|
</template>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
<template v-if="form.protocol === 'ipmi'">
|
<template v-if="form.protocol === 'ipmi' || form.protocol === 'redfish'">
|
||||||
<a-row :gutter="16">
|
|
||||||
<a-col :span="8">
|
|
||||||
<a-form-item field="port" label="IPMI 端口">
|
|
||||||
<a-input-number v-model="form.port" :min="1" :max="65535" style="width: 100%" />
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
<a-col :span="8">
|
|
||||||
<a-form-item field="ipmi_timeout_seconds" label="超时(秒)">
|
|
||||||
<a-input-number v-model="form.ipmi_timeout_seconds" :min="0" style="width: 100%" />
|
|
||||||
<template #extra><span class="form-extra">0 表示使用服务默认</span></template>
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
<a-col :span="8">
|
|
||||||
<a-form-item label="启用 IPMI 采集">
|
|
||||||
<a-switch v-model="form.ipmi_collect_enabled" />
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
</a-row>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template v-if="form.protocol === 'snmp'">
|
|
||||||
<a-row :gutter="16">
|
|
||||||
<a-col :span="8">
|
|
||||||
<a-form-item field="snmp_port" label="SNMP 端口">
|
|
||||||
<a-input-number v-model="form.snmp_port" :min="1" :max="65535" style="width: 100%" />
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
<a-col :span="8">
|
|
||||||
<a-form-item field="snmp_version" label="SNMP 版本">
|
|
||||||
<a-select v-model="form.snmp_version" allow-clear>
|
|
||||||
<a-option value="v2c">v2c</a-option>
|
|
||||||
<a-option value="v1">v1</a-option>
|
|
||||||
</a-select>
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
<a-col :span="8">
|
|
||||||
<a-form-item field="snmp_timeout_seconds" label="超时(秒)">
|
|
||||||
<a-input-number v-model="form.snmp_timeout_seconds" :min="0" style="width: 100%" />
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
<a-col :span="12">
|
|
||||||
<a-form-item field="community" label="Community 共享口令">
|
|
||||||
<a-input v-model="form.community" placeholder="v2c Community" allow-clear />
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
<a-col :span="12">
|
|
||||||
<a-form-item label="启用 SNMP 采集">
|
|
||||||
<a-switch v-model="form.snmp_collect_enabled" />
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
</a-row>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template v-if="form.protocol === 'redfish'">
|
|
||||||
<a-row :gutter="16">
|
|
||||||
<a-col :span="24">
|
|
||||||
<a-form-item field="redfish_base_url" label="Redfish 根 URL">
|
|
||||||
<a-input
|
|
||||||
v-model="form.redfish_base_url"
|
|
||||||
placeholder="留空则默认 https://{管理IP}/redfish/v1"
|
|
||||||
allow-clear
|
|
||||||
/>
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
<a-col :span="8">
|
|
||||||
<a-form-item label="跳过 TLS 校验">
|
|
||||||
<a-switch v-model="form.redfish_tls_skip_verify" />
|
|
||||||
<template #extra><span class="form-extra">仅内网测试环境建议开启</span></template>
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
<a-col :span="8">
|
|
||||||
<a-form-item field="redfish_timeout_seconds" label="超时(秒)">
|
|
||||||
<a-input-number v-model="form.redfish_timeout_seconds" :min="0" style="width: 100%" />
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
<a-col :span="8">
|
|
||||||
<a-form-item label="启用 Redfish 采集">
|
|
||||||
<a-switch v-model="form.redfish_collect_enabled" />
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
</a-row>
|
|
||||||
</template>
|
|
||||||
</a-collapse-item>
|
|
||||||
|
|
||||||
<a-collapse-item key="collect" header="采集调度与扩展">
|
|
||||||
<a-row :gutter="16">
|
<a-row :gutter="16">
|
||||||
<a-col :span="12">
|
<a-col :span="12">
|
||||||
<a-form-item field="collect_interval" label="采集间隔(秒)">
|
<a-form-item field="username" label="用户名">
|
||||||
<a-input-number v-model="form.collect_interval" :min="0" style="width: 100%" />
|
<a-input v-model="form.username" placeholder="BMC 用户名" allow-clear />
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item field="password" label="密码">
|
||||||
|
<a-input-password v-model="form.password" :placeholder="passwordPlaceholder" allow-clear />
|
||||||
<template #extra>
|
<template #extra>
|
||||||
<span class="form-extra">0 或留空表示使用服务默认间隔(YAML)</span>
|
<span class="form-extra">{{ passwordHint }}</span>
|
||||||
</template>
|
</template>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col v-if="isEdit && hwEnabled !== undefined" :span="12">
|
</a-row>
|
||||||
<a-form-item label="监控开关(只读)">
|
</template>
|
||||||
<a-space>
|
|
||||||
<a-tag :color="hwEnabled ? 'green' : 'gray'">{{ hwEnabled ? '已启用' : '已禁用' }}</a-tag>
|
<template v-if="form.protocol === 'ipmi'">
|
||||||
<span class="form-extra">保存后可用下方快捷操作切换</span>
|
<a-row :gutter="16">
|
||||||
</a-space>
|
<a-col :span="8">
|
||||||
|
<a-form-item field="port" label="IPMI 端口">
|
||||||
|
<a-input-number v-model="form.port" :min="1" :max="65535" style="width: 100%" />
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :span="24">
|
<a-col :span="8">
|
||||||
<a-form-item field="extra_config" label="扩展配置(JSON 字符串)">
|
<a-form-item field="ipmi_timeout_seconds" label="超时(秒)">
|
||||||
<a-textarea
|
<a-input-number v-model="form.ipmi_timeout_seconds" :min="0" style="width: 100%" />
|
||||||
v-model="form.extra_config"
|
<template #extra><span class="form-extra">0 表示使用服务默认</span></template>
|
||||||
:rows="4"
|
</a-form-item>
|
||||||
placeholder='可选,非空须为合法 JSON,如 {"key":"value"}'
|
</a-col>
|
||||||
allow-clear
|
<a-col :span="8">
|
||||||
/>
|
<a-form-item label="启用 IPMI 采集">
|
||||||
|
<a-switch v-model="form.ipmi_collect_enabled" />
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
</a-col>
|
||||||
</a-row>
|
</a-row>
|
||||||
</a-collapse-item>
|
</template>
|
||||||
</a-collapse>
|
|
||||||
|
|
||||||
<a-divider v-if="isEdit && deviceId" orientation="left">快捷操作</a-divider>
|
<template v-if="form.protocol === 'snmp'">
|
||||||
<a-space v-if="isEdit && deviceId" wrap>
|
<a-row :gutter="16">
|
||||||
<a-button type="outline" :loading="actionLoading === 'collect'" @click="doCollect">
|
<a-col :span="8">
|
||||||
立即采集
|
<a-form-item field="snmp_port" label="SNMP 端口">
|
||||||
</a-button>
|
<a-input-number v-model="form.snmp_port" :min="1" :max="65535" style="width: 100%" />
|
||||||
<a-button v-if="!hwEnabled" type="outline" status="success" :loading="actionLoading === 'enable'" @click="doEnable">
|
</a-form-item>
|
||||||
启用监控
|
</a-col>
|
||||||
</a-button>
|
<a-col :span="8">
|
||||||
<a-button v-else type="outline" status="warning" :loading="actionLoading === 'disable'" @click="doDisable">
|
<a-form-item field="snmp_version" label="SNMP 版本">
|
||||||
禁用监控
|
<a-select v-model="form.snmp_version" allow-clear>
|
||||||
</a-button>
|
<a-option value="v2c">v2c</a-option>
|
||||||
</a-space>
|
<a-option value="v1">v1</a-option>
|
||||||
</a-form>
|
</a-select>
|
||||||
</template>
|
</a-form-item>
|
||||||
</a-spin>
|
</a-col>
|
||||||
|
<a-col :span="8">
|
||||||
|
<a-form-item field="snmp_timeout_seconds" label="超时(秒)">
|
||||||
|
<a-input-number v-model="form.snmp_timeout_seconds" :min="0" style="width: 100%" />
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item field="community" label="Community 共享口令">
|
||||||
|
<a-input v-model="form.community" placeholder="v2c Community" allow-clear />
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item label="启用 SNMP 采集">
|
||||||
|
<a-switch v-model="form.snmp_collect_enabled" />
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template v-if="form.protocol === 'redfish'">
|
||||||
|
<a-row :gutter="16">
|
||||||
|
<a-col :span="24">
|
||||||
|
<a-form-item field="redfish_base_url" label="Redfish 根 URL">
|
||||||
|
<a-input v-model="form.redfish_base_url" placeholder="留空则默认 https://{管理IP}/redfish/v1" allow-clear />
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="8">
|
||||||
|
<a-form-item label="跳过 TLS 校验">
|
||||||
|
<a-switch v-model="form.redfish_tls_skip_verify" />
|
||||||
|
<template #extra><span class="form-extra">仅内网测试环境建议开启</span></template>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="8">
|
||||||
|
<a-form-item field="redfish_timeout_seconds" label="超时(秒)">
|
||||||
|
<a-input-number v-model="form.redfish_timeout_seconds" :min="0" style="width: 100%" />
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="8">
|
||||||
|
<a-form-item label="启用 Redfish 采集">
|
||||||
|
<a-switch v-model="form.redfish_collect_enabled" />
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<a-divider orientation="left">扩展配置</a-divider>
|
||||||
|
<a-row :gutter="16">
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item label="启用监控">
|
||||||
|
<a-switch v-model="form.enabled" />
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="24">
|
||||||
|
<a-form-item field="extra_config" label="扩展配置(JSON 字符串)">
|
||||||
|
<a-textarea v-model="form.extra_config" :rows="4" placeholder='可选,非空须为合法 JSON,如 {"key":"value"}' allow-clear />
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
|
||||||
|
</a-form>
|
||||||
|
</template>
|
||||||
|
</a-spin>
|
||||||
</div>
|
</div>
|
||||||
</a-modal>
|
</a-modal>
|
||||||
</template>
|
</template>
|
||||||
@@ -228,9 +191,6 @@ import {
|
|||||||
fetchHostHardwareDeviceList,
|
fetchHostHardwareDeviceList,
|
||||||
createHostHardwareDevice,
|
createHostHardwareDevice,
|
||||||
updateHostHardwareDevice,
|
updateHostHardwareDevice,
|
||||||
triggerHostHardwareCollect,
|
|
||||||
enableHostHardwareDevice,
|
|
||||||
disableHostHardwareDevice,
|
|
||||||
isHostHardwareApiSuccess,
|
isHostHardwareApiSuccess,
|
||||||
unwrapHostHardwareDetails,
|
unwrapHostHardwareDetails,
|
||||||
type HostHardwareDevice,
|
type HostHardwareDevice,
|
||||||
@@ -250,8 +210,6 @@ const loading = ref(false)
|
|||||||
const submitLoading = ref(false)
|
const submitLoading = ref(false)
|
||||||
const blockedNoIdentity = ref(false)
|
const blockedNoIdentity = ref(false)
|
||||||
const deviceId = ref<string | null>(null)
|
const deviceId = ref<string | null>(null)
|
||||||
const hwEnabled = ref<boolean | undefined>(undefined)
|
|
||||||
const actionLoading = ref<'collect' | 'enable' | 'disable' | ''>('')
|
|
||||||
|
|
||||||
/** 防止多次触发 loadHardware 时,较早返回的请求覆盖用户已切换的协议/表单 */
|
/** 防止多次触发 loadHardware 时,较早返回的请求覆盖用户已切换的协议/表单 */
|
||||||
let hardwareLoadGeneration = 0
|
let hardwareLoadGeneration = 0
|
||||||
@@ -285,13 +243,9 @@ const managementIp = computed(() => {
|
|||||||
const hardwareType = ref('server')
|
const hardwareType = ref('server')
|
||||||
const hardwareAssetId = ref('')
|
const hardwareAssetId = ref('')
|
||||||
|
|
||||||
const passwordPlaceholder = computed(() =>
|
const passwordPlaceholder = computed(() => (isEdit.value ? '留空则不修改已保存的密码' : 'BMC / Redfish 密码'))
|
||||||
isEdit.value ? '留空则不修改已保存的密码' : 'BMC / Redfish 密码',
|
|
||||||
)
|
|
||||||
|
|
||||||
const passwordHint = computed(() =>
|
const passwordHint = computed(() => (isEdit.value ? '编辑时留空将保留原密码' : '按机房安全要求妥善保管'))
|
||||||
isEdit.value ? '编辑时留空将保留原密码' : '按机房安全要求妥善保管',
|
|
||||||
)
|
|
||||||
|
|
||||||
const form = reactive({
|
const form = reactive({
|
||||||
protocol: 'ipmi' as 'ipmi' | 'snmp' | 'redfish',
|
protocol: 'ipmi' as 'ipmi' | 'snmp' | 'redfish',
|
||||||
@@ -313,6 +267,7 @@ const form = reactive({
|
|||||||
snmp_collect_enabled: true,
|
snmp_collect_enabled: true,
|
||||||
redfish_collect_enabled: true,
|
redfish_collect_enabled: true,
|
||||||
collect_interval: 0,
|
collect_interval: 0,
|
||||||
|
enabled: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
const rules: Record<string, FieldRule | FieldRule[]> = {
|
const rules: Record<string, FieldRule | FieldRule[]> = {
|
||||||
@@ -341,6 +296,7 @@ function resetForm() {
|
|||||||
form.snmp_collect_enabled = true
|
form.snmp_collect_enabled = true
|
||||||
form.redfish_collect_enabled = true
|
form.redfish_collect_enabled = true
|
||||||
form.collect_interval = 0
|
form.collect_interval = 0
|
||||||
|
form.enabled = true
|
||||||
}
|
}
|
||||||
|
|
||||||
function applyFromServer(r: ServerItem) {
|
function applyFromServer(r: ServerItem) {
|
||||||
@@ -372,7 +328,7 @@ function applyFromDevice(d: HostHardwareDevice) {
|
|||||||
form.snmp_collect_enabled = d.snmp_collect_enabled !== false
|
form.snmp_collect_enabled = d.snmp_collect_enabled !== false
|
||||||
form.redfish_collect_enabled = d.redfish_collect_enabled !== false
|
form.redfish_collect_enabled = d.redfish_collect_enabled !== false
|
||||||
form.collect_interval = d.collect_interval ?? 0
|
form.collect_interval = d.collect_interval ?? 0
|
||||||
hwEnabled.value = d.enabled
|
form.enabled = d.enabled !== false
|
||||||
}
|
}
|
||||||
|
|
||||||
function validateExtraConfigJson(): boolean {
|
function validateExtraConfigJson(): boolean {
|
||||||
@@ -417,6 +373,7 @@ function buildPayload(): HostHardwareDeviceUpsert {
|
|||||||
snmp_collect_enabled: form.snmp_collect_enabled,
|
snmp_collect_enabled: form.snmp_collect_enabled,
|
||||||
redfish_collect_enabled: form.redfish_collect_enabled,
|
redfish_collect_enabled: form.redfish_collect_enabled,
|
||||||
collect_interval: form.collect_interval,
|
collect_interval: form.collect_interval,
|
||||||
|
enabled: form.enabled,
|
||||||
}
|
}
|
||||||
const pw = form.password?.trim()
|
const pw = form.password?.trim()
|
||||||
if (pw) {
|
if (pw) {
|
||||||
@@ -454,7 +411,6 @@ async function loadDeviceByServerIdentityFallback(sid: string, gen: number): Pro
|
|||||||
async function loadHardware() {
|
async function loadHardware() {
|
||||||
const gen = ++hardwareLoadGeneration
|
const gen = ++hardwareLoadGeneration
|
||||||
deviceId.value = null
|
deviceId.value = null
|
||||||
hwEnabled.value = undefined
|
|
||||||
blockedNoIdentity.value = false
|
blockedNoIdentity.value = false
|
||||||
if (!props.record) {
|
if (!props.record) {
|
||||||
return
|
return
|
||||||
@@ -474,6 +430,7 @@ async function loadHardware() {
|
|||||||
if (isHostHardwareApiSuccess(colRes)) {
|
if (isHostHardwareApiSuccess(colRes)) {
|
||||||
const col = unwrapHostHardwareDetails(colRes)
|
const col = unwrapHostHardwareDetails(colRes)
|
||||||
const did = col?.device_id
|
const did = col?.device_id
|
||||||
|
deviceId.value = col?.device_id
|
||||||
if (did) {
|
if (did) {
|
||||||
const detailOk = await loadDeviceDetailIntoForm(did, gen)
|
const detailOk = await loadDeviceDetailIntoForm(did, gen)
|
||||||
if (gen !== hardwareLoadGeneration) return
|
if (gen !== hardwareLoadGeneration) return
|
||||||
@@ -516,7 +473,7 @@ watch(
|
|||||||
deviceId.value = null
|
deviceId.value = null
|
||||||
blockedNoIdentity.value = false
|
blockedNoIdentity.value = false
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
async function handleSubmit() {
|
async function handleSubmit() {
|
||||||
@@ -531,6 +488,7 @@ async function handleSubmit() {
|
|||||||
if (!validateExtraConfigJson()) return
|
if (!validateExtraConfigJson()) return
|
||||||
|
|
||||||
const r = props.record
|
const r = props.record
|
||||||
|
console.log('r,', r)
|
||||||
if (!(r?.name || '').trim()) {
|
if (!(r?.name || '').trim()) {
|
||||||
Message.error('服务器名称为空,请先在编辑服务器中填写名称')
|
Message.error('服务器名称为空,请先在编辑服务器中填写名称')
|
||||||
return
|
return
|
||||||
@@ -541,13 +499,14 @@ async function handleSubmit() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
submitLoading.value = true
|
submitLoading.value = true
|
||||||
|
console.log('deviceId.value,', deviceId.value)
|
||||||
try {
|
try {
|
||||||
const payload = buildPayload()
|
const payload = buildPayload()
|
||||||
let res: { code?: number | string; message?: string }
|
let res: { code?: number | string; message?: string }
|
||||||
if (deviceId.value) {
|
if (deviceId.value) {
|
||||||
res = await updateHostHardwareDevice(deviceId.value, payload) as typeof res
|
res = (await updateHostHardwareDevice(deviceId.value, payload)) as typeof res
|
||||||
} else {
|
} else {
|
||||||
res = await createHostHardwareDevice(payload) as typeof res
|
res = (await createHostHardwareDevice(payload)) as typeof res
|
||||||
}
|
}
|
||||||
if (isHostHardwareApiSuccess(res)) {
|
if (isHostHardwareApiSuccess(res)) {
|
||||||
Message.success(deviceId.value ? '修改成功' : '保存成功')
|
Message.success(deviceId.value ? '修改成功' : '保存成功')
|
||||||
@@ -557,8 +516,9 @@ async function handleSubmit() {
|
|||||||
Message.error((res as { message?: string }).message || '保存失败')
|
Message.error((res as { message?: string }).message || '保存失败')
|
||||||
}
|
}
|
||||||
} catch (e: unknown) {
|
} catch (e: unknown) {
|
||||||
const msg = (e as { response?: { data?: { message?: string } }; message?: string })?.response?.data?.message
|
const msg =
|
||||||
|| (e as { message?: string })?.message
|
(e as { response?: { data?: { message?: string } }; message?: string })?.response?.data?.message ||
|
||||||
|
(e as { message?: string })?.message
|
||||||
Message.error(msg || '保存失败')
|
Message.error(msg || '保存失败')
|
||||||
} finally {
|
} finally {
|
||||||
submitLoading.value = false
|
submitLoading.value = false
|
||||||
@@ -568,59 +528,6 @@ async function handleSubmit() {
|
|||||||
function handleCancel() {
|
function handleCancel() {
|
||||||
emit('update:visible', false)
|
emit('update:visible', false)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function doCollect() {
|
|
||||||
if (!deviceId.value) return
|
|
||||||
actionLoading.value = 'collect'
|
|
||||||
try {
|
|
||||||
const res = await triggerHostHardwareCollect(deviceId.value)
|
|
||||||
if (isHostHardwareApiSuccess(res)) {
|
|
||||||
Message.success('采集任务已启动')
|
|
||||||
} else {
|
|
||||||
Message.error((res as { message?: string }).message || '操作失败')
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
Message.error('请求失败')
|
|
||||||
} finally {
|
|
||||||
actionLoading.value = ''
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function doEnable() {
|
|
||||||
if (!deviceId.value) return
|
|
||||||
actionLoading.value = 'enable'
|
|
||||||
try {
|
|
||||||
const res = await enableHostHardwareDevice(deviceId.value)
|
|
||||||
if (isHostHardwareApiSuccess(res)) {
|
|
||||||
Message.success('已启用监控')
|
|
||||||
hwEnabled.value = true
|
|
||||||
} else {
|
|
||||||
Message.error((res as { message?: string }).message || '操作失败')
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
Message.error('请求失败')
|
|
||||||
} finally {
|
|
||||||
actionLoading.value = ''
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function doDisable() {
|
|
||||||
if (!deviceId.value) return
|
|
||||||
actionLoading.value = 'disable'
|
|
||||||
try {
|
|
||||||
const res = await disableHostHardwareDevice(deviceId.value)
|
|
||||||
if (isHostHardwareApiSuccess(res)) {
|
|
||||||
Message.success('已禁用监控')
|
|
||||||
hwEnabled.value = false
|
|
||||||
} else {
|
|
||||||
Message.error((res as { message?: string }).message || '操作失败')
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
Message.error('请求失败')
|
|
||||||
} finally {
|
|
||||||
actionLoading.value = ''
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user