285 lines
7.6 KiB
Go
285 lines
7.6 KiB
Go
package app
|
|
|
|
import (
|
|
"sort"
|
|
"strings"
|
|
)
|
|
|
|
func normalizeTransportIfaceID(raw string) string {
|
|
id := sanitizeID(raw)
|
|
if strings.TrimSpace(id) == "" {
|
|
return transportDefaultIfaceID
|
|
}
|
|
return id
|
|
}
|
|
|
|
func normalizeTransportInterfaceMode(raw TransportInterfaceMode, ifaceID string) TransportInterfaceMode {
|
|
mode := TransportInterfaceMode(strings.ToLower(strings.TrimSpace(string(raw))))
|
|
switch mode {
|
|
case TransportInterfaceModeShared, TransportInterfaceModeDedicated:
|
|
return mode
|
|
default:
|
|
if normalizeTransportIfaceID(ifaceID) == transportDefaultIfaceID {
|
|
return TransportInterfaceModeShared
|
|
}
|
|
return TransportInterfaceModeDedicated
|
|
}
|
|
}
|
|
|
|
func defaultTransportInterfaceName(ifaceID string) string {
|
|
if normalizeTransportIfaceID(ifaceID) == transportDefaultIfaceID {
|
|
return "Shared interface"
|
|
}
|
|
return ifaceID
|
|
}
|
|
|
|
func transportInterfaceRoutingTableHint(iface TransportInterface) string {
|
|
if strings.TrimSpace(iface.RoutingTable) != "" {
|
|
return strings.TrimSpace(iface.RoutingTable)
|
|
}
|
|
if iface.Config == nil {
|
|
return ""
|
|
}
|
|
if v := strings.TrimSpace(transportConfigString(iface.Config, "routing_table")); v != "" {
|
|
return v
|
|
}
|
|
if v := strings.TrimSpace(transportConfigString(iface.Config, "table")); v != "" {
|
|
return v
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func resolveTransportInterfaceRoutingTable(iface TransportInterface, ifaceID string) string {
|
|
hint := transportInterfaceRoutingTableHint(iface)
|
|
if hint != "" {
|
|
return normalizeTransportRoutingTable(hint, transportRoutingTableForIfaceID(ifaceID))
|
|
}
|
|
if normalizeTransportIfaceID(ifaceID) == transportDefaultIfaceID {
|
|
return ""
|
|
}
|
|
return transportRoutingTableForIfaceID(ifaceID)
|
|
}
|
|
|
|
func normalizeTransportInterfacesState(st transportInterfacesState, clients []TransportClient) (transportInterfacesState, bool) {
|
|
changed := false
|
|
st.Version = transportStateVersion
|
|
if st.Items == nil {
|
|
st.Items = nil
|
|
}
|
|
|
|
byID := map[string]int{}
|
|
out := make([]TransportInterface, 0, len(st.Items)+1)
|
|
for _, raw := range st.Items {
|
|
it := raw
|
|
id := normalizeTransportIfaceID(it.ID)
|
|
if id == "" {
|
|
changed = true
|
|
continue
|
|
}
|
|
if it.ID != id {
|
|
it.ID = id
|
|
changed = true
|
|
}
|
|
if strings.TrimSpace(it.Name) == "" {
|
|
it.Name = defaultTransportInterfaceName(id)
|
|
changed = true
|
|
}
|
|
mode := normalizeTransportInterfaceMode(it.Mode, id)
|
|
if it.Mode != mode {
|
|
it.Mode = mode
|
|
changed = true
|
|
}
|
|
wantTable := resolveTransportInterfaceRoutingTable(it, id)
|
|
if strings.TrimSpace(it.RoutingTable) != wantTable {
|
|
it.RoutingTable = wantTable
|
|
changed = true
|
|
}
|
|
if it.Config != nil {
|
|
it.Config = cloneMap(it.Config)
|
|
}
|
|
if idx, ok := byID[id]; ok {
|
|
if preferTransportInterface(it, out[idx]) {
|
|
out[idx] = it
|
|
}
|
|
changed = true
|
|
continue
|
|
}
|
|
byID[id] = len(out)
|
|
out = append(out, it)
|
|
}
|
|
|
|
ensureIface := func(id string) {
|
|
normID := normalizeTransportIfaceID(id)
|
|
if normID == "" {
|
|
return
|
|
}
|
|
if _, ok := byID[normID]; ok {
|
|
return
|
|
}
|
|
it := TransportInterface{
|
|
ID: normID,
|
|
Name: defaultTransportInterfaceName(normID),
|
|
Mode: normalizeTransportInterfaceMode("", normID),
|
|
}
|
|
it.RoutingTable = resolveTransportInterfaceRoutingTable(it, normID)
|
|
out = append(out, it)
|
|
byID[normID] = len(out) - 1
|
|
changed = true
|
|
}
|
|
|
|
ensureIface(transportDefaultIfaceID)
|
|
for _, it := range clients {
|
|
ensureIface(it.IfaceID)
|
|
}
|
|
|
|
sort.Slice(out, func(i, j int) bool { return out[i].ID < out[j].ID })
|
|
st.Items = out
|
|
return st, changed
|
|
}
|
|
|
|
func preferTransportInterface(cand, cur TransportInterface) bool {
|
|
cu := strings.TrimSpace(cand.UpdatedAt)
|
|
ou := strings.TrimSpace(cur.UpdatedAt)
|
|
if cu != ou {
|
|
if cu == "" {
|
|
return false
|
|
}
|
|
if ou == "" {
|
|
return true
|
|
}
|
|
return cu > ou
|
|
}
|
|
return strings.TrimSpace(cand.Name) > strings.TrimSpace(cur.Name)
|
|
}
|
|
|
|
func buildTransportInterfaceItems(ifaces []TransportInterface, clients []TransportClient) []TransportInterfaceItem {
|
|
group := map[string][]TransportClient{}
|
|
for _, it := range clients {
|
|
id := normalizeTransportIfaceID(it.IfaceID)
|
|
group[id] = append(group[id], it)
|
|
}
|
|
|
|
items := make([]TransportInterfaceItem, 0, len(ifaces))
|
|
seen := map[string]struct{}{}
|
|
for _, iface := range ifaces {
|
|
id := normalizeTransportIfaceID(iface.ID)
|
|
if id == "" {
|
|
continue
|
|
}
|
|
seen[id] = struct{}{}
|
|
clientsForIface := append([]TransportClient(nil), group[id]...)
|
|
sort.Slice(clientsForIface, func(i, j int) bool { return clientsForIface[i].ID < clientsForIface[j].ID })
|
|
clientIDs := make([]string, 0, len(clientsForIface))
|
|
upCount := 0
|
|
for _, cl := range clientsForIface {
|
|
clientIDs = append(clientIDs, cl.ID)
|
|
if cl.Status == TransportClientUp {
|
|
upCount++
|
|
}
|
|
}
|
|
items = append(items, TransportInterfaceItem{
|
|
ID: id,
|
|
Name: strings.TrimSpace(iface.Name),
|
|
Mode: normalizeTransportInterfaceMode(iface.Mode, id),
|
|
RuntimeIface: strings.TrimSpace(iface.RuntimeIface),
|
|
NetnsName: strings.TrimSpace(iface.NetnsName),
|
|
RoutingTable: strings.TrimSpace(iface.RoutingTable),
|
|
Config: cloneMap(iface.Config),
|
|
UpdatedAt: strings.TrimSpace(iface.UpdatedAt),
|
|
ClientIDs: clientIDs,
|
|
ClientCount: len(clientIDs),
|
|
UpCount: upCount,
|
|
})
|
|
}
|
|
|
|
for id, clientsForIface := range group {
|
|
if _, ok := seen[id]; ok {
|
|
continue
|
|
}
|
|
sort.Slice(clientsForIface, func(i, j int) bool { return clientsForIface[i].ID < clientsForIface[j].ID })
|
|
clientIDs := make([]string, 0, len(clientsForIface))
|
|
upCount := 0
|
|
routingTable := ""
|
|
for _, cl := range clientsForIface {
|
|
clientIDs = append(clientIDs, cl.ID)
|
|
if cl.Status == TransportClientUp {
|
|
upCount++
|
|
}
|
|
if routingTable == "" && strings.TrimSpace(cl.RoutingTable) != "" {
|
|
routingTable = strings.TrimSpace(cl.RoutingTable)
|
|
}
|
|
}
|
|
if routingTable == "" && id != transportDefaultIfaceID {
|
|
routingTable = transportRoutingTableForIfaceID(id)
|
|
}
|
|
items = append(items, TransportInterfaceItem{
|
|
ID: id,
|
|
Name: defaultTransportInterfaceName(id),
|
|
Mode: normalizeTransportInterfaceMode("", id),
|
|
RoutingTable: routingTable,
|
|
ClientIDs: clientIDs,
|
|
ClientCount: len(clientIDs),
|
|
UpCount: upCount,
|
|
})
|
|
}
|
|
|
|
sort.Slice(items, func(i, j int) bool { return items[i].ID < items[j].ID })
|
|
return items
|
|
}
|
|
|
|
func appendTransportVirtualInterfaceItems(items []TransportInterfaceItem, clients []TransportClient) []TransportInterfaceItem {
|
|
if len(clients) == 0 {
|
|
return items
|
|
}
|
|
byID := map[string]int{}
|
|
for idx, it := range items {
|
|
byID[normalizeTransportIfaceID(it.ID)] = idx
|
|
if id := strings.TrimSpace(sanitizeID(it.ID)); id != "" {
|
|
byID[id] = idx
|
|
}
|
|
}
|
|
for _, client := range clients {
|
|
if !isTransportPolicyVirtualClient(client) {
|
|
continue
|
|
}
|
|
id := strings.TrimSpace(sanitizeID(client.ID))
|
|
if id == "" {
|
|
continue
|
|
}
|
|
item := TransportInterfaceItem{
|
|
ID: id,
|
|
Name: strings.TrimSpace(client.Name),
|
|
Mode: normalizeTransportInterfaceMode("", client.IfaceID),
|
|
RuntimeIface: strings.TrimSpace(client.Iface),
|
|
NetnsName: "",
|
|
RoutingTable: strings.TrimSpace(client.RoutingTable),
|
|
UpdatedAt: strings.TrimSpace(client.UpdatedAt),
|
|
ClientIDs: []string{id},
|
|
ClientCount: 1,
|
|
UpCount: transportInterfaceItemUpCountForStatus(client.Status),
|
|
Config: map[string]any{
|
|
"virtual": true,
|
|
"virtual_owner": id,
|
|
},
|
|
}
|
|
if item.Name == "" {
|
|
item.Name = id
|
|
}
|
|
if idx, ok := byID[id]; ok {
|
|
items[idx] = item
|
|
continue
|
|
}
|
|
items = append(items, item)
|
|
byID[id] = len(items) - 1
|
|
}
|
|
sort.Slice(items, func(i, j int) bool { return items[i].ID < items[j].ID })
|
|
return items
|
|
}
|
|
|
|
func transportInterfaceItemUpCountForStatus(status TransportClientStatus) int {
|
|
if normalizeTransportStatus(status) == TransportClientUp {
|
|
return 1
|
|
}
|
|
return 0
|
|
}
|