mirror of
https://github.com/MengMengCode/CLICD.git
synced 2026-08-06 05:52:19 +08:00
FIX #18
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { useEffect, useMemo, useState, type ReactNode } from 'react'
|
||||
import { CalendarClock, RefreshCw, X } from 'lucide-react'
|
||||
import { batchCreate, getIPv6Status, getEnabledImages, getHostInfo, CreateContainerRequest, HostInfo, IPv6Status, Template } from '../services/api'
|
||||
import { batchCreate, getIPv6Status, getEnabledImages, getHostInfo, getHostReport, CreateContainerRequest, HostInfo, HostProbeReport, IPv6Status, Template } from '../services/api'
|
||||
import { useDialog } from './Dialog'
|
||||
import { useLanguage, type Language } from '../contexts/LanguageContext'
|
||||
import { generateSSHPassword, sshPasswordError, sshPublicKeyError, type SSHAuthMode } from '../utils/sshAuth'
|
||||
@@ -33,6 +33,11 @@ const defaultForm: CreateContainerRequest = {
|
||||
extra_ports: [],
|
||||
port_mapping_count: 2,
|
||||
assign_nat: true,
|
||||
lan_ipv4_mode: '',
|
||||
lan_interface: '',
|
||||
lan_ipv4_address: '',
|
||||
lan_ipv4_prefix_len: 24,
|
||||
lan_ipv4_gateway: '',
|
||||
snapshot_limit: 1,
|
||||
assign_ipv4: false,
|
||||
ipv4_count: 1,
|
||||
@@ -57,6 +62,7 @@ export default function CreateContainerModal({ isOpen, onClose, onSuccess, exist
|
||||
const [batchCount, setBatchCount] = useState(1)
|
||||
const [form, setForm] = useState<CreateContainerRequest>(defaultForm)
|
||||
const [hostInfo, setHostInfo] = useState<HostInfo | null>(null)
|
||||
const [hostReport, setHostReport] = useState<HostProbeReport | null>(null)
|
||||
const [ipv6Status, setIPv6Status] = useState<IPv6Status | null>(null)
|
||||
const [nameError, setNameError] = useState('')
|
||||
|
||||
@@ -97,6 +103,10 @@ export default function CreateContainerModal({ isOpen, onClose, onSuccess, exist
|
||||
getHostInfo()
|
||||
.then((res) => setHostInfo(res.data.data || null))
|
||||
.catch(() => setHostInfo(null))
|
||||
|
||||
getHostReport()
|
||||
.then((res) => setHostReport(res.data.data || null))
|
||||
.catch(() => setHostReport(null))
|
||||
}, [isOpen, form.virtualization])
|
||||
|
||||
const ipv6Available = !!ipv6Status?.available
|
||||
@@ -116,7 +126,11 @@ export default function CreateContainerModal({ isOpen, onClose, onSuccess, exist
|
||||
}, [hostInfo, kvmAvailable, form.virtualization])
|
||||
const maxDiskGB = hostInfo?.disk.total_gb ? Math.max(1, Math.floor(hostInfo.disk.total_gb)) : undefined
|
||||
const resourceErrors = validateResourceInputs(form, maxVCPU, maxRAMMB, maxDiskGB)
|
||||
const natEnabled = form.assign_nat !== false
|
||||
const lanIPv4Enabled = form.lan_ipv4_mode === 'dhcp' || form.lan_ipv4_mode === 'static'
|
||||
const lanStaticEnabled = form.lan_ipv4_mode === 'static'
|
||||
const natEnabled = form.assign_nat !== false && !lanIPv4Enabled
|
||||
const lanInterfaces = useMemo(() => getLANDHCPInterfaces(hostReport), [hostReport])
|
||||
const defaultLANInterface = lanInterfaces[0]?.name || ''
|
||||
const natPortCount = natEnabled ? Math.max(2, form.port_mapping_count || 2) : 0
|
||||
const linuxTemplate = !isWindowsTemplate(form.template_id)
|
||||
const sshAuthMode = (form.ssh_auth_mode || 'auto_password') as SSHAuthMode
|
||||
@@ -169,11 +183,18 @@ export default function CreateContainerModal({ isOpen, onClose, onSuccess, exist
|
||||
return
|
||||
}
|
||||
|
||||
if (!form.assign_ipv4 && !form.assign_ipv6 && form.assign_nat === false) {
|
||||
if (!form.assign_ipv4 && !form.assign_ipv6 && form.assign_nat === false && form.lan_ipv4_mode !== 'dhcp' && form.lan_ipv4_mode !== 'static') {
|
||||
dialog.alert('提示', '请勾选任意一个可用网络')
|
||||
return
|
||||
}
|
||||
|
||||
if (form.lan_ipv4_mode === 'static') {
|
||||
if (!isIPv4Address(form.lan_ipv4_address || '') || !isIPv4Address(form.lan_ipv4_gateway || '') || !form.lan_ipv4_prefix_len) {
|
||||
dialog.alert('局域网 IPv4 配置有误', '请填写有效的 IPv4 地址、子网掩码和网关')
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
const authError = validateSSHAuthInputs(form)
|
||||
if (authError) {
|
||||
dialog.alert('登录方式有误', authError)
|
||||
@@ -388,7 +409,7 @@ export default function CreateContainerModal({ isOpen, onClose, onSuccess, exist
|
||||
...form,
|
||||
assign_ipv4: event.target.checked,
|
||||
public_ipv4s: event.target.checked ? form.public_ipv4s : [],
|
||||
...(event.target.checked ? { assign_nat: false, port_mapping_count: 0, extra_ports: [] } : {}),
|
||||
...(event.target.checked ? { assign_nat: false, port_mapping_count: 0, extra_ports: [], lan_ipv4_mode: '', lan_interface: '' } : {}),
|
||||
})}
|
||||
className="mt-1"
|
||||
/>
|
||||
@@ -454,6 +475,98 @@ export default function CreateContainerModal({ isOpen, onClose, onSuccess, exist
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className={`rounded-md border px-3 py-2 text-sm ${form.virtualization === 'lxc' && lanInterfaces.length > 0 ? 'border-gray-200 bg-white' : 'border-gray-200 bg-gray-50 text-gray-400'}`}>
|
||||
<div className="flex items-start justify-between gap-3">
|
||||
<label className="flex min-w-0 flex-1 items-start gap-3">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={lanIPv4Enabled}
|
||||
disabled={form.virtualization !== 'lxc' || lanInterfaces.length === 0}
|
||||
onChange={(event) => {
|
||||
const checked = event.target.checked
|
||||
setForm({
|
||||
...form,
|
||||
lan_ipv4_mode: checked ? 'dhcp' : '',
|
||||
lan_interface: checked ? (form.lan_interface || defaultLANInterface) : '',
|
||||
assign_nat: checked ? false : form.assign_nat,
|
||||
port_mapping_count: checked ? 0 : form.port_mapping_count,
|
||||
extra_ports: checked ? [] : form.extra_ports,
|
||||
assign_ipv4: checked ? false : form.assign_ipv4,
|
||||
public_ipv4s: checked ? [] : form.public_ipv4s,
|
||||
ipv4_count: checked ? 0 : form.ipv4_count,
|
||||
})
|
||||
}}
|
||||
className="mt-1"
|
||||
/>
|
||||
<span className="min-w-0">
|
||||
<span className="block font-medium text-gray-800">局域网 DHCP</span>
|
||||
<span className="block text-xs text-gray-500">
|
||||
{lanInterfaces.length > 0 ? 'macvlan 独立局域网 IP' : '未检测到可用上联网卡'}
|
||||
</span>
|
||||
</span>
|
||||
</label>
|
||||
{lanIPv4Enabled && (
|
||||
<select
|
||||
value={form.lan_interface || defaultLANInterface}
|
||||
onChange={(event) => setForm({ ...form, lan_interface: event.target.value })}
|
||||
className="h-9 w-32 shrink-0 rounded-md border border-gray-300 bg-white px-2 text-xs text-gray-700 focus:outline-none focus:ring-1 focus:ring-black"
|
||||
>
|
||||
{lanInterfaces.map((item) => (
|
||||
<option key={item.name} value={item.name}>{item.name}</option>
|
||||
))}
|
||||
</select>
|
||||
)}
|
||||
</div>
|
||||
{lanIPv4Enabled && (
|
||||
<div className="mt-3 space-y-3 pl-6">
|
||||
<div className="grid grid-cols-2 gap-2">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setForm({ ...form, lan_ipv4_mode: 'dhcp' })}
|
||||
className={`rounded-md border px-3 py-2 text-xs font-medium ${form.lan_ipv4_mode === 'dhcp' ? 'border-black bg-black text-white' : 'border-gray-300 text-gray-700 hover:bg-gray-50'}`}
|
||||
>
|
||||
DHCP 自动获取
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setForm({ ...form, lan_ipv4_mode: 'static' })}
|
||||
className={`rounded-md border px-3 py-2 text-xs font-medium ${lanStaticEnabled ? 'border-black bg-black text-white' : 'border-gray-300 text-gray-700 hover:bg-gray-50'}`}
|
||||
>
|
||||
手动配置
|
||||
</button>
|
||||
</div>
|
||||
{lanStaticEnabled && (
|
||||
<div className="grid gap-3 sm:grid-cols-3">
|
||||
<Field label="IPv4 地址">
|
||||
<input
|
||||
value={form.lan_ipv4_address || ''}
|
||||
onChange={(event) => setForm({ ...form, lan_ipv4_address: event.target.value })}
|
||||
className={inputClass}
|
||||
placeholder="192.168.2.250"
|
||||
/>
|
||||
</Field>
|
||||
<Field label="子网掩码">
|
||||
<input
|
||||
value={subnetMaskFromPrefixLen(form.lan_ipv4_prefix_len || 24)}
|
||||
onChange={(event) => setForm({ ...form, lan_ipv4_prefix_len: prefixLenFromSubnetMask(event.target.value) || 24 })}
|
||||
className={inputClass}
|
||||
placeholder="255.255.255.0"
|
||||
/>
|
||||
</Field>
|
||||
<Field label="网关">
|
||||
<input
|
||||
value={form.lan_ipv4_gateway || ''}
|
||||
onChange={(event) => setForm({ ...form, lan_ipv4_gateway: event.target.value })}
|
||||
className={inputClass}
|
||||
placeholder="192.168.2.202"
|
||||
/>
|
||||
</Field>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className={`rounded-md border px-3 py-2 text-sm ${ipv6Available ? 'border-gray-200 bg-white' : 'border-gray-200 bg-gray-50 text-gray-400'}`}>
|
||||
<div className="flex items-start justify-between gap-3">
|
||||
<label className="flex min-w-0 flex-1 items-start gap-3">
|
||||
@@ -497,7 +610,7 @@ export default function CreateContainerModal({ isOpen, onClose, onSuccess, exist
|
||||
assign_nat: checked,
|
||||
port_mapping_count: checked ? Math.max(2, form.port_mapping_count || 2) : 0,
|
||||
extra_ports: [],
|
||||
...(checked ? { assign_ipv4: false, public_ipv4s: [], ipv4_count: 0 } : {}),
|
||||
...(checked ? { assign_ipv4: false, public_ipv4s: [], ipv4_count: 0, lan_ipv4_mode: '', lan_interface: '' } : {}),
|
||||
})
|
||||
}}
|
||||
className="mt-1"
|
||||
@@ -757,10 +870,13 @@ function validateResourceInputs(form: CreateContainerRequest, maxVCPU: number, m
|
||||
|
||||
function normalizeCreateForm(form: CreateContainerRequest): CreateContainerRequest {
|
||||
const normalized = applyTemplateDefaults(form)
|
||||
const wantsLANDHCP = normalized.virtualization === 'lxc' && normalized.lan_ipv4_mode === 'dhcp'
|
||||
const wantsLANStatic = normalized.virtualization === 'lxc' && normalized.lan_ipv4_mode === 'static'
|
||||
const wantsLANIPv4 = wantsLANDHCP || wantsLANStatic
|
||||
const wantsIPv4 = !!normalized.assign_ipv4
|
||||
const wantsIPv6 = !!normalized.assign_ipv6
|
||||
// IPv4 and NAT are mutually exclusive
|
||||
const wantsNAT = wantsIPv4 ? false : normalized.assign_nat !== false
|
||||
const wantsNAT = wantsLANIPv4 || wantsIPv4 ? false : normalized.assign_nat !== false
|
||||
const linuxTemplate = !isWindowsTemplate(normalized.template_id)
|
||||
const sshAuthMode = linuxTemplate ? (normalized.ssh_auth_mode || 'auto_password') : 'auto_password'
|
||||
return {
|
||||
@@ -770,6 +886,11 @@ function normalizeCreateForm(form: CreateContainerRequest): CreateContainerReque
|
||||
disk_gb: Math.round(normalized.disk_gb),
|
||||
assign_nat: wantsNAT,
|
||||
port_mapping_count: wantsNAT ? clampInt(normalized.port_mapping_count, 2, 64, 2) : 0,
|
||||
lan_ipv4_mode: wantsLANDHCP ? 'dhcp' : (wantsLANStatic ? 'static' : ''),
|
||||
lan_interface: wantsLANIPv4 ? (normalized.lan_interface || '').trim() : '',
|
||||
lan_ipv4_address: wantsLANStatic ? (normalized.lan_ipv4_address || '').trim() : '',
|
||||
lan_ipv4_prefix_len: wantsLANStatic ? clampInt(normalized.lan_ipv4_prefix_len || 24, 1, 32, 24) : 0,
|
||||
lan_ipv4_gateway: wantsLANStatic ? (normalized.lan_ipv4_gateway || '').trim() : '',
|
||||
assign_ipv4: wantsIPv4,
|
||||
ipv4_count: wantsIPv4 ? clampInt(normalized.ipv4_count || 1, 1, 64, 1) : 0,
|
||||
public_ipv4s: wantsIPv4 ? (normalized.public_ipv4s || []) : [],
|
||||
@@ -792,6 +913,16 @@ function validateSSHAuthInputs(form: CreateContainerRequest) {
|
||||
return ''
|
||||
}
|
||||
|
||||
function getLANDHCPInterfaces(report: HostProbeReport | null) {
|
||||
const interfaces = report?.network_interfaces || []
|
||||
return interfaces.filter((item) => {
|
||||
const name = item.name || ''
|
||||
if (!name || name === 'lo') return false
|
||||
if (name.startsWith('lxc') || name.startsWith('docker') || name.startsWith('br-') || name.startsWith('veth') || name.startsWith('virbr') || name.startsWith('clmv-')) return false
|
||||
return (item.state || '').toLowerCase() === 'up'
|
||||
})
|
||||
}
|
||||
|
||||
function applyTemplateDefaults(form: CreateContainerRequest): CreateContainerRequest {
|
||||
if (!isWindowsTemplate(form.template_id)) return form
|
||||
return {
|
||||
@@ -817,6 +948,28 @@ function clampInt(value: number, min: number, max?: number, fallback = min) {
|
||||
return Math.min(Math.max(next, min), max ?? next)
|
||||
}
|
||||
|
||||
function isIPv4Address(value: string) {
|
||||
const parts = value.trim().split('.')
|
||||
return parts.length === 4 && parts.every((part) => {
|
||||
if (!/^\d+$/.test(part)) return false
|
||||
const n = Number(part)
|
||||
return n >= 0 && n <= 255
|
||||
})
|
||||
}
|
||||
|
||||
function subnetMaskFromPrefixLen(prefixLen: number) {
|
||||
if (!Number.isFinite(prefixLen) || prefixLen < 0 || prefixLen > 32) return '255.255.255.0'
|
||||
const mask = prefixLen === 0 ? 0 : (0xffffffff << (32 - prefixLen)) >>> 0
|
||||
return [24, 16, 8, 0].map((shift) => (mask >>> shift) & 255).join('.')
|
||||
}
|
||||
|
||||
function prefixLenFromSubnetMask(mask: string) {
|
||||
if (!isIPv4Address(mask)) return 0
|
||||
const bits = mask.split('.').map((part) => Number(part).toString(2).padStart(8, '0')).join('')
|
||||
if (!/^1*0*$/.test(bits)) return 0
|
||||
return bits.indexOf('0') === -1 ? 32 : bits.indexOf('0')
|
||||
}
|
||||
|
||||
const createNetworkText = {
|
||||
zh: {
|
||||
publicIPv4: '公网 IPv4',
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
updateRoutingPools,
|
||||
type IPv4Route,
|
||||
type IPv6Route,
|
||||
type LANDHCPRoute,
|
||||
type IPv6PrefixInfo,
|
||||
type NAT4PortRange,
|
||||
type NAT4Route,
|
||||
@@ -56,6 +57,7 @@ export default function Routing() {
|
||||
|
||||
const publicIPv4s = routing?.public_ipv4_addresses || []
|
||||
const ipv4Assignments = routing?.ipv4_assignments || []
|
||||
const lanDHCPAssignments = routing?.lan_dhcp_assignments || []
|
||||
const nat4Mappings = routing?.nat4_mappings || []
|
||||
const ipv6Prefixes = routing?.ipv6_prefixes || []
|
||||
const ipv6Assignments = routing?.ipv6_assignments || []
|
||||
@@ -276,7 +278,7 @@ export default function Routing() {
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-4 md:grid-cols-3">
|
||||
<div className="grid gap-4 md:grid-cols-4">
|
||||
<CapacityCard
|
||||
title={text.nat4Ports}
|
||||
watermark="NAT4"
|
||||
@@ -293,6 +295,7 @@ export default function Routing() {
|
||||
}
|
||||
/>
|
||||
<CapacityCard title={text.publicIPv4} watermark="IPv4" remaining={routing?.ipv4.remaining || '0'} total={routing?.ipv4.total || '0'} used={routing?.ipv4.used || 0} label={formatPoolCount(publicIPv4s.length, language)} usedLabel={text.used} />
|
||||
<CapacityCard title={text.lanDHCP} watermark="LAN" remaining={String(routing?.lan_dhcp.used || 0)} total={routing?.lan_dhcp.total || 'DHCP'} used={routing?.lan_dhcp.used || 0} label={text.dhcpManagedByLAN} usedLabel={text.used} />
|
||||
<CapacityCard title="IPv6" watermark="IPv6" remaining={formatCapacity(routing?.ipv6.remaining || '0', language)} total={formatCapacity(routing?.ipv6.total || '0', language)} used={routing?.ipv6.used || 0} label={formatDetectedPrefixCount(ipv6Prefixes.length, language)} usedLabel={text.used} />
|
||||
</div>
|
||||
|
||||
@@ -541,6 +544,48 @@ export default function Routing() {
|
||||
</RouteModal>
|
||||
)}
|
||||
|
||||
<Panel title={text.lanDHCPAssignments} subtitle={formatAddressSubtitle(lanDHCPAssignments.length, lanDHCPAssignments.length, language)}>
|
||||
{lanDHCPAssignments.length === 0 ? (
|
||||
<EmptyState text={text.noLANDHCPAssignments} icon={<Network className="h-7 w-7" />} />
|
||||
) : (
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full min-w-[980px] text-sm">
|
||||
<thead className="border-b border-gray-200 bg-gray-50 text-xs text-gray-500">
|
||||
<tr>
|
||||
<th className="px-4 py-3 text-left font-medium">{text.container}</th>
|
||||
<th className="px-4 py-3 text-left font-medium">{text.runtimeName}</th>
|
||||
<th className="px-4 py-3 text-left font-medium">{text.guestIPv4}</th>
|
||||
<th className="px-4 py-3 text-left font-medium">模式</th>
|
||||
<th className="px-4 py-3 text-left font-medium">{text.gateway}</th>
|
||||
<th className="px-4 py-3 text-left font-medium">MAC</th>
|
||||
<th className="px-4 py-3 text-left font-medium">{text.interface}</th>
|
||||
<th className="px-4 py-3 text-left font-medium">{text.status}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-gray-100">
|
||||
{lanDHCPAssignments.map((item: LANDHCPRoute) => (
|
||||
<tr key={`${item.container_id}-${item.interface}-${item.mac_address || item.address}`} className="hover:bg-gray-50">
|
||||
<td className="px-4 py-3">
|
||||
<button onClick={() => navigate(`/container/${item.container_id}`)} className="inline-flex items-center gap-2 text-left font-medium text-black hover:underline">
|
||||
<Server className="h-4 w-4 text-gray-400" />
|
||||
{item.container_name}
|
||||
</button>
|
||||
</td>
|
||||
<td className="px-4 py-3 font-mono text-xs text-gray-600">{item.lxc_name}</td>
|
||||
<td className="px-4 py-3 font-mono text-xs text-gray-700">{item.address ? `${item.address}${item.prefix_len ? `/${item.prefix_len}` : ''}` : '-'}</td>
|
||||
<td className="px-4 py-3 text-xs text-gray-600">{item.mode === 'static' ? '手动' : 'DHCP'}</td>
|
||||
<td className="px-4 py-3 font-mono text-xs text-gray-600">{item.gateway || '-'}</td>
|
||||
<td className="px-4 py-3 font-mono text-xs text-gray-600">{item.mac_address || '-'}</td>
|
||||
<td className="px-4 py-3 font-mono text-xs text-gray-600">{item.interface || '-'}</td>
|
||||
<td className="px-4 py-3"><StatusBadge status={item.status} language={language} /></td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
)}
|
||||
</Panel>
|
||||
|
||||
<Panel title={text.ipv4NAT} subtitle={formatMappingSubtitle(filteredNat4.length, nat4Mappings.length, language)} action={<SearchBox value={nat4Search} onChange={setNat4Search} placeholder={text.searchNAT} />}>
|
||||
{nat4Mappings.length === 0 ? (
|
||||
<EmptyState text={text.noIPv4NATMappings} icon={<Network className="h-7 w-7" />} />
|
||||
@@ -851,6 +896,10 @@ const routingText = {
|
||||
saveNAT4RangeFailed: '保存 NAT4 范围失败',
|
||||
remainingTotal: '剩余 / 总数',
|
||||
publicIPv4: '公网 IPv4',
|
||||
lanDHCP: '局域网 DHCP',
|
||||
dhcpManagedByLAN: '由局域网 DHCP 分配',
|
||||
lanDHCPAssignments: '局域网 DHCP 分配',
|
||||
noLANDHCPAssignments: '暂无局域网 DHCP 分配',
|
||||
publicIPv4Pool: '公网 IPv4 池',
|
||||
editPool: '编辑 IP 池',
|
||||
noPublicIPv4Pool: '暂未配置公网 IPv4 池',
|
||||
@@ -922,6 +971,10 @@ const routingText = {
|
||||
saveNAT4RangeFailed: 'Save NAT4 range failed',
|
||||
remainingTotal: 'remaining / total',
|
||||
publicIPv4: 'Public IPv4',
|
||||
lanDHCP: 'LAN DHCP',
|
||||
dhcpManagedByLAN: 'Managed by LAN DHCP',
|
||||
lanDHCPAssignments: 'LAN DHCP assignments',
|
||||
noLANDHCPAssignments: 'No LAN DHCP assignments',
|
||||
publicIPv4Pool: 'Public IPv4 pool',
|
||||
editPool: 'Edit pool',
|
||||
noPublicIPv4Pool: 'No public IPv4 pool configured',
|
||||
|
||||
@@ -94,6 +94,12 @@ export interface Container {
|
||||
io_write_mbps: number
|
||||
status: string
|
||||
ip: string
|
||||
lan_ipv4_mode?: string
|
||||
lan_interface?: string
|
||||
lan_ipv4_address?: string
|
||||
lan_ipv4_prefix_len?: number
|
||||
lan_ipv4_gateway?: string
|
||||
mac_address?: string
|
||||
public_ipv4s?: PublicIPv4Assignment[]
|
||||
ipv6: string
|
||||
ipv6_prefix_len: number
|
||||
@@ -154,6 +160,11 @@ export interface CreateContainerRequest {
|
||||
extra_ports: number[]
|
||||
port_mapping_count: number
|
||||
assign_nat?: boolean
|
||||
lan_ipv4_mode?: string
|
||||
lan_interface?: string
|
||||
lan_ipv4_address?: string
|
||||
lan_ipv4_prefix_len?: number
|
||||
lan_ipv4_gateway?: string
|
||||
snapshot_limit: number
|
||||
assign_ipv4?: boolean
|
||||
ipv4_count?: number
|
||||
@@ -602,6 +613,19 @@ export interface IPv4Route {
|
||||
gateway?: string
|
||||
}
|
||||
|
||||
export interface LANDHCPRoute {
|
||||
container_id: number
|
||||
container_name: string
|
||||
lxc_name: string
|
||||
status: string
|
||||
address: string
|
||||
interface: string
|
||||
prefix_len?: number
|
||||
gateway?: string
|
||||
mac_address?: string
|
||||
mode: string
|
||||
}
|
||||
|
||||
export interface IPv6Route {
|
||||
container_id: number
|
||||
container_name: string
|
||||
@@ -616,10 +640,12 @@ export interface RoutingInfo {
|
||||
nat4: RouteCapacity
|
||||
nat4_port_range: NAT4PortRange
|
||||
ipv4: RouteCapacity
|
||||
lan_dhcp: RouteCapacity
|
||||
ipv6: RouteCapacity
|
||||
host_public_ipv4?: PublicIPv4Info
|
||||
public_ipv4_addresses: PublicIPv4Info[]
|
||||
ipv4_assignments: IPv4Route[]
|
||||
lan_dhcp_assignments: LANDHCPRoute[]
|
||||
nat4_mappings: NAT4Route[]
|
||||
ipv6_assignments: IPv6Route[]
|
||||
ipv6_prefixes: IPv6PrefixInfo[]
|
||||
|
||||
Reference in New Issue
Block a user