blob: 6f17a0892b0a76ae4f4d7effb0a5b4fc040212d7 [file]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
// Shared, module-level cache of the effective (zone-override-or-global) value of the
// multi.network.nic.enabled ConfigKey, keyed by zoneid. Reused by any view that needs to
// gate multi-VLAN trunk NIC UI on whether the feature is actually usable in a given zone
// (e.g. the host list's VLAN-filtering badge, the deploy wizard's associate-network control).
import { reactive } from 'vue'
import { getAPI } from '@/api'
export const multiNetworkNicEnabledZones = reactive({})
// in-flight requests only, keyed by zoneid - dedupes simultaneous callers (e.g. many host rows
// in the same zone on one page) without skipping a fresh re-check on the next page visit
const pendingRequests = {}
export function isMultiNetworkNicEnabledForZone (zoneid) {
return !!multiNetworkNicEnabledZones[zoneid]
}
export function fetchMultiNetworkNicEnabledForZone (zoneid) {
if (!zoneid || pendingRequests[zoneid]) {
return
}
pendingRequests[zoneid] = getAPI('listConfigurations', { name: 'multi.network.nic.enabled', zoneid }).then(json => {
const config = json?.listconfigurationsresponse?.configuration?.[0]
multiNetworkNicEnabledZones[zoneid] = !!(config && config.value === 'true')
}).catch(() => {}).finally(() => {
delete pendingRequests[zoneid]
})
}
// Flattens a nic's selectable IP targets for rule-creation pickers (Static NAT / Port Forwarding / Load
// Balancing), distinguishing three kinds that are otherwise easy to conflate in the UI:
// - primary: the nic's own network (nic.networkid / nic.ipaddress)
// - secondary: another IP on that SAME network (nic.secondaryip[], pre-existing nic_secondary_ips feature)
// - associated: an IP on a DIFFERENT network this nic trunks to (nic.associatednetworks[], nic_network_map)
// Each option carries the networkid a rule-creation call (enableStaticNat/createPortForwardingRule/
// assignToLoadBalancerRule) must submit alongside its ip - an associated option's networkid differs from
// the nic's own, which is the one real behavioral difference from a secondary IP.
//
// targetNetworkId, when given (the rule's own network - e.g. the public IP's network), excludes the
// primary/secondary options when the nic's primary network isn't that one: a trunk nic listed as a
// candidate only because one of its *associated* networks matches must not also offer its unrelated
// primary IP, which is never valid for this rule.
export function buildNicIpOptions (nic, targetNetworkId) {
const options = []
const primaryMatches = !targetNetworkId || nic.networkid === targetNetworkId
if (nic.ipaddress && primaryMatches) {
options.push({ kind: 'primary', ip: nic.ipaddress, networkid: nic.networkid, networkname: nic.networkname, nicid: nic.id })
}
if (primaryMatches) {
for (const secondary of (nic.secondaryip || [])) {
options.push({ kind: 'secondary', ip: secondary.ipaddress, networkid: nic.networkid, networkname: nic.networkname, nicid: nic.id })
}
}
for (const association of (nic.associatednetworks || [])) {
if (targetNetworkId && association.networkid !== targetNetworkId) {
continue
}
options.push({ kind: 'associated', ip: association.ipaddress, networkid: association.networkid, networkname: association.networkname, nicid: nic.id })
}
return options
}
// Groups buildNicIpOptions() output into labeled sections for a picker (e.g. <a-select-opt-group>),
// in a fixed, meaningful order - primary first, then secondary, then associated - omitting any section
// that has nothing to show (the common legacy case has only a primary section).
export function groupNicIpOptionsByKind (options, translate) {
return [
{ kind: 'primary', label: translate('label.primary') },
{ kind: 'secondary', label: translate('label.secondaryips') },
{ kind: 'associated', label: translate('label.associatednetworks') }
]
.map(({ kind, label }) => ({ kind, label, items: options.filter(item => item.kind === kind) }))
.filter(group => group.items.length > 0)
}
// Resolves the networkid a rule-creation call (enableStaticNat/createPortForwardingRule/
// assignToLoadBalancerRule) must submit for a chosen ip, falling back to fallbackNetworkId when ip
// doesn't match any option (e.g. options haven't loaded yet) - keeps existing callers' behavior for
// every pre-existing case, only changing the outcome for an associated-network selection.
export function resolveNetworkIdForIp (options, ip, fallbackNetworkId) {
const match = (options || []).find(item => item.ip === ip)
return match ? match.networkid : fallbackNetworkId
}