feat: 新增网卡配置文件生成器(ifcfg/NM keyfile/Netplan)
- 新增 3 个网络配置生成器,覆盖 CentOS/RHEL 7-9 + Ubuntu 18.04-24.04: - network-rhel-ifcfg: RHEL/CentOS 7-8 ifcfg 风格(network-scripts) - network-rhel-nmkeyfile: RHEL/CentOS 9 NetworkManager keyfile - network-ubuntu-netplan: Ubuntu Netplan YAML - 每个生成器支持 5 种配置类型: 静态IP / DHCP / Bond / Bridge / VLAN - Bond 支持 7 种模式(0-6),含 miimon/lacp_rate/成员网卡配置 - 支持路由 Metric 配置(ifcfg METRIC= / NM route-metric= / Netplan metric:) - ConfigForm 增加 valueIn 依赖支持(修复 keepalived 已有但失效的 valueIn bug) - 新增「网络配置」侧边栏分类,组件计数 60 -> 63
This commit is contained in:
Generated
-39
@@ -644,9 +644,6 @@
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -660,9 +657,6 @@
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -676,9 +670,6 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -692,9 +683,6 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -708,9 +696,6 @@
|
||||
"cpu": [
|
||||
"loong64"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -724,9 +709,6 @@
|
||||
"cpu": [
|
||||
"loong64"
|
||||
],
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -740,9 +722,6 @@
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -756,9 +735,6 @@
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -772,9 +748,6 @@
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -788,9 +761,6 @@
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -804,9 +774,6 @@
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -820,9 +787,6 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -836,9 +800,6 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
|
||||
@@ -141,6 +141,7 @@ function isFieldVisible(field) {
|
||||
const dep = field.dependsOn
|
||||
const val = formData.value[dep.key]
|
||||
if ('value' in dep) return val === dep.value
|
||||
if ('valueIn' in dep) return dep.valueIn.includes(val)
|
||||
if ('valueNotIn' in dep) return !dep.valueNotIn.includes(val)
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@
|
||||
</div>
|
||||
|
||||
<div class="sidebar-footer">
|
||||
<el-text size="small" type="info">v1.0.0 · 纯前端生成 · 60 个组件</el-text>
|
||||
<el-text size="small" type="info">v1.0.0 · 纯前端生成 · 63 个组件</el-text>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -95,6 +95,11 @@ import { sersyncSchema, generateSersyncXml } from './sersync'
|
||||
import { vsftpdSchema, generateVsftpdConf } from './vsftpd'
|
||||
import { subversionSchema, generateSubversionConf } from './subversion'
|
||||
|
||||
// ============ 网络配置 ============
|
||||
import { networkRhelIfcfgSchema, generateNetworkRhelIfcfg } from './network-rhel-ifcfg'
|
||||
import { networkRhelNmKeyfileSchema, generateNetworkRhelNmKeyfile } from './network-rhel-nmkeyfile'
|
||||
import { networkUbuntuNetplanSchema, generateNetworkUbuntuNetplan } from './network-ubuntu-netplan'
|
||||
|
||||
// ============ 数据库中间件 ============
|
||||
import { mycatSchema, generateMycatServerXml } from './mycat'
|
||||
|
||||
@@ -183,6 +188,10 @@ export const schemas = {
|
||||
subversion: subversionSchema,
|
||||
// 数据库中间件
|
||||
mycat: mycatSchema,
|
||||
// 网络配置
|
||||
'network-rhel-ifcfg': networkRhelIfcfgSchema,
|
||||
'network-rhel-nmkeyfile': networkRhelNmKeyfileSchema,
|
||||
'network-ubuntu-netplan': networkUbuntuNetplanSchema,
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
@@ -254,6 +263,10 @@ export const generators = {
|
||||
subversion: generateSubversionConf,
|
||||
// 数据库中间件
|
||||
mycat: generateMycatServerXml,
|
||||
// 网络配置
|
||||
'network-rhel-ifcfg': generateNetworkRhelIfcfg,
|
||||
'network-rhel-nmkeyfile': generateNetworkRhelNmKeyfile,
|
||||
'network-ubuntu-netplan': generateNetworkUbuntuNetplan,
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
@@ -351,6 +364,11 @@ export const categories = [
|
||||
icon: 'Connection',
|
||||
items: ['keepalived', 'haproxy', 'lvs'],
|
||||
},
|
||||
{
|
||||
name: '网络配置',
|
||||
icon: 'Connection',
|
||||
items: ['network-rhel-ifcfg', 'network-rhel-nmkeyfile', 'network-ubuntu-netplan'],
|
||||
},
|
||||
{
|
||||
name: '系统服务',
|
||||
icon: 'Setting',
|
||||
|
||||
@@ -0,0 +1,458 @@
|
||||
// RHEL / CentOS 7-8 ifcfg 风格网络配置
|
||||
// 覆盖: CentOS 7, RHEL 7, CentOS 8/Stream 8, RHEL 8, Rocky 8, AlmaLinux 8
|
||||
// 配置文件位于 /etc/sysconfig/network-scripts/ifcfg-<name>
|
||||
|
||||
export const networkRhelIfcfgSchema = {
|
||||
id: 'network-rhel-ifcfg',
|
||||
name: 'RHEL/CentOS ifcfg',
|
||||
icon: 'Connection',
|
||||
category: '网络配置',
|
||||
description: 'RHEL/CentOS 7-8 ifcfg 网络脚本(含 Bond/Bridge/VLAN)',
|
||||
format: 'conf',
|
||||
fileName: 'ifcfg-eth0',
|
||||
groups: [
|
||||
{
|
||||
title: '系统版本',
|
||||
fields: [
|
||||
{
|
||||
key: 'osVersion',
|
||||
label: '操作系统版本',
|
||||
type: 'select',
|
||||
options: [
|
||||
{ label: 'CentOS 7', value: 'centos7' },
|
||||
{ label: 'RHEL 7', value: 'rhel7' },
|
||||
{ label: 'CentOS 8 / Stream 8', value: 'centos8' },
|
||||
{ label: 'RHEL 8', value: 'rhel8' },
|
||||
{ label: 'Rocky Linux 8 / AlmaLinux 8', value: 'el8-clone' },
|
||||
],
|
||||
default: 'centos7',
|
||||
tip: '影响配置文件注释与建议说明,ifcfg 语法本身通用',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '配置类型',
|
||||
fields: [
|
||||
{
|
||||
key: 'configType',
|
||||
label: '配置类型',
|
||||
type: 'select',
|
||||
options: [
|
||||
{ label: '单网卡 - 静态 IP', value: 'static' },
|
||||
{ label: '单网卡 - DHCP', value: 'dhcp' },
|
||||
{ label: 'Bond - 链路聚合', value: 'bond' },
|
||||
{ label: 'Bridge - 桥接', value: 'bridge' },
|
||||
{ label: 'VLAN - 子接口', value: 'vlan' },
|
||||
],
|
||||
default: 'static',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '主接口配置',
|
||||
fields: [
|
||||
{
|
||||
key: 'interface',
|
||||
label: '主接口名',
|
||||
type: 'text',
|
||||
default: 'eth0',
|
||||
tip: 'ifcfg-<此名称>,例如 eth0 / ens33 / enp0s3',
|
||||
},
|
||||
{
|
||||
key: 'bootproto',
|
||||
label: '协议',
|
||||
type: 'select',
|
||||
options: [
|
||||
{ label: 'static / none - 静态', value: 'none' },
|
||||
{ label: 'dhcp - 自动获取', value: 'dhcp' },
|
||||
],
|
||||
default: 'none',
|
||||
dependsOn: { key: 'configType', valueIn: ['static', 'bond', 'bridge', 'vlan'] },
|
||||
},
|
||||
{
|
||||
key: 'ipaddr',
|
||||
label: 'IP 地址',
|
||||
type: 'text',
|
||||
default: '192.168.1.10',
|
||||
dependsOn: { key: 'configType', valueIn: ['static', 'bond', 'bridge', 'vlan'] },
|
||||
},
|
||||
{
|
||||
key: 'prefix',
|
||||
label: '子网前缀 (CIDR)',
|
||||
type: 'number',
|
||||
min: 1,
|
||||
max: 32,
|
||||
default: 24,
|
||||
tip: '例如 24 = 255.255.255.0',
|
||||
dependsOn: { key: 'configType', valueIn: ['static', 'bond', 'bridge', 'vlan'] },
|
||||
},
|
||||
{
|
||||
key: 'gateway',
|
||||
label: '默认网关',
|
||||
type: 'text',
|
||||
default: '192.168.1.1',
|
||||
dependsOn: { key: 'configType', valueIn: ['static', 'bond', 'bridge', 'vlan'] },
|
||||
},
|
||||
{
|
||||
key: 'dns1',
|
||||
label: '主 DNS',
|
||||
type: 'text',
|
||||
default: '8.8.8.8',
|
||||
dependsOn: { key: 'configType', valueIn: ['static', 'bond', 'bridge', 'vlan'] },
|
||||
},
|
||||
{
|
||||
key: 'dns2',
|
||||
label: '备用 DNS',
|
||||
type: 'text',
|
||||
default: '8.8.4.4',
|
||||
dependsOn: { key: 'configType', valueIn: ['static', 'bond', 'bridge', 'vlan'] },
|
||||
},
|
||||
{
|
||||
key: 'routeMetric',
|
||||
label: '路由 Metric',
|
||||
type: 'number',
|
||||
min: 0,
|
||||
max: 9999,
|
||||
default: 0,
|
||||
tip: '默认路由优先级,值越小优先级越高;0 表示不输出',
|
||||
dependsOn: { key: 'configType', valueIn: ['static', 'bond', 'bridge', 'vlan'] },
|
||||
},
|
||||
{
|
||||
key: 'onboot',
|
||||
label: '开机自动启用',
|
||||
type: 'switch',
|
||||
default: true,
|
||||
},
|
||||
{
|
||||
key: 'ipv6',
|
||||
label: '禁用 IPv6',
|
||||
type: 'switch',
|
||||
default: true,
|
||||
tip: '在 ifcfg 中写入 IPV6INIT=no',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Bond 配置',
|
||||
fields: [
|
||||
{
|
||||
key: 'bondName',
|
||||
label: 'Bond 接口名',
|
||||
type: 'text',
|
||||
default: 'bond0',
|
||||
dependsOn: { key: 'configType', value: 'bond' },
|
||||
},
|
||||
{
|
||||
key: 'bondMode',
|
||||
label: 'Bond 模式',
|
||||
type: 'select',
|
||||
options: [
|
||||
{ label: '0 - balance-rr (轮询)', value: '0' },
|
||||
{ label: '1 - active-backup (主备)', value: '1' },
|
||||
{ label: '2 - balance-xor (XOR)', value: '2' },
|
||||
{ label: '3 - broadcast (广播)', value: '3' },
|
||||
{ label: '4 - 802.3ad (LACP 动态聚合)', value: '4' },
|
||||
{ label: '5 - balance-tlb (自适应发送负载)', value: '5' },
|
||||
{ label: '6 - balance-alb (自适应负载均衡)', value: '6' },
|
||||
],
|
||||
default: '4',
|
||||
tip: '模式 4 需要交换机端开启 LACP;模式 1 兼容性最好',
|
||||
dependsOn: { key: 'configType', value: 'bond' },
|
||||
},
|
||||
{
|
||||
key: 'miimon',
|
||||
label: 'MII 监控间隔 (ms)',
|
||||
type: 'number',
|
||||
min: 0,
|
||||
max: 10000,
|
||||
default: 100,
|
||||
tip: '链路状态检测间隔,0 表示关闭',
|
||||
dependsOn: { key: 'configType', value: 'bond' },
|
||||
},
|
||||
{
|
||||
key: 'lacpRate',
|
||||
label: 'LACP 速率',
|
||||
type: 'select',
|
||||
options: [
|
||||
{ label: 'slow (默认 30s)', value: 'slow' },
|
||||
{ label: 'fast (1s)', value: 'fast' },
|
||||
],
|
||||
default: 'fast',
|
||||
tip: '仅 mode=4 (802.3ad) 生效',
|
||||
dependsOn: { key: 'configType', value: 'bond' },
|
||||
},
|
||||
{
|
||||
key: 'bondSlaves',
|
||||
label: '成员网卡 (逗号分隔)',
|
||||
type: 'text',
|
||||
default: 'eth0,eth1',
|
||||
tip: '会被设为 SLAVE=yes,MASTER=<bondName>',
|
||||
dependsOn: { key: 'configType', value: 'bond' },
|
||||
},
|
||||
{
|
||||
key: 'primarySlave',
|
||||
label: '主网卡 (可选)',
|
||||
type: 'text',
|
||||
default: '',
|
||||
placeholder: '留空使用默认',
|
||||
tip: '仅 mode=1 active-backup 时作为活动网卡,可留空',
|
||||
dependsOn: { key: 'configType', value: 'bond' },
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Bridge 配置',
|
||||
fields: [
|
||||
{
|
||||
key: 'bridgeName',
|
||||
label: 'Bridge 接口名',
|
||||
type: 'text',
|
||||
default: 'br0',
|
||||
dependsOn: { key: 'configType', value: 'bridge' },
|
||||
},
|
||||
{
|
||||
key: 'bridgePorts',
|
||||
label: '桥接成员 (逗号分隔)',
|
||||
type: 'text',
|
||||
default: 'eth0',
|
||||
tip: '成员网卡会写成 BRIDGE=<bridgeName>',
|
||||
dependsOn: { key: 'configType', value: 'bridge' },
|
||||
},
|
||||
{
|
||||
key: 'bridgeStp',
|
||||
label: '启用 STP 生成树',
|
||||
type: 'switch',
|
||||
default: false,
|
||||
dependsOn: { key: 'configType', value: 'bridge' },
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'VLAN 配置',
|
||||
fields: [
|
||||
{
|
||||
key: 'vlanParent',
|
||||
label: '父接口',
|
||||
type: 'text',
|
||||
default: 'eth0',
|
||||
tip: '例如 eth0,生成的子接口为 eth0.<vlan_id>',
|
||||
dependsOn: { key: 'configType', value: 'vlan' },
|
||||
},
|
||||
{
|
||||
key: 'vlanId',
|
||||
label: 'VLAN ID',
|
||||
type: 'number',
|
||||
min: 1,
|
||||
max: 4094,
|
||||
default: 100,
|
||||
dependsOn: { key: 'configType', value: 'vlan' },
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
function genIfcfgFile(fileName, fields, header) {
|
||||
const lines = []
|
||||
if (header) lines.push(header)
|
||||
for (const [k, v] of Object.entries(fields)) {
|
||||
if (v === undefined || v === null) continue
|
||||
lines.push(`${k}=${v}`)
|
||||
}
|
||||
return lines.join('\n')
|
||||
}
|
||||
|
||||
export function generateNetworkRhelIfcfg(config) {
|
||||
const out = []
|
||||
const ts = new Date().toLocaleString('zh-CN')
|
||||
out.push(`# =========================================================`)
|
||||
out.push(`# RHEL/CentOS ifcfg 网络配置 - 由 ConfTemplate 生成`)
|
||||
out.push(`# 生成时间: ${ts}`)
|
||||
out.push(`# 目标目录: /etc/sysconfig/network-scripts/`)
|
||||
out.push(`# 配置类型: ${config.configType}`)
|
||||
out.push(`# =========================================================`)
|
||||
out.push(``)
|
||||
out.push(`# 应用步骤:`)
|
||||
out.push(`# 1. 将下面每个 ifcfg-xxx 内容保存到对应文件`)
|
||||
out.push(`# 2. 执行: systemctl restart network # CentOS 7`)
|
||||
out.push(`# nmcli connection reload # CentOS 8+ (NetworkManager)`)
|
||||
out.push(`# nmcli connection up <name>`)
|
||||
out.push(``)
|
||||
out.push(`# -----------------------------------------------------------`)
|
||||
|
||||
const ipv6Line = config.ipv6 ? 'IPV6INIT=no' : null
|
||||
|
||||
if (config.configType === 'static' || config.configType === 'dhcp') {
|
||||
const bootproto = config.configType === 'dhcp' ? 'dhcp' : (config.bootproto || 'none')
|
||||
const base = {
|
||||
TYPE: 'Ethernet',
|
||||
PROXY_METHOD: 'none',
|
||||
BROWSER_ONLY: 'no',
|
||||
BOOTPROTO: bootproto,
|
||||
DEFROUTE: 'yes',
|
||||
IPV4_FAILURE_FATAL: 'no',
|
||||
NAME: config.interface,
|
||||
DEVICE: config.interface,
|
||||
ONBOOT: config.onboot ? 'yes' : 'no',
|
||||
}
|
||||
if (config.ipv6) base.IPV6INIT = 'no'
|
||||
if (config.configType === 'static') {
|
||||
Object.assign(base, {
|
||||
IPADDR: config.ipaddr,
|
||||
PREFIX: String(config.prefix),
|
||||
GATEWAY: config.gateway,
|
||||
DNS1: config.dns1,
|
||||
DNS2: config.dns2,
|
||||
})
|
||||
if (config.routeMetric) base.METRIC = String(config.routeMetric)
|
||||
}
|
||||
out.push(``)
|
||||
out.push(`# >>> 文件: ifcfg-${config.interface}`)
|
||||
out.push(`# >>> 接口 ${config.interface} (${config.configType === 'dhcp' ? 'DHCP' : '静态 IP'})`)
|
||||
out.push(genIfcfgFile(`ifcfg-${config.interface}`, base))
|
||||
out.push(``)
|
||||
}
|
||||
|
||||
if (config.configType === 'bond') {
|
||||
// bond 主接口
|
||||
const bondOpts = [`mode=${config.bondMode}`, `miimon=${config.miimon}`]
|
||||
if (config.bondMode === '4') {
|
||||
bondOpts.push(`lacp_rate=${config.lacpRate}`)
|
||||
}
|
||||
if (config.primarySlave) {
|
||||
bondOpts.push(`primary=${config.primarySlave}`)
|
||||
}
|
||||
const master = {
|
||||
TYPE: 'Bond',
|
||||
BONDING_MASTER: 'yes',
|
||||
PROXY_METHOD: 'none',
|
||||
BROWSER_ONLY: 'no',
|
||||
BOOTPROTO: config.bootproto || 'none',
|
||||
DEFROUTE: 'yes',
|
||||
IPV4_FAILURE_FATAL: 'no',
|
||||
NAME: config.bondName,
|
||||
DEVICE: config.bondName,
|
||||
ONBOOT: config.onboot ? 'yes' : 'no',
|
||||
BONDING_OPTS: `"${bondOpts.join(' ')}"`,
|
||||
IPADDR: config.ipaddr,
|
||||
PREFIX: String(config.prefix),
|
||||
GATEWAY: config.gateway,
|
||||
DNS1: config.dns1,
|
||||
DNS2: config.dns2,
|
||||
}
|
||||
if (config.ipv6) master.IPV6INIT = 'no'
|
||||
if (config.routeMetric) master.METRIC = String(config.routeMetric)
|
||||
out.push(``)
|
||||
out.push(`# >>> 文件: ifcfg-${config.bondName}`)
|
||||
out.push(`# >>> Bond 主接口 ${config.bondName} (mode=${config.bondMode})`)
|
||||
out.push(genIfcfgFile(`ifcfg-${config.bondName}`, master))
|
||||
out.push(``)
|
||||
|
||||
// 成员接口
|
||||
const slaves = String(config.bondSlaves || '').split(',').map((s) => s.trim()).filter(Boolean)
|
||||
for (const slv of slaves) {
|
||||
const slave = {
|
||||
TYPE: 'Ethernet',
|
||||
PROXY_METHOD: 'none',
|
||||
BROWSER_ONLY: 'no',
|
||||
BOOTPROTO: 'none',
|
||||
DEFROUTE: 'no',
|
||||
IPV4_FAILURE_FATAL: 'no',
|
||||
NAME: slv,
|
||||
DEVICE: slv,
|
||||
ONBOOT: config.onboot ? 'yes' : 'no',
|
||||
MASTER: config.bondName,
|
||||
SLAVE: 'yes',
|
||||
}
|
||||
if (config.ipv6) slave.IPV6INIT = 'no'
|
||||
out.push(`# >>> 文件: ifcfg-${slv}`)
|
||||
out.push(`# >>> Bond 成员 ${slv} → ${config.bondName}`)
|
||||
out.push(genIfcfgFile(`ifcfg-${slv}`, slave))
|
||||
out.push(``)
|
||||
}
|
||||
out.push(`# 提示: mode=4 (LACP) 需要在交换机端开启 LACP 动态聚合`)
|
||||
}
|
||||
|
||||
if (config.configType === 'bridge') {
|
||||
// bridge 主接口
|
||||
const br = {
|
||||
TYPE: 'Bridge',
|
||||
PROXY_METHOD: 'none',
|
||||
BROWSER_ONLY: 'no',
|
||||
BOOTPROTO: config.bootproto || 'none',
|
||||
DEFROUTE: 'yes',
|
||||
IPV4_FAILURE_FATAL: 'no',
|
||||
NAME: config.bridgeName,
|
||||
DEVICE: config.bridgeName,
|
||||
ONBOOT: config.onboot ? 'yes' : 'no',
|
||||
STP: config.bridgeStp ? 'yes' : 'no',
|
||||
IPADDR: config.ipaddr,
|
||||
PREFIX: String(config.prefix),
|
||||
GATEWAY: config.gateway,
|
||||
DNS1: config.dns1,
|
||||
DNS2: config.dns2,
|
||||
}
|
||||
if (config.ipv6) br.IPV6INIT = 'no'
|
||||
if (config.routeMetric) br.METRIC = String(config.routeMetric)
|
||||
out.push(``)
|
||||
out.push(`# >>> 文件: ifcfg-${config.bridgeName}`)
|
||||
out.push(`# >>> Bridge ${config.bridgeName} (STP=${config.bridgeStp ? 'on' : 'off'})`)
|
||||
out.push(genIfcfgFile(`ifcfg-${config.bridgeName}`, br))
|
||||
out.push(``)
|
||||
|
||||
// 成员接口
|
||||
const ports = String(config.bridgePorts || '').split(',').map((s) => s.trim()).filter(Boolean)
|
||||
for (const p of ports) {
|
||||
const port = {
|
||||
TYPE: 'Ethernet',
|
||||
PROXY_METHOD: 'none',
|
||||
BROWSER_ONLY: 'no',
|
||||
BOOTPROTO: 'none',
|
||||
DEFROUTE: 'no',
|
||||
IPV4_FAILURE_FATAL: 'no',
|
||||
NAME: p,
|
||||
DEVICE: p,
|
||||
ONBOOT: config.onboot ? 'yes' : 'no',
|
||||
BRIDGE: config.bridgeName,
|
||||
}
|
||||
if (config.ipv6) port.IPV6INIT = 'no'
|
||||
out.push(`# >>> 文件: ifcfg-${p}`)
|
||||
out.push(`# >>> Bridge 成员 ${p} → ${config.bridgeName}`)
|
||||
out.push(genIfcfgFile(`ifcfg-${p}`, port))
|
||||
out.push(``)
|
||||
}
|
||||
}
|
||||
|
||||
if (config.configType === 'vlan') {
|
||||
const subName = `${config.vlanParent}.${config.vlanId}`
|
||||
const vlan = {
|
||||
TYPE: 'Ethernet',
|
||||
PROXY_METHOD: 'none',
|
||||
BROWSER_ONLY: 'no',
|
||||
BOOTPROTO: config.bootproto || 'none',
|
||||
DEFROUTE: 'yes',
|
||||
IPV4_FAILURE_FATAL: 'no',
|
||||
NAME: subName,
|
||||
DEVICE: subName,
|
||||
ONBOOT: config.onboot ? 'yes' : 'no',
|
||||
VLAN: 'yes',
|
||||
IPADDR: config.ipaddr,
|
||||
PREFIX: String(config.prefix),
|
||||
GATEWAY: config.gateway,
|
||||
DNS1: config.dns1,
|
||||
DNS2: config.dns2,
|
||||
}
|
||||
if (config.ipv6) vlan.IPV6INIT = 'no'
|
||||
if (config.routeMetric) vlan.METRIC = String(config.routeMetric)
|
||||
out.push(``)
|
||||
out.push(`# >>> 文件: ifcfg-${subName}`)
|
||||
out.push(`# >>> VLAN 子接口 ${subName} (VLAN ID=${config.vlanId})`)
|
||||
out.push(genIfcfgFile(`ifcfg-${subName}`, vlan))
|
||||
out.push(``)
|
||||
out.push(`# 提示: 父接口 ${config.vlanParent} 也需要 ONBOOT=yes 并处于 up 状态`)
|
||||
out.push(`# 若使用基于接口名的隐式 VLAN,VLAN=yes 即可;`)
|
||||
out.push(`# 若用显式 VLAN_ID,可增加: PHYSDEV=${config.vlanParent}, VLAN_ID=${config.vlanId}`)
|
||||
}
|
||||
|
||||
return out.join('\n')
|
||||
}
|
||||
@@ -0,0 +1,430 @@
|
||||
// RHEL / CentOS 9 NetworkManager keyfile 风格网络配置
|
||||
// 覆盖: CentOS Stream 9, RHEL 9, Rocky Linux 9, AlmaLinux 9
|
||||
// 配置文件位于 /etc/NetworkManager/system-connections/<name>.nmconnection
|
||||
// 说明: RHEL 9 / CentOS Stream 9 已弃用 network-scripts (ifcfg-*),必须使用 NM keyfile
|
||||
|
||||
export const networkRhelNmKeyfileSchema = {
|
||||
id: 'network-rhel-nmkeyfile',
|
||||
name: 'RHEL/CentOS 9 NM',
|
||||
icon: 'Connection',
|
||||
category: '网络配置',
|
||||
description: 'RHEL/CentOS 9 NetworkManager keyfile(含 Bond/Bridge/VLAN)',
|
||||
format: 'ini',
|
||||
fileName: 'connection.nmconnection',
|
||||
groups: [
|
||||
{
|
||||
title: '系统版本',
|
||||
fields: [
|
||||
{
|
||||
key: 'osVersion',
|
||||
label: '操作系统版本',
|
||||
type: 'select',
|
||||
options: [
|
||||
{ label: 'CentOS Stream 9', value: 'centos-stream9' },
|
||||
{ label: 'RHEL 9', value: 'rhel9' },
|
||||
{ label: 'Rocky Linux 9', value: 'rocky9' },
|
||||
{ label: 'AlmaLinux 9', value: 'alma9' },
|
||||
],
|
||||
default: 'centos-stream9',
|
||||
tip: '语法相同,仅影响注释与说明',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '配置类型',
|
||||
fields: [
|
||||
{
|
||||
key: 'configType',
|
||||
label: '配置类型',
|
||||
type: 'select',
|
||||
options: [
|
||||
{ label: '单网卡 - 静态 IP', value: 'static' },
|
||||
{ label: '单网卡 - DHCP', value: 'dhcp' },
|
||||
{ label: 'Bond - 链路聚合', value: 'bond' },
|
||||
{ label: 'Bridge - 桥接', value: 'bridge' },
|
||||
{ label: 'VLAN - 子接口', value: 'vlan' },
|
||||
],
|
||||
default: 'static',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '主接口配置',
|
||||
fields: [
|
||||
{
|
||||
key: 'interface',
|
||||
label: '主接口名',
|
||||
type: 'text',
|
||||
default: 'eth0',
|
||||
tip: 'nmconnection 文件名建议与接口名一致',
|
||||
},
|
||||
{
|
||||
key: 'ipaddr',
|
||||
label: 'IP 地址',
|
||||
type: 'text',
|
||||
default: '192.168.1.10',
|
||||
dependsOn: { key: 'configType', valueIn: ['static', 'bond', 'bridge', 'vlan'] },
|
||||
},
|
||||
{
|
||||
key: 'prefix',
|
||||
label: '子网前缀 (CIDR)',
|
||||
type: 'number',
|
||||
min: 1,
|
||||
max: 32,
|
||||
default: 24,
|
||||
dependsOn: { key: 'configType', valueIn: ['static', 'bond', 'bridge', 'vlan'] },
|
||||
},
|
||||
{
|
||||
key: 'gateway',
|
||||
label: '默认网关',
|
||||
type: 'text',
|
||||
default: '192.168.1.1',
|
||||
dependsOn: { key: 'configType', valueIn: ['static', 'bond', 'bridge', 'vlan'] },
|
||||
},
|
||||
{
|
||||
key: 'dns',
|
||||
label: 'DNS (逗号分隔)',
|
||||
type: 'text',
|
||||
default: '8.8.8.8,8.8.4.4',
|
||||
dependsOn: { key: 'configType', valueIn: ['static', 'bond', 'bridge', 'vlan'] },
|
||||
},
|
||||
{
|
||||
key: 'routeMetric',
|
||||
label: '路由 Metric',
|
||||
type: 'number',
|
||||
min: 0,
|
||||
max: 9999,
|
||||
default: 0,
|
||||
tip: '默认路由优先级,值越小优先级越高;0 表示不输出',
|
||||
dependsOn: { key: 'configType', valueIn: ['static', 'bond', 'bridge', 'vlan'] },
|
||||
},
|
||||
{
|
||||
key: 'autoconnect',
|
||||
label: '开机自动启用',
|
||||
type: 'switch',
|
||||
default: true,
|
||||
},
|
||||
{
|
||||
key: 'ipv6Method',
|
||||
label: 'IPv6 方法',
|
||||
type: 'select',
|
||||
options: [
|
||||
{ label: 'disabled - 禁用', value: 'disabled' },
|
||||
{ label: 'auto - 自动', value: 'auto' },
|
||||
{ label: 'ignore - 忽略', value: 'ignore' },
|
||||
],
|
||||
default: 'disabled',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Bond 配置',
|
||||
fields: [
|
||||
{
|
||||
key: 'bondName',
|
||||
label: 'Bond 接口名',
|
||||
type: 'text',
|
||||
default: 'bond0',
|
||||
dependsOn: { key: 'configType', value: 'bond' },
|
||||
},
|
||||
{
|
||||
key: 'bondMode',
|
||||
label: 'Bond 模式',
|
||||
type: 'select',
|
||||
options: [
|
||||
{ label: 'balance-rr (0)', value: 'balance-rr' },
|
||||
{ label: 'active-backup (1)', value: 'active-backup' },
|
||||
{ label: 'balance-xor (2)', value: 'balance-xor' },
|
||||
{ label: 'broadcast (3)', value: 'broadcast' },
|
||||
{ label: '802.3ad (4 LACP)', value: '802.3ad' },
|
||||
{ label: 'balance-tlb (5)', value: 'balance-tlb' },
|
||||
{ label: 'balance-alb (6)', value: 'balance-alb' },
|
||||
],
|
||||
default: '802.3ad',
|
||||
tip: 'mode 4 需要交换机端开启 LACP',
|
||||
dependsOn: { key: 'configType', value: 'bond' },
|
||||
},
|
||||
{
|
||||
key: 'miimon',
|
||||
label: 'MII 监控间隔 (ms)',
|
||||
type: 'number',
|
||||
min: 0,
|
||||
max: 10000,
|
||||
default: 100,
|
||||
dependsOn: { key: 'configType', value: 'bond' },
|
||||
},
|
||||
{
|
||||
key: 'lacpRate',
|
||||
label: 'LACP 速率',
|
||||
type: 'select',
|
||||
options: [
|
||||
{ label: 'slow (30s)', value: 'slow' },
|
||||
{ label: 'fast (1s)', value: 'fast' },
|
||||
],
|
||||
default: 'fast',
|
||||
tip: '仅 802.3ad 模式生效',
|
||||
dependsOn: { key: 'configType', value: 'bond' },
|
||||
},
|
||||
{
|
||||
key: 'bondSlaves',
|
||||
label: '成员网卡 (逗号分隔)',
|
||||
type: 'text',
|
||||
default: 'eth0,eth1',
|
||||
dependsOn: { key: 'configType', value: 'bond' },
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Bridge 配置',
|
||||
fields: [
|
||||
{
|
||||
key: 'bridgeName',
|
||||
label: 'Bridge 接口名',
|
||||
type: 'text',
|
||||
default: 'br0',
|
||||
dependsOn: { key: 'configType', value: 'bridge' },
|
||||
},
|
||||
{
|
||||
key: 'bridgePorts',
|
||||
label: '桥接成员 (逗号分隔)',
|
||||
type: 'text',
|
||||
default: 'eth0',
|
||||
dependsOn: { key: 'configType', value: 'bridge' },
|
||||
},
|
||||
{
|
||||
key: 'bridgeStp',
|
||||
label: '启用 STP',
|
||||
type: 'switch',
|
||||
default: false,
|
||||
dependsOn: { key: 'configType', value: 'bridge' },
|
||||
},
|
||||
{
|
||||
key: 'bridgeHelloTime',
|
||||
label: 'STP Hello 时间 (秒)',
|
||||
type: 'number',
|
||||
min: 1,
|
||||
max: 10,
|
||||
default: 2,
|
||||
dependsOn: { key: 'configType', value: 'bridge' },
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'VLAN 配置',
|
||||
fields: [
|
||||
{
|
||||
key: 'vlanParent',
|
||||
label: '父接口',
|
||||
type: 'text',
|
||||
default: 'eth0',
|
||||
dependsOn: { key: 'configType', value: 'vlan' },
|
||||
},
|
||||
{
|
||||
key: 'vlanId',
|
||||
label: 'VLAN ID',
|
||||
type: 'number',
|
||||
min: 1,
|
||||
max: 4094,
|
||||
default: 100,
|
||||
dependsOn: { key: 'configType', value: 'vlan' },
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
function genKeyfile(sections, headerComment) {
|
||||
const lines = []
|
||||
if (headerComment) {
|
||||
lines.push(headerComment)
|
||||
}
|
||||
for (const [section, kv] of Object.entries(sections)) {
|
||||
lines.push(`[${section}]`)
|
||||
for (const [k, v] of Object.entries(kv)) {
|
||||
if (v === undefined || v === null || v === '') continue
|
||||
lines.push(`${k}=${v}`)
|
||||
}
|
||||
lines.push('')
|
||||
}
|
||||
return lines.join('\n')
|
||||
}
|
||||
|
||||
export function generateNetworkRhelNmKeyfile(config) {
|
||||
const out = []
|
||||
const ts = new Date().toLocaleString('zh-CN')
|
||||
out.push(`# =========================================================`)
|
||||
out.push(`# RHEL/CentOS 9 NetworkManager keyfile 配置 - 由 ConfTemplate 生成`)
|
||||
out.push(`# 生成时间: ${ts}`)
|
||||
out.push(`# 目标目录: /etc/NetworkManager/system-connections/`)
|
||||
out.push(`# 配置类型: ${config.configType}`)
|
||||
out.push(`# =========================================================`)
|
||||
out.push(``)
|
||||
out.push(`# 应用步骤:`)
|
||||
out.push(`# 1. 将下面每个 <name>.nmconnection 内容保存到对应文件`)
|
||||
out.push(`# 2. 设置权限: chmod 600 <name>.nmconnection`)
|
||||
out.push(`# 3. 执行: nmcli connection reload`)
|
||||
out.push(`# nmcli connection up <name>`)
|
||||
out.push(``)
|
||||
out.push(`# keyfile 必须有 600 权限,否则 NetworkManager 会忽略它`)
|
||||
out.push(`# -----------------------------------------------------------`)
|
||||
|
||||
const autoStr = config.autoconnect ? 'true' : 'false'
|
||||
const dnsList = String(config.dns || '').split(',').map((s) => s.trim()).filter(Boolean).join(';')
|
||||
const dnsSuffix = dnsList ? `${dnsList};` : ''
|
||||
|
||||
if (config.configType === 'static' || config.configType === 'dhcp') {
|
||||
const sections = {
|
||||
connection: {
|
||||
id: config.interface,
|
||||
type: 'ethernet',
|
||||
'interface-name': config.interface,
|
||||
autoconnect: autoStr,
|
||||
},
|
||||
ipv4: {},
|
||||
ipv6: { method: config.ipv6Method },
|
||||
}
|
||||
if (config.configType === 'dhcp') {
|
||||
sections.ipv4.method = 'auto'
|
||||
} else {
|
||||
sections.ipv4.method = 'manual'
|
||||
sections.ipv4.addresses = `${config.ipaddr}/${config.prefix}`
|
||||
sections.ipv4.gateway = config.gateway
|
||||
if (dnsSuffix) sections.ipv4.dns = dnsSuffix
|
||||
if (config.routeMetric) sections.ipv4['route-metric'] = String(config.routeMetric)
|
||||
}
|
||||
out.push(``)
|
||||
out.push(`# >>> 文件: ${config.interface}.nmconnection`)
|
||||
out.push(`# >>> 接口 ${config.interface} (${config.configType === 'dhcp' ? 'DHCP' : '静态'})`)
|
||||
out.push(genKeyfile(sections))
|
||||
}
|
||||
|
||||
if (config.configType === 'bond') {
|
||||
// bond 主接口
|
||||
const bondSections = {
|
||||
connection: {
|
||||
id: config.bondName,
|
||||
type: 'bond',
|
||||
'interface-name': config.bondName,
|
||||
autoconnect: autoStr,
|
||||
},
|
||||
bond: {
|
||||
miimon: String(config.miimon),
|
||||
mode: config.bondMode,
|
||||
},
|
||||
ipv4: {
|
||||
method: 'manual',
|
||||
addresses: `${config.ipaddr}/${config.prefix}`,
|
||||
gateway: config.gateway,
|
||||
dns: dnsSuffix,
|
||||
},
|
||||
ipv6: { method: config.ipv6Method },
|
||||
}
|
||||
if (config.routeMetric) bondSections.ipv4['route-metric'] = String(config.routeMetric)
|
||||
if (config.bondMode === '802.3ad') {
|
||||
bondSections.bond['lacp_rate'] = config.lacpRate
|
||||
}
|
||||
out.push(``)
|
||||
out.push(`# >>> 文件: ${config.bondName}.nmconnection`)
|
||||
out.push(`# >>> Bond 主接口 ${config.bondName} (mode=${config.bondMode})`)
|
||||
out.push(genKeyfile(bondSections))
|
||||
|
||||
// 成员接口
|
||||
const slaves = String(config.bondSlaves || '').split(',').map((s) => s.trim()).filter(Boolean)
|
||||
for (const slv of slaves) {
|
||||
const slaveSections = {
|
||||
connection: {
|
||||
id: slv,
|
||||
type: 'ethernet',
|
||||
'interface-name': slv,
|
||||
master: config.bondName,
|
||||
'slave-type': 'bond',
|
||||
autoconnect: autoStr,
|
||||
},
|
||||
ipv4: { method: 'disabled' },
|
||||
ipv6: { method: config.ipv6Method },
|
||||
}
|
||||
out.push(`# >>> 文件: ${slv}.nmconnection`)
|
||||
out.push(`# >>> Bond 成员 ${slv} → ${config.bondName}`)
|
||||
out.push(genKeyfile(slaveSections))
|
||||
}
|
||||
out.push(`# 提示: mode=802.3ad 需要在交换机端开启 LACP`)
|
||||
}
|
||||
|
||||
if (config.configType === 'bridge') {
|
||||
// bridge 主接口
|
||||
const brSections = {
|
||||
connection: {
|
||||
id: config.bridgeName,
|
||||
type: 'bridge',
|
||||
'interface-name': config.bridgeName,
|
||||
autoconnect: autoStr,
|
||||
},
|
||||
bridge: {
|
||||
stp: config.bridgeStp ? 'yes' : 'no',
|
||||
'hello-time': String(config.bridgeHelloTime),
|
||||
},
|
||||
ipv4: {
|
||||
method: 'manual',
|
||||
addresses: `${config.ipaddr}/${config.prefix}`,
|
||||
gateway: config.gateway,
|
||||
dns: dnsSuffix,
|
||||
},
|
||||
ipv6: { method: config.ipv6Method },
|
||||
}
|
||||
if (config.routeMetric) brSections.ipv4['route-metric'] = String(config.routeMetric)
|
||||
out.push(``)
|
||||
out.push(`# >>> 文件: ${config.bridgeName}.nmconnection`)
|
||||
out.push(`# >>> Bridge ${config.bridgeName} (STP=${config.bridgeStp ? 'on' : 'off'})`)
|
||||
out.push(genKeyfile(brSections))
|
||||
|
||||
// 成员接口
|
||||
const ports = String(config.bridgePorts || '').split(',').map((s) => s.trim()).filter(Boolean)
|
||||
for (const p of ports) {
|
||||
const portSections = {
|
||||
connection: {
|
||||
id: p,
|
||||
type: 'ethernet',
|
||||
'interface-name': p,
|
||||
master: config.bridgeName,
|
||||
'slave-type': 'bridge',
|
||||
autoconnect: autoStr,
|
||||
},
|
||||
ipv4: { method: 'disabled' },
|
||||
ipv6: { method: config.ipv6Method },
|
||||
}
|
||||
out.push(`# >>> 文件: ${p}.nmconnection`)
|
||||
out.push(`# >>> Bridge 成员 ${p} → ${config.bridgeName}`)
|
||||
out.push(genKeyfile(portSections))
|
||||
}
|
||||
}
|
||||
|
||||
if (config.configType === 'vlan') {
|
||||
const subName = `${config.vlanParent}.${config.vlanId}`
|
||||
const vlanSections = {
|
||||
connection: {
|
||||
id: subName,
|
||||
type: 'vlan',
|
||||
'interface-name': subName,
|
||||
autoconnect: autoStr,
|
||||
},
|
||||
vlan: {
|
||||
parent: config.vlanParent,
|
||||
id: String(config.vlanId),
|
||||
},
|
||||
ipv4: {
|
||||
method: 'manual',
|
||||
addresses: `${config.ipaddr}/${config.prefix}`,
|
||||
gateway: config.gateway,
|
||||
dns: dnsSuffix,
|
||||
},
|
||||
ipv6: { method: config.ipv6Method },
|
||||
}
|
||||
if (config.routeMetric) vlanSections.ipv4['route-metric'] = String(config.routeMetric)
|
||||
out.push(``)
|
||||
out.push(`# >>> 文件: ${subName}.nmconnection`)
|
||||
out.push(`# >>> VLAN 子接口 ${subName} (VLAN ID=${config.vlanId})`)
|
||||
out.push(genKeyfile(vlanSections))
|
||||
}
|
||||
|
||||
return out.join('\n')
|
||||
}
|
||||
@@ -0,0 +1,329 @@
|
||||
// Ubuntu Netplan 网络配置
|
||||
// 覆盖: Ubuntu 18.04 / 20.04 / 22.04 / 24.04
|
||||
// 配置文件位于 /etc/netplan/<name>.yaml
|
||||
|
||||
export const networkUbuntuNetplanSchema = {
|
||||
id: 'network-ubuntu-netplan',
|
||||
name: 'Ubuntu Netplan',
|
||||
icon: 'Connection',
|
||||
category: '网络配置',
|
||||
description: 'Ubuntu Netplan 配置(含 Bond/Bridge/VLAN)',
|
||||
format: 'yaml',
|
||||
fileName: '01-netcfg.yaml',
|
||||
groups: [
|
||||
{
|
||||
title: '系统版本',
|
||||
fields: [
|
||||
{
|
||||
key: 'osVersion',
|
||||
label: '操作系统版本',
|
||||
type: 'select',
|
||||
options: [
|
||||
{ label: 'Ubuntu 18.04 LTS', value: '18.04' },
|
||||
{ label: 'Ubuntu 20.04 LTS', value: '20.04' },
|
||||
{ label: 'Ubuntu 22.04 LTS', value: '22.04' },
|
||||
{ label: 'Ubuntu 24.04 LTS', value: '24.04' },
|
||||
],
|
||||
default: '22.04',
|
||||
tip: '18.04 推荐 renderer: networkd;20.04+ 同样支持',
|
||||
},
|
||||
{
|
||||
key: 'renderer',
|
||||
label: '渲染器',
|
||||
type: 'select',
|
||||
options: [
|
||||
{ label: 'networkd - systemd-networkd', value: 'networkd' },
|
||||
{ label: 'NetworkManager', value: 'NetworkManager' },
|
||||
],
|
||||
default: 'networkd',
|
||||
tip: '服务器默认 networkd;桌面版可改 NetworkManager',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '配置类型',
|
||||
fields: [
|
||||
{
|
||||
key: 'configType',
|
||||
label: '配置类型',
|
||||
type: 'select',
|
||||
options: [
|
||||
{ label: '单网卡 - 静态 IP', value: 'static' },
|
||||
{ label: '单网卡 - DHCP', value: 'dhcp' },
|
||||
{ label: 'Bond - 链路聚合', value: 'bond' },
|
||||
{ label: 'Bridge - 桥接', value: 'bridge' },
|
||||
{ label: 'VLAN - 子接口', value: 'vlan' },
|
||||
],
|
||||
default: 'static',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '主接口配置',
|
||||
fields: [
|
||||
{
|
||||
key: 'interface',
|
||||
label: '主接口名',
|
||||
type: 'text',
|
||||
default: 'eth0',
|
||||
tip: 'Ubuntu 18.04 常见 ens33 / enp0s3;20.04+ 已恢复 eth0',
|
||||
},
|
||||
{
|
||||
key: 'ipaddr',
|
||||
label: 'IP 地址',
|
||||
type: 'text',
|
||||
default: '192.168.1.10',
|
||||
dependsOn: { key: 'configType', valueIn: ['static', 'bond', 'bridge', 'vlan'] },
|
||||
},
|
||||
{
|
||||
key: 'prefix',
|
||||
label: '子网前缀 (CIDR)',
|
||||
type: 'number',
|
||||
min: 1,
|
||||
max: 32,
|
||||
default: 24,
|
||||
dependsOn: { key: 'configType', valueIn: ['static', 'bond', 'bridge', 'vlan'] },
|
||||
},
|
||||
{
|
||||
key: 'gateway',
|
||||
label: '默认网关',
|
||||
type: 'text',
|
||||
default: '192.168.1.1',
|
||||
dependsOn: { key: 'configType', valueIn: ['static', 'bond', 'bridge', 'vlan'] },
|
||||
},
|
||||
{
|
||||
key: 'dns',
|
||||
label: 'DNS (逗号分隔)',
|
||||
type: 'text',
|
||||
default: '8.8.8.8,8.8.4.4',
|
||||
dependsOn: { key: 'configType', valueIn: ['static', 'bond', 'bridge', 'vlan'] },
|
||||
},
|
||||
{
|
||||
key: 'routeMetric',
|
||||
label: '路由 Metric',
|
||||
type: 'number',
|
||||
min: 0,
|
||||
max: 9999,
|
||||
default: 0,
|
||||
tip: '默认路由优先级,值越小优先级越高;0 表示不输出',
|
||||
dependsOn: { key: 'configType', valueIn: ['static', 'bond', 'bridge', 'vlan'] },
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Bond 配置',
|
||||
fields: [
|
||||
{
|
||||
key: 'bondName',
|
||||
label: 'Bond 接口名',
|
||||
type: 'text',
|
||||
default: 'bond0',
|
||||
dependsOn: { key: 'configType', value: 'bond' },
|
||||
},
|
||||
{
|
||||
key: 'bondMode',
|
||||
label: 'Bond 模式',
|
||||
type: 'select',
|
||||
options: [
|
||||
{ label: 'balance-rr (0)', value: 'balance-rr' },
|
||||
{ label: 'active-backup (1)', value: 'active-backup' },
|
||||
{ label: 'balance-xor (2)', value: 'balance-xor' },
|
||||
{ label: 'broadcast (3)', value: 'broadcast' },
|
||||
{ label: '802.3ad (4 LACP)', value: '802.3ad' },
|
||||
{ label: 'balance-tlb (5)', value: 'balance-tlb' },
|
||||
{ label: 'balance-alb (6)', value: 'balance-alb' },
|
||||
],
|
||||
default: '802.3ad',
|
||||
dependsOn: { key: 'configType', value: 'bond' },
|
||||
},
|
||||
{
|
||||
key: 'miimon',
|
||||
label: 'MII 监控间隔 (ms)',
|
||||
type: 'number',
|
||||
min: 0,
|
||||
max: 10000,
|
||||
default: 100,
|
||||
dependsOn: { key: 'configType', value: 'bond' },
|
||||
},
|
||||
{
|
||||
key: 'lacpRate',
|
||||
label: 'LACP 速率',
|
||||
type: 'select',
|
||||
options: [
|
||||
{ label: 'slow (30s)', value: 'slow' },
|
||||
{ label: 'fast (1s)', value: 'fast' },
|
||||
],
|
||||
default: 'fast',
|
||||
dependsOn: { key: 'configType', value: 'bond' },
|
||||
},
|
||||
{
|
||||
key: 'bondSlaves',
|
||||
label: '成员网卡 (逗号分隔)',
|
||||
type: 'text',
|
||||
default: 'eth0,eth1',
|
||||
dependsOn: { key: 'configType', value: 'bond' },
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Bridge 配置',
|
||||
fields: [
|
||||
{
|
||||
key: 'bridgeName',
|
||||
label: 'Bridge 接口名',
|
||||
type: 'text',
|
||||
default: 'br0',
|
||||
dependsOn: { key: 'configType', value: 'bridge' },
|
||||
},
|
||||
{
|
||||
key: 'bridgePorts',
|
||||
label: '桥接成员 (逗号分隔)',
|
||||
type: 'text',
|
||||
default: 'eth0',
|
||||
dependsOn: { key: 'configType', value: 'bridge' },
|
||||
},
|
||||
{
|
||||
key: 'bridgeStp',
|
||||
label: '启用 STP',
|
||||
type: 'switch',
|
||||
default: false,
|
||||
dependsOn: { key: 'configType', value: 'bridge' },
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'VLAN 配置',
|
||||
fields: [
|
||||
{
|
||||
key: 'vlanParent',
|
||||
label: '父接口',
|
||||
type: 'text',
|
||||
default: 'eth0',
|
||||
dependsOn: { key: 'configType', value: 'vlan' },
|
||||
},
|
||||
{
|
||||
key: 'vlanId',
|
||||
label: 'VLAN ID',
|
||||
type: 'number',
|
||||
min: 1,
|
||||
max: 4094,
|
||||
default: 100,
|
||||
dependsOn: { key: 'configType', value: 'vlan' },
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
// 生成接口下的 IPv4 静态配置块(addresses / routes / nameservers)
|
||||
// base = 接口下属性的缩进空格数(如接口在 2 空格则 base=4)
|
||||
function ipBlock(base, extraLines) {
|
||||
const pad = (n) => ' '.repeat(base + n)
|
||||
const lines = []
|
||||
lines.push(`${pad(0)}addresses:`)
|
||||
lines.push(`${pad(2)}- ${this.cidr}`)
|
||||
if (this.gateway) {
|
||||
lines.push(`${pad(0)}routes:`)
|
||||
lines.push(`${pad(2)}- to: default`)
|
||||
lines.push(`${pad(4)}via: ${this.gateway}`)
|
||||
if (this.routeMetric) {
|
||||
lines.push(`${pad(4)}metric: ${this.routeMetric}`)
|
||||
}
|
||||
}
|
||||
if (this.dnsList.length > 0) {
|
||||
lines.push(`${pad(0)}nameservers:`)
|
||||
lines.push(`${pad(2)}addresses:`)
|
||||
for (const d of this.dnsList) lines.push(`${pad(4)}- ${d}`)
|
||||
}
|
||||
return lines.concat(extraLines || [])
|
||||
}
|
||||
|
||||
export function generateNetworkUbuntuNetplan(config) {
|
||||
const dnsList = String(config.dns || '').split(',').map((s) => s.trim()).filter(Boolean)
|
||||
const cidr = `${config.ipaddr}/${config.prefix}`
|
||||
const ts = new Date().toLocaleString('zh-CN')
|
||||
|
||||
// 绑定 ipBlock 的 this 上下文
|
||||
const ctx = { cidr, gateway: config.gateway, dnsList, routeMetric: config.routeMetric }
|
||||
const genIp = (base, extra) => ipBlock.call(ctx, base, extra)
|
||||
|
||||
const body = [] // 每个元素是一行,已包含完整缩进
|
||||
|
||||
if (config.configType === 'static') {
|
||||
body.push(` ${config.interface}:`)
|
||||
body.push(...genIp(4))
|
||||
} else if (config.configType === 'dhcp') {
|
||||
body.push(` ${config.interface}:`)
|
||||
body.push(` dhcp4: true`)
|
||||
if (dnsList.length > 0) {
|
||||
body.push(` nameservers:`)
|
||||
body.push(` addresses:`)
|
||||
for (const d of dnsList) body.push(` - ${d}`)
|
||||
}
|
||||
} else if (config.configType === 'bond') {
|
||||
const slaves = String(config.bondSlaves || '').split(',').map((s) => s.trim()).filter(Boolean)
|
||||
body.push(` ethernets:`)
|
||||
for (const slv of slaves) {
|
||||
body.push(` ${slv}:`)
|
||||
body.push(` dhcp4: false`)
|
||||
body.push(` optional: true`)
|
||||
}
|
||||
body.push(` bonds:`)
|
||||
body.push(` ${config.bondName}:`)
|
||||
body.push(` interfaces:`)
|
||||
for (const slv of slaves) body.push(` - ${slv}`)
|
||||
body.push(...genIp(6, [
|
||||
` parameters:`,
|
||||
` mode: ${config.bondMode}`,
|
||||
` miimon: ${config.miimon}`,
|
||||
...(config.bondMode === '802.3ad' ? [` lacp-rate: ${config.lacpRate}`] : []),
|
||||
]))
|
||||
} else if (config.configType === 'bridge') {
|
||||
const ports = String(config.bridgePorts || '').split(',').map((s) => s.trim()).filter(Boolean)
|
||||
body.push(` ethernets:`)
|
||||
for (const p of ports) {
|
||||
body.push(` ${p}:`)
|
||||
body.push(` dhcp4: false`)
|
||||
body.push(` optional: true`)
|
||||
}
|
||||
body.push(` bridges:`)
|
||||
body.push(` ${config.bridgeName}:`)
|
||||
body.push(` interfaces:`)
|
||||
for (const p of ports) body.push(` - ${p}`)
|
||||
body.push(...genIp(6, [
|
||||
` parameters:`,
|
||||
` stp: ${config.bridgeStp ? 'true' : 'false'}`,
|
||||
]))
|
||||
} else if (config.configType === 'vlan') {
|
||||
const subName = `${config.vlanParent}.${config.vlanId}`
|
||||
body.push(` ethernets:`)
|
||||
body.push(` ${config.vlanParent}:`)
|
||||
body.push(` dhcp4: false`)
|
||||
body.push(` optional: true`)
|
||||
body.push(` vlans:`)
|
||||
body.push(` ${subName}:`)
|
||||
body.push(...genIp(6, [
|
||||
` id: ${config.vlanId}`,
|
||||
` link: ${config.vlanParent}`,
|
||||
]))
|
||||
}
|
||||
|
||||
const header = []
|
||||
header.push(`# =========================================================`)
|
||||
header.push(`# Ubuntu Netplan 配置 - 由 ConfTemplate 生成`)
|
||||
header.push(`# 生成时间: ${ts}`)
|
||||
header.push(`# 目标文件: /etc/netplan/01-netcfg.yaml`)
|
||||
header.push(`# 配置类型: ${config.configType}`)
|
||||
header.push(`# =========================================================`)
|
||||
header.push(`#`)
|
||||
header.push(`# 应用步骤:`)
|
||||
header.push(`# 1. 将本文件保存到 /etc/netplan/01-netcfg.yaml`)
|
||||
header.push(`# 2. 权限: sudo chmod 600 /etc/netplan/01-netcfg.yaml`)
|
||||
header.push(`# 3. 应用: sudo netplan apply`)
|
||||
header.push(`# 4. 验证: ip a / networkctl status`)
|
||||
header.push(`#`)
|
||||
header.push(`# 注意: netplan 文件需 600 权限,否则 apply 会拒绝执行`)
|
||||
header.push(``)
|
||||
|
||||
return header.join('\n') + `network:\n version: 2\n renderer: ${config.renderer}\n` + body.join('\n') + '\n'
|
||||
}
|
||||
Reference in New Issue
Block a user