feat(admin/vm-network): 网络列表增加is_primary主IP标识+设为主IP+重置MAC地址 -- 缘由: 后端新增network/set_primary和vm/reset_mac接口 -- 预期: VmDetail与UserVmDetail网络列表显示主IP标签,非主IP行有设为主IP按钮,更多菜单增加重置MAC地址
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -284,6 +284,20 @@ export const deleteNetwork = (params) => {
|
|||||||
return http2.delete('/api/v1/admin/server/host_service/point/network/delete', { params })
|
return http2.delete('/api/v1/admin/server/host_service/point/network/delete', { params })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 设置主IP */
|
||||||
|
export const setNetworkPrimary = (data) => {
|
||||||
|
return http2.post('/api/v1/admin/server/host_service/point/network/set_primary', data, {
|
||||||
|
headers: { 'Content-Type': 'multipart/form-data' }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 重置虚拟机MAC地址 */
|
||||||
|
export const resetVmMac = (data) => {
|
||||||
|
return http2.post('/api/v1/admin/server/host_service/point/vm/reset_mac', data, {
|
||||||
|
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ================================
|
* ================================
|
||||||
* 主控服务接口 - 数据卷管理
|
* 主控服务接口 - 数据卷管理
|
||||||
|
|||||||
@@ -88,6 +88,8 @@ export const deleteUserVmPostGroupRule = (params) => http2.delete(`${BASE}/post_
|
|||||||
// ========== 网络 ==========
|
// ========== 网络 ==========
|
||||||
export const getUserVmNetworkList = (params) => http2.get(`${BASE}/network/list`, { params })
|
export const getUserVmNetworkList = (params) => http2.get(`${BASE}/network/list`, { params })
|
||||||
export const getUserVmNetworkDetail = (params) => http2.get(`${BASE}/network/detail`, { params })
|
export const getUserVmNetworkDetail = (params) => http2.get(`${BASE}/network/detail`, { params })
|
||||||
|
export const setUserVmNetworkPrimary = (data) => http2.post(`${BASE}/network/set_primary`, fd(data), { headers: { 'Content-Type': 'multipart/form-data' } })
|
||||||
|
export const resetUserVmMac = (params) => http2.post(`${BASE}/reset_mac`, null, { params })
|
||||||
|
|
||||||
// ========== 组网 ==========
|
// ========== 组网 ==========
|
||||||
export const getUserVmNetworkingList = (params) => http2.get(`${BASE}/networking/list`, { params })
|
export const getUserVmNetworkingList = (params) => http2.get(`${BASE}/networking/list`, { params })
|
||||||
|
|||||||
@@ -46,6 +46,7 @@
|
|||||||
<el-dropdown-item command="resume">恢复</el-dropdown-item>
|
<el-dropdown-item command="resume">恢复</el-dropdown-item>
|
||||||
<el-dropdown-item command="rescue" :disabled="!!vm?.rescue">救援模式</el-dropdown-item>
|
<el-dropdown-item command="rescue" :disabled="!!vm?.rescue">救援模式</el-dropdown-item>
|
||||||
<el-dropdown-item command="exitRescue" :disabled="!vm?.rescue">退出救援</el-dropdown-item>
|
<el-dropdown-item command="exitRescue" :disabled="!vm?.rescue">退出救援</el-dropdown-item>
|
||||||
|
<el-dropdown-item command="resetMac">重置MAC地址</el-dropdown-item>
|
||||||
<el-dropdown-item divided command="rebuild">重装系统</el-dropdown-item>
|
<el-dropdown-item divided command="rebuild">重装系统</el-dropdown-item>
|
||||||
<el-dropdown-item command="updateVm">编辑虚拟机</el-dropdown-item>
|
<el-dropdown-item command="updateVm">编辑虚拟机</el-dropdown-item>
|
||||||
<el-dropdown-item command="refactorVm">重构虚拟机</el-dropdown-item>
|
<el-dropdown-item command="refactorVm">重构虚拟机</el-dropdown-item>
|
||||||
@@ -329,9 +330,15 @@
|
|||||||
<el-table-column prop="address" label="地址(CIDR)" min-width="150" />
|
<el-table-column prop="address" label="地址(CIDR)" min-width="150" />
|
||||||
<el-table-column prop="gateway" label="网关" min-width="120" />
|
<el-table-column prop="gateway" label="网关" min-width="120" />
|
||||||
<el-table-column prop="mac_address" label="MAC" min-width="150" show-overflow-tooltip />
|
<el-table-column prop="mac_address" label="MAC" min-width="150" show-overflow-tooltip />
|
||||||
<el-table-column label="类型" width="80"><template #default="{ row }"><el-tag :type="row.type === 'bridge' ? 'success' : 'warning'" size="small">{{ row.type === 'bridge' ? '网桥' : 'NAT' }}</el-tag></template></el-table-column>
|
<el-table-column label="主IP" width="70" align="center">
|
||||||
<el-table-column label="操作" width="90" fixed="right">
|
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
|
<el-tag v-if="row.is_primary" type="success" size="small" effect="dark">主</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="类型" width="80"><template #default="{ row }"><el-tag :type="row.type === 'bridge' ? 'success' : 'warning'" size="small">{{ row.type === 'bridge' ? '网桥' : 'NAT' }}</el-tag></template></el-table-column>
|
||||||
|
<el-table-column label="操作" width="160" fixed="right">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-button v-if="!row.is_primary" link type="warning" size="small" @click="handleSetVmNetworkPrimary(row)">设为主IP</el-button>
|
||||||
<el-button link type="danger" size="small" :loading="deletingNetworkId === row.id" @click="handleDeleteVmNetwork(row)">
|
<el-button link type="danger" size="small" :loading="deletingNetworkId === row.id" @click="handleDeleteVmNetwork(row)">
|
||||||
<el-icon :size="14"><Delete /></el-icon>删除
|
<el-icon :size="14"><Delete /></el-icon>删除
|
||||||
</el-button>
|
</el-button>
|
||||||
@@ -1117,7 +1124,8 @@ import {
|
|||||||
getUserVmNetworkList, getUserVmNetworkDetail, getUserVmNetworkingList, createUserVmNetworking, assignUserVmNetworking, removeUserVmNetworkingNetwork, deleteUserVmNetworking,
|
getUserVmNetworkList, getUserVmNetworkDetail, getUserVmNetworkingList, createUserVmNetworking, assignUserVmNetworking, removeUserVmNetworkingNetwork, deleteUserVmNetworking,
|
||||||
getUserGoodsDetail,
|
getUserGoodsDetail,
|
||||||
getUserVmMetricsHistory,
|
getUserVmMetricsHistory,
|
||||||
getUserVmTrafficPolicy, updateUserVmTrafficPolicy, addUserVmFixedTraffic, addUserVmTemporaryTraffic
|
getUserVmTrafficPolicy, updateUserVmTrafficPolicy, addUserVmFixedTraffic, addUserVmTemporaryTraffic,
|
||||||
|
setUserVmNetworkPrimary, resetUserVmMac
|
||||||
} from '@/api/admin/userVm'
|
} from '@/api/admin/userVm'
|
||||||
import { deleteNetwork as deletePointNetwork } from '@/api/admin/kvmService'
|
import { deleteNetwork as deletePointNetwork } from '@/api/admin/kvmService'
|
||||||
import { extractApiError } from '@/utils/kvmErrorUtil'
|
import { extractApiError } from '@/utils/kvmErrorUtil'
|
||||||
@@ -1363,6 +1371,7 @@ const handleMoreCmd = (cmd) => {
|
|||||||
if (cmd === 'refactorVm') openRefactorVm()
|
if (cmd === 'refactorVm') openRefactorVm()
|
||||||
if (cmd === 'migrate') openMigrateVm()
|
if (cmd === 'migrate') openMigrateVm()
|
||||||
if (cmd === 'editGoods') openEditGoods()
|
if (cmd === 'editGoods') openEditGoods()
|
||||||
|
if (cmd === 'resetMac') handleResetVmMac()
|
||||||
if (cmd === 'delete') {
|
if (cmd === 'delete') {
|
||||||
ElMessageBox.confirm('确定删除该用户虚拟机吗?', '删除确认', { type: 'error' }).then(async () => {
|
ElMessageBox.confirm('确定删除该用户虚拟机吗?', '删除确认', { type: 'error' }).then(async () => {
|
||||||
try {
|
try {
|
||||||
@@ -1731,6 +1740,32 @@ const handleDeleteVmNetwork = (row) => {
|
|||||||
}).catch(() => {})
|
}).catch(() => {})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleSetVmNetworkPrimary = (row) => {
|
||||||
|
ElMessageBox.confirm(
|
||||||
|
`将「${row.address || row.name}」设为主IP?设置后将触发 CloudInit 重建并重启虚拟机。`,
|
||||||
|
'设置主IP', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }
|
||||||
|
).then(async () => {
|
||||||
|
try {
|
||||||
|
const res = await setUserVmNetworkPrimary({ user_good_id: userGoodsId.value, network_id: row.id })
|
||||||
|
if (res?.data?.code === 200) { ElMessage.success('设置主IP成功,虚拟机将重启'); loadDetail() }
|
||||||
|
else ElMessage.error(extractApiError(res?.data, '设置主IP失败'))
|
||||||
|
} catch (e) { ElMessage.error(extractApiError(e?.response?.data, '设置主IP失败')) }
|
||||||
|
}).catch(() => {})
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleResetVmMac = () => {
|
||||||
|
ElMessageBox.confirm(
|
||||||
|
'重置MAC地址将重建 CloudInit 并重启虚拟机,确定继续?',
|
||||||
|
'重置MAC地址', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }
|
||||||
|
).then(async () => {
|
||||||
|
try {
|
||||||
|
const res = await resetUserVmMac({ user_goods_id: userGoodsId.value })
|
||||||
|
if (res?.data?.code === 200) { ElMessage.success('MAC地址重置成功,虚拟机将重启'); loadDetail() }
|
||||||
|
else ElMessage.error(extractApiError(res?.data, '重置MAC失败'))
|
||||||
|
} catch (e) { ElMessage.error(extractApiError(e?.response?.data, '重置MAC失败')) }
|
||||||
|
}).catch(() => {})
|
||||||
|
}
|
||||||
|
|
||||||
// ---- 绑定网络 ----
|
// ---- 绑定网络 ----
|
||||||
const showBindNetworkSelector = ref(false)
|
const showBindNetworkSelector = ref(false)
|
||||||
|
|
||||||
|
|||||||
@@ -30,6 +30,7 @@
|
|||||||
<el-dropdown-item divided command="editVm" :disabled="isMigrating">修改虚拟机</el-dropdown-item>
|
<el-dropdown-item divided command="editVm" :disabled="isMigrating">修改虚拟机</el-dropdown-item>
|
||||||
<el-dropdown-item command="refactorVm" :disabled="isMigrating">重构虚拟机</el-dropdown-item>
|
<el-dropdown-item command="refactorVm" :disabled="isMigrating">重构虚拟机</el-dropdown-item>
|
||||||
<el-dropdown-item command="updateTraffic" :disabled="isMigrating">修改带宽</el-dropdown-item>
|
<el-dropdown-item command="updateTraffic" :disabled="isMigrating">修改带宽</el-dropdown-item>
|
||||||
|
<el-dropdown-item command="resetMac" :disabled="isMigrating">重置MAC地址</el-dropdown-item>
|
||||||
<el-dropdown-item divided command="rebuild" :disabled="isMigrating">重装虚拟机</el-dropdown-item>
|
<el-dropdown-item divided command="rebuild" :disabled="isMigrating">重装虚拟机</el-dropdown-item>
|
||||||
<el-dropdown-item command="rescue">救援模式</el-dropdown-item>
|
<el-dropdown-item command="rescue">救援模式</el-dropdown-item>
|
||||||
<el-dropdown-item command="exitRescue">退出救援</el-dropdown-item>
|
<el-dropdown-item command="exitRescue">退出救援</el-dropdown-item>
|
||||||
@@ -280,6 +281,11 @@
|
|||||||
<el-table-column prop="gateway" label="网关" min-width="120" />
|
<el-table-column prop="gateway" label="网关" min-width="120" />
|
||||||
<el-table-column prop="nameservers" label="DNS" min-width="140" show-overflow-tooltip />
|
<el-table-column prop="nameservers" label="DNS" min-width="140" show-overflow-tooltip />
|
||||||
<el-table-column prop="mac_address" label="MAC地址" min-width="150" show-overflow-tooltip />
|
<el-table-column prop="mac_address" label="MAC地址" min-width="150" show-overflow-tooltip />
|
||||||
|
<el-table-column label="主IP" width="70" align="center">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-tag v-if="row.is_primary" type="success" size="small" effect="dark">主</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column label="类型" width="80">
|
<el-table-column label="类型" width="80">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<el-tag :type="row.type === 'bridge' ? 'success' : 'warning'" size="small">{{ row.type === 'bridge' ? '网桥' : 'NAT' }}</el-tag>
|
<el-tag :type="row.type === 'bridge' ? 'success' : 'warning'" size="small">{{ row.type === 'bridge' ? '网桥' : 'NAT' }}</el-tag>
|
||||||
@@ -287,10 +293,11 @@
|
|||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="bridge_name" label="网桥" width="100" />
|
<el-table-column prop="bridge_name" label="网桥" width="100" />
|
||||||
<el-table-column prop="target_device" label="目标设备" width="100" show-overflow-tooltip />
|
<el-table-column prop="target_device" label="目标设备" width="100" show-overflow-tooltip />
|
||||||
<el-table-column label="操作" width="180" fixed="right">
|
<el-table-column label="操作" width="240" fixed="right">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<el-button link type="primary" size="small" @click="handleNetDetail(row)">详情</el-button>
|
<el-button link type="primary" size="small" @click="handleNetDetail(row)">详情</el-button>
|
||||||
<el-button link type="primary" size="small" @click="handleNetEdit(row)">编辑</el-button>
|
<el-button link type="primary" size="small" @click="handleNetEdit(row)">编辑</el-button>
|
||||||
|
<el-button v-if="!row.is_primary" link type="warning" size="small" @click="handleSetPrimary(row)">设为主IP</el-button>
|
||||||
<el-button link type="danger" size="small" @click="handleNetDelete(row)">删除</el-button>
|
<el-button link type="danger" size="small" @click="handleNetDelete(row)">删除</el-button>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
@@ -1598,7 +1605,8 @@ import {
|
|||||||
migrateVm, getRemoteHostGroupList, getRemoteHostDetail,
|
migrateVm, getRemoteHostGroupList, getRemoteHostDetail,
|
||||||
dataMigrateVm, getDataMigrateProgress, abortDataMigrate,
|
dataMigrateVm, getDataMigrateProgress, abortDataMigrate,
|
||||||
getKvmServiceList, getMetricsHistory, getNetworkList,
|
getKvmServiceList, getMetricsHistory, getNetworkList,
|
||||||
getVmTrafficPolicy, updateVmTrafficPolicy, addVmFixedTraffic, addVmTemporaryTraffic
|
getVmTrafficPolicy, updateVmTrafficPolicy, addVmFixedTraffic, addVmTemporaryTraffic,
|
||||||
|
setNetworkPrimary, resetVmMac
|
||||||
} from '@/api/admin/kvmService'
|
} from '@/api/admin/kvmService'
|
||||||
import { getUserInfo } from '@/api/admin/user'
|
import { getUserInfo } from '@/api/admin/user'
|
||||||
import { extractApiError } from '@/utils/kvmErrorUtil'
|
import { extractApiError } from '@/utils/kvmErrorUtil'
|
||||||
@@ -1736,7 +1744,7 @@ const handleMoreCommand = (cmd) => {
|
|||||||
const actionMap = {
|
const actionMap = {
|
||||||
editVm: handleEditVm, refactorVm: handleRefactorVm, updateTraffic: handleUpdateTraffic,
|
editVm: handleEditVm, refactorVm: handleRefactorVm, updateTraffic: handleUpdateTraffic,
|
||||||
rebuild: handleRebuild, rescue: handleRescue, exitRescue: handleExitRescue,
|
rebuild: handleRebuild, rescue: handleRescue, exitRescue: handleExitRescue,
|
||||||
migrateVm: handleMigrateVm
|
migrateVm: handleMigrateVm, resetMac: handleResetMac
|
||||||
}
|
}
|
||||||
if (actionMap[cmd]) actionMap[cmd]()
|
if (actionMap[cmd]) actionMap[cmd]()
|
||||||
if (cmd === 'dataMigrateVm') handleDataMigrateVm()
|
if (cmd === 'dataMigrateVm') handleDataMigrateVm()
|
||||||
@@ -2825,6 +2833,39 @@ const handleNetDelete = (row) => {
|
|||||||
}).catch(() => {})
|
}).catch(() => {})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleSetPrimary = (row) => {
|
||||||
|
ElMessageBox.confirm(
|
||||||
|
`将「${row.address || row.name}」设为主IP?设置后将触发 CloudInit 重建并重启虚拟机。`,
|
||||||
|
'设置主IP', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }
|
||||||
|
).then(async () => {
|
||||||
|
try {
|
||||||
|
const fd = new FormData()
|
||||||
|
fd.append('service_id', serviceId.value)
|
||||||
|
fd.append('network_id', row.id)
|
||||||
|
fd.append('host_id', row.host_id || detail.value?.host_id)
|
||||||
|
const res = await setNetworkPrimary(fd)
|
||||||
|
if (res?.data?.code === 200) { ElMessage.success('设置主IP成功,虚拟机将重启'); loadDetail() }
|
||||||
|
else ElMessage.error(extractApiError(res?.data, '设置主IP失败'))
|
||||||
|
} catch (e) { ElMessage.error(extractApiError(e?.response?.data, '设置主IP失败')) }
|
||||||
|
}).catch(() => {})
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleResetMac = () => {
|
||||||
|
ElMessageBox.confirm(
|
||||||
|
'重置MAC地址将重建 CloudInit 并重启虚拟机,确定继续?',
|
||||||
|
'重置MAC地址', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }
|
||||||
|
).then(async () => {
|
||||||
|
try {
|
||||||
|
const fd = new FormData()
|
||||||
|
fd.append('service_id', serviceId.value)
|
||||||
|
fd.append('vm_id', detail.value?.id)
|
||||||
|
const res = await resetVmMac(fd)
|
||||||
|
if (res?.data?.code === 200) { ElMessage.success('MAC地址重置成功,虚拟机将重启'); loadDetail() }
|
||||||
|
else ElMessage.error(extractApiError(res?.data, '重置MAC失败'))
|
||||||
|
} catch (e) { ElMessage.error(extractApiError(e?.response?.data, '重置MAC失败')) }
|
||||||
|
}).catch(() => {})
|
||||||
|
}
|
||||||
|
|
||||||
// ---- 数据卷操作(绑定/创建/调整/挂载/卸载/迁移/删除/详情) ----
|
// ---- 数据卷操作(绑定/创建/调整/挂载/卸载/迁移/删除/详情) ----
|
||||||
const showVolSelector = ref(false)
|
const showVolSelector = ref(false)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user