diff --git a/src/api/admin/kvmService.js b/src/api/admin/kvmService.js
index cfc26e3..9cda5f4 100644
--- a/src/api/admin/kvmService.js
+++ b/src/api/admin/kvmService.js
@@ -284,6 +284,20 @@ export const deleteNetwork = (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' }
+ })
+}
+
/**
* ================================
* 主控服务接口 - 数据卷管理
diff --git a/src/api/admin/userVm.js b/src/api/admin/userVm.js
index 0492175..9d1d14f 100644
--- a/src/api/admin/userVm.js
+++ b/src/api/admin/userVm.js
@@ -88,6 +88,8 @@ export const deleteUserVmPostGroupRule = (params) => http2.delete(`${BASE}/post_
// ========== 网络 ==========
export const getUserVmNetworkList = (params) => http2.get(`${BASE}/network/list`, { 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 })
diff --git a/src/views/user-vm/UserVmDetail.vue b/src/views/user-vm/UserVmDetail.vue
index 4f9150d..e0b1904 100644
--- a/src/views/user-vm/UserVmDetail.vue
+++ b/src/views/user-vm/UserVmDetail.vue
@@ -46,6 +46,7 @@
恢复
救援模式
退出救援
+ 重置MAC地址
重装系统
编辑虚拟机
重构虚拟机
@@ -329,9 +330,15 @@
- {{ row.type === 'bridge' ? '网桥' : 'NAT' }}
-
+
+ 主
+
+
+ {{ row.type === 'bridge' ? '网桥' : 'NAT' }}
+
+
+ 设为主IP
删除
@@ -1117,7 +1124,8 @@ import {
getUserVmNetworkList, getUserVmNetworkDetail, getUserVmNetworkingList, createUserVmNetworking, assignUserVmNetworking, removeUserVmNetworkingNetwork, deleteUserVmNetworking,
getUserGoodsDetail,
getUserVmMetricsHistory,
- getUserVmTrafficPolicy, updateUserVmTrafficPolicy, addUserVmFixedTraffic, addUserVmTemporaryTraffic
+ getUserVmTrafficPolicy, updateUserVmTrafficPolicy, addUserVmFixedTraffic, addUserVmTemporaryTraffic,
+ setUserVmNetworkPrimary, resetUserVmMac
} from '@/api/admin/userVm'
import { deleteNetwork as deletePointNetwork } from '@/api/admin/kvmService'
import { extractApiError } from '@/utils/kvmErrorUtil'
@@ -1363,6 +1371,7 @@ const handleMoreCmd = (cmd) => {
if (cmd === 'refactorVm') openRefactorVm()
if (cmd === 'migrate') openMigrateVm()
if (cmd === 'editGoods') openEditGoods()
+ if (cmd === 'resetMac') handleResetVmMac()
if (cmd === 'delete') {
ElMessageBox.confirm('确定删除该用户虚拟机吗?', '删除确认', { type: 'error' }).then(async () => {
try {
@@ -1731,6 +1740,32 @@ const handleDeleteVmNetwork = (row) => {
}).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)
diff --git a/src/views/virtualization/VmDetail.vue b/src/views/virtualization/VmDetail.vue
index 21d80f4..efed543 100644
--- a/src/views/virtualization/VmDetail.vue
+++ b/src/views/virtualization/VmDetail.vue
@@ -30,6 +30,7 @@
修改虚拟机
重构虚拟机
修改带宽
+ 重置MAC地址
重装虚拟机
救援模式
退出救援
@@ -280,6 +281,11 @@
+
+
+ 主
+
+
{{ row.type === 'bridge' ? '网桥' : 'NAT' }}
@@ -287,10 +293,11 @@
-
+
详情
编辑
+ 设为主IP
删除
@@ -1598,7 +1605,8 @@ import {
migrateVm, getRemoteHostGroupList, getRemoteHostDetail,
dataMigrateVm, getDataMigrateProgress, abortDataMigrate,
getKvmServiceList, getMetricsHistory, getNetworkList,
- getVmTrafficPolicy, updateVmTrafficPolicy, addVmFixedTraffic, addVmTemporaryTraffic
+ getVmTrafficPolicy, updateVmTrafficPolicy, addVmFixedTraffic, addVmTemporaryTraffic,
+ setNetworkPrimary, resetVmMac
} from '@/api/admin/kvmService'
import { getUserInfo } from '@/api/admin/user'
import { extractApiError } from '@/utils/kvmErrorUtil'
@@ -1736,7 +1744,7 @@ const handleMoreCommand = (cmd) => {
const actionMap = {
editVm: handleEditVm, refactorVm: handleRefactorVm, updateTraffic: handleUpdateTraffic,
rebuild: handleRebuild, rescue: handleRescue, exitRescue: handleExitRescue,
- migrateVm: handleMigrateVm
+ migrateVm: handleMigrateVm, resetMac: handleResetMac
}
if (actionMap[cmd]) actionMap[cmd]()
if (cmd === 'dataMigrateVm') handleDataMigrateVm()
@@ -2825,6 +2833,39 @@ const handleNetDelete = (row) => {
}).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)