From 42e58b4996f2ff4f6eab1cd1086f4dc249c9c3f4 Mon Sep 17 00:00:00 2001 From: wanyaoqi <18528551+wanyaoqi@users.noreply.github.com> Date: Tue, 15 Jul 2025 16:21:49 +0800 Subject: [PATCH] feat(region,host-deployer): ipv6 only guest support (#22897) --- go.mod | 2 +- go.sum | 4 +- pkg/apis/compute/api.go | 4 + pkg/cloudcommon/cmdline/parser.go | 3 + pkg/compute/models/guest_actions.go | 4 + pkg/compute/models/guestnetworks.go | 8 +- pkg/compute/models/guests.go | 8 + pkg/compute/models/networks.go | 80 +++++--- pkg/hostman/guestfs/fsdriver/linux.go | 114 ++++++----- pkg/hostman/guestfs/fsdriver/suse.go | 39 ++-- pkg/hostman/hostinfo/hostbridge/hostbridge.go | 11 +- pkg/hostman/hostinfo/hostinfo.go | 27 ++- pkg/hostman/hostinfo/hostinfohelper.go | 18 +- pkg/hostman/system_service/telegraf.go | 4 +- pkg/mcclient/options/compute/network.go | 19 +- pkg/scheduler/algorithm/predicates/error.go | 1 + .../algorithm/predicates/network_predicate.go | 6 + pkg/util/netutils2/iface.go | 2 + pkg/util/netutils2/netutils.go | 189 ++++++++++++++++++ pkg/util/netutils2/netutils_linux.go | 5 +- pkg/util/ovnutils/ovnutils.go | 12 +- vendor/modules.txt | 2 +- vendor/yunion.io/x/pkg/util/netutils/ipv6.go | 18 ++ vendor/yunion.io/x/pkg/utils/misc.go | 56 +++++- 24 files changed, 503 insertions(+), 133 deletions(-) diff --git a/go.mod b/go.mod index df4d8b5cc1..78767967d7 100644 --- a/go.mod +++ b/go.mod @@ -101,7 +101,7 @@ require ( yunion.io/x/jsonutils v1.0.1-0.20250507052344-1abcf4f443b1 yunion.io/x/log v1.0.1-0.20240305175729-7cf2d6cd5a91 yunion.io/x/ovsdb v0.0.0-20230306173834-f164f413a900 - yunion.io/x/pkg v1.10.4-0.20250712095850-7797c1f3ffa6 + yunion.io/x/pkg v1.10.4-0.20250715023532-99a31ea00303 yunion.io/x/s3cli v0.0.0-20241221171442-1c11599d28e1 yunion.io/x/sqlchemy v1.1.3-0.20250531010554-ce98f840b833 yunion.io/x/structarg v0.0.0-20231017124457-df4d5009457c diff --git a/go.sum b/go.sum index 4f75b8812a..b08333c696 100644 --- a/go.sum +++ b/go.sum @@ -1423,8 +1423,8 @@ yunion.io/x/ovsdb v0.0.0-20230306173834-f164f413a900 h1:Hu/4ERvoWaN6aiFs4h4/yvVB yunion.io/x/ovsdb v0.0.0-20230306173834-f164f413a900/go.mod h1:0vLkNEhlmA64HViPBAnSTUMrx5QP1CLsxXmxDKQ80tc= yunion.io/x/pkg v0.0.0-20190620104149-945c25821dbf/go.mod h1:t6rEGG2sQ4J7DhFxSZVOTjNd0YO/KlfWQyK1W4tog+E= yunion.io/x/pkg v0.0.0-20190628082551-f4033ba2ea30/go.mod h1:t6rEGG2sQ4J7DhFxSZVOTjNd0YO/KlfWQyK1W4tog+E= -yunion.io/x/pkg v1.10.4-0.20250712095850-7797c1f3ffa6 h1:CzEjNUur33HGmDkW7uKw1eU97gwojNsXlB2AMEzItn8= -yunion.io/x/pkg v1.10.4-0.20250712095850-7797c1f3ffa6/go.mod h1:0Bwxqd9MA3ACi119/l02FprY/o9gHahmYC2bsSbnVpM= +yunion.io/x/pkg v1.10.4-0.20250715023532-99a31ea00303 h1:gbAWm7tVNr0mZjLMJa1A6P85tHsuEpDEEnUHz0+mBhw= +yunion.io/x/pkg v1.10.4-0.20250715023532-99a31ea00303/go.mod h1:0Bwxqd9MA3ACi119/l02FprY/o9gHahmYC2bsSbnVpM= yunion.io/x/s3cli v0.0.0-20241221171442-1c11599d28e1 h1:1KJ3YYinydPHpDEQRXdr/T8SYcKZ5Er+m489H+PnaQ4= yunion.io/x/s3cli v0.0.0-20241221171442-1c11599d28e1/go.mod h1:0iFKpOs1y4lbCxeOmq3Xx/0AcQoewVPwj62eRluioEo= yunion.io/x/sqlchemy v1.1.3-0.20250531010554-ce98f840b833 h1:XTFC1naKYkciCQDLm9izpzHXfTenmmtYsTpVKrsN5hE= diff --git a/pkg/apis/compute/api.go b/pkg/apis/compute/api.go index c5b5396115..1a2d5c92c2 100644 --- a/pkg/apis/compute/api.go +++ b/pkg/apis/compute/api.go @@ -90,6 +90,10 @@ type NetworkConfig struct { // required: false RequireIPv6 bool `json:"require_ipv6"` + // 只分配IPv6地址,禁用IPv4 + // required: false + StrictIPv6 bool `json:"strict_ipv6"` + // 驱动方式 // 若指定镜像的网络驱动方式,此参数会被覆盖 Driver string `json:"driver"` diff --git a/pkg/cloudcommon/cmdline/parser.go b/pkg/cloudcommon/cmdline/parser.go index 0c99b41764..e4f252271d 100644 --- a/pkg/cloudcommon/cmdline/parser.go +++ b/pkg/cloudcommon/cmdline/parser.go @@ -336,6 +336,9 @@ func ParseNetworkConfig(desc string, idx int) (*compute.NetworkConfig, error) { netConfig.IsDefault = true } else if p == "ipv6" { netConfig.RequireIPv6 = true + } else if p == "strict-ipv6" { + netConfig.RequireIPv6 = true + netConfig.StrictIPv6 = true } else if strings.HasPrefix(p, "standby-port=") { netConfig.StandbyPortCount, _ = strconv.Atoi(p[len("standby-port="):]) } else if strings.HasPrefix(p, "standby-addr=") { diff --git a/pkg/compute/models/guest_actions.go b/pkg/compute/models/guest_actions.go index 2d2c7d6e48..cbdc9f4082 100644 --- a/pkg/compute/models/guest_actions.go +++ b/pkg/compute/models/guest_actions.go @@ -2630,6 +2630,10 @@ func (self *SGuest) PerformChangeIpaddr( if err != nil { return nil, httperrors.NewInputParameterError("parseNetworkInfo fail: %s", err) } + if len(gn.IpAddr) == 0 && len(conf.Address) > 0 { + // strict ipv6 network, can't add ipv4 address + return nil, httperrors.NewBadRequestError("guest network has no ipv4 address") + } reuseV4 := "" if conf.Address == gn.IpAddr { // 允许IPv4地址不变,只改IPv6地址 diff --git a/pkg/compute/models/guestnetworks.go b/pkg/compute/models/guestnetworks.go index 7c750d4b68..07d412c013 100644 --- a/pkg/compute/models/guestnetworks.go +++ b/pkg/compute/models/guestnetworks.go @@ -253,6 +253,7 @@ type newGuestNetworkArgs struct { ip6Addr string requireIPv6 bool + strictIPv6 bool // 是否为缺省路由 isDefault bool @@ -298,6 +299,7 @@ func (manager *SGuestnetworkManager) newGuestNetwork( address6 = args.ip6Addr requireIPv6 = args.requireIPv6 + strictIPv6 = args.strictIPv6 ) gn.GuestId = guest.Id @@ -325,7 +327,7 @@ func (manager *SGuestnetworkManager) newGuestNetwork( } provider := vpc.GetProviderName() - if !virtual { + if !virtual && !strictIPv6 && network.IsSupportIPv4() { if len(address) > 0 && reUseAddr { ipAddr, err := netutils.NewIPV4Addr(address) if err != nil { @@ -386,7 +388,7 @@ func (manager *SGuestnetworkManager) newGuestNetwork( // assign ipv6 address if !virtual { - if len(address6) > 0 || requireIPv6 { + if len(address6) > 0 || (requireIPv6 || len(gn.IpAddr) == 0) { if provider == api.CLOUD_PROVIDER_ONECLOUD || options.Options.EnablePreAllocateIpAddr || (!options.Options.EnablePreAllocateIpAddr && len(address6) > 0) { addrTable := network.GetUsedAddresses6(ctx) recentAddrTable := manager.getRecentlyReleasedIPAddresses6(network.Id, network.getAllocTimoutDuration()) @@ -1081,7 +1083,7 @@ func (gn *SGuestnetwork) getMtu(net *SNetwork) int16 { func (gn *SGuestnetwork) IsAllocated() bool { region, _ := gn.GetGuest().getRegion() provider := region.Provider - if regutils.MatchMacAddr(gn.MacAddr) && (gn.Virtual || regutils.MatchIP4Addr(gn.IpAddr) || (provider != api.CLOUD_PROVIDER_ONECLOUD && !options.Options.EnablePreAllocateIpAddr)) { + if regutils.MatchMacAddr(gn.MacAddr) && (gn.Virtual || regutils.MatchIP4Addr(gn.IpAddr) || regutils.MatchIP6Addr(gn.Ip6Addr) || (provider != api.CLOUD_PROVIDER_ONECLOUD && !options.Options.EnablePreAllocateIpAddr)) { return true } return false diff --git a/pkg/compute/models/guests.go b/pkg/compute/models/guests.go index f285fd81f9..136c277b40 100644 --- a/pkg/compute/models/guests.go +++ b/pkg/compute/models/guests.go @@ -3506,6 +3506,7 @@ type Attach2NetworkArgs struct { RequireDesignatedIP bool UseDesignatedIP bool RequireIPv6 bool + StrictIPv6 bool BwLimit int NicDriver string @@ -3536,6 +3537,7 @@ func (args *Attach2NetworkArgs) onceArgs(i int) attach2NetworkOnceArgs { requireDesignatedIP: args.RequireDesignatedIP, useDesignatedIP: args.UseDesignatedIP, requireIPv6: args.RequireIPv6, + strictIPv6: args.StrictIPv6, bwLimit: args.BwLimit, nicDriver: args.NicDriver, @@ -3578,6 +3580,7 @@ type attach2NetworkOnceArgs struct { requireDesignatedIP bool useDesignatedIP bool requireIPv6 bool + strictIPv6 bool bwLimit int nicDriver string @@ -3656,6 +3659,7 @@ func (self *SGuest) attach2NetworkOnce( requireDesignatedIP: args.requireDesignatedIP, useDesignatedIP: args.useDesignatedIP, requireIPv6: args.requireIPv6, + strictIPv6: args.strictIPv6, ifname: args.nicConf.Ifname, macAddr: args.nicConf.Mac, @@ -4429,6 +4433,7 @@ func (self *SGuest) attach2NamedNetworkDesc(ctx context.Context, userCred mcclie IpAddr: netConfig.Address, Ip6Addr: netConfig.Address6, RequireIPv6: netConfig.RequireIPv6, + StrictIPv6: netConfig.StrictIPv6, NicDriver: netConfig.Driver, NumQueues: netConfig.NumQueues, BwLimit: netConfig.BwLimit, @@ -6576,6 +6581,9 @@ func (self *SGuest) ToNetworksConfig() []*api.NetworkConfig { netConf.Exit = guestNetwork.IsExit() if len(guestNetwork.Ip6Addr) > 0 { netConf.RequireIPv6 = true + if len(guestNetwork.IpAddr) == 0 { + netConf.StrictIPv6 = true + } } // netConf.Private // netConf.Reserved diff --git a/pkg/compute/models/networks.go b/pkg/compute/models/networks.go index 4a680b6801..d96bbe1c72 100644 --- a/pkg/compute/models/networks.go +++ b/pkg/compute/models/networks.go @@ -18,6 +18,8 @@ import ( "context" "database/sql" "fmt" + "math" + "math/big" "sort" "strconv" "strings" @@ -94,11 +96,11 @@ type SNetwork struct { IfnameHint string `width:"9" charset:"ascii" nullable:"true" list:"user" create:"optional"` // 起始IP地址 - GuestIpStart string `width:"16" charset:"ascii" nullable:"false" list:"user" update:"user" create:"required"` + GuestIpStart string `width:"16" charset:"ascii" nullable:"false" list:"user" update:"user" create:"optional"` // 结束IP地址 - GuestIpEnd string `width:"16" charset:"ascii" nullable:"false" list:"user" update:"user" create:"required"` + GuestIpEnd string `width:"16" charset:"ascii" nullable:"false" list:"user" update:"user" create:"optional"` // 掩码 - GuestIpMask int8 `nullable:"false" list:"user" update:"user" create:"required"` + GuestIpMask int8 `nullable:"false" list:"user" update:"user" create:"optional"` // 网关地址 GuestGateway string `width:"16" charset:"ascii" nullable:"true" list:"user" update:"user" create:"optional"` // DNS, allow multiple dns, seperated by "," @@ -618,6 +620,9 @@ func (snet *SNetwork) GetGuestIpv4StartAddress() netutils.IPV4Addr { } func (snet *SNetwork) IsExitNetwork() bool { + if len(snet.GuestIpStart) == 0 { + return false + } return len(snet.ExternalId) == 0 && netutils.IsExitAddress(snet.GetGuestIpv4StartAddress()) } @@ -861,7 +866,11 @@ func (manager *SNetworkManager) newFromCloudNetwork(ctx context.Context, userCre } func (net *SNetwork) IsAddressInRange(address netutils.IPV4Addr) bool { - return net.getIPRange().Contains(address) + ipRange := net.getIPRange() + if ipRange == nil { + return false + } + return ipRange.Contains(address) } func (net *SNetwork) IsAddress6InRange(address netutils.IPV6Addr) bool { @@ -1012,7 +1021,7 @@ func (manager *SNetworkManager) TotalPortCount( stat, _ = ret[net.ServerType] } allStat, _ = ret[""] - count := net.getIPRange().AddressCount() + count := net.GetTotalAddressCount() if net.IsExitNetwork() { if len(net.ServerType) > 0 { stat.CountExt += count @@ -1070,7 +1079,19 @@ func (snet *SNetwork) GetFreeAddressCount() (int, error) { } func (snet *SNetwork) GetTotalAddressCount() int { - return snet.getIPRange().AddressCount() + ipRange := snet.getIPRange() + if ipRange != nil { + return ipRange.AddressCount() + } + ip6Range := snet.getIPRange6() + if ip6Range == nil { + return 0 + } + cnt := ip6Range.AddressCount() + if cnt.Cmp(big.NewInt(math.MaxInt32)) > 0 { + return math.MaxInt32 + } + return int(cnt.Int64()) } func (snet *SNetwork) getFreeAddressCount() (int, error) { @@ -1082,19 +1103,7 @@ func (snet *SNetwork) getFreeAddressCount() (int, error) { if nics, ok := vnics[snet.Id]; ok { used = nics.Total } - return snet.getIPRange().AddressCount() - used, nil -} - -func (snet *SNetwork) getFreeAddress6Count() (int, error) { - vnics, err := NetworkManager.TotalNicCount([]string{snet.Id}) - if err != nil { - return -1, errors.Wrapf(err, "TotalNicCount") - } - used := 0 - if nics, ok := vnics[snet.Id]; ok { - used = nics.Total - } - return snet.getIPRange().AddressCount() - used, nil + return snet.GetTotalAddressCount() - used, nil } func isValidNetworkInfo(ctx context.Context, userCred mcclient.TokenCredential, netConfig *api.NetworkConfig, reuseAddr string) error { @@ -1280,10 +1289,6 @@ func IsExitNetworkInfo(ctx context.Context, userCred mcclient.TokenCredential, n return false } -func (snet *SNetwork) GetPorts() int { - return snet.getIPRange().AddressCount() -} - func (manager *SNetworkManager) FetchCustomizeColumns( ctx context.Context, userCred mcclient.TokenCredential, @@ -1311,7 +1316,7 @@ func (manager *SNetworkManager) FetchCustomizeColumns( if network.IsClassic() { rows[i].IsClassic = true } - rows[i].Ports = network.GetPorts() + rows[i].Ports = network.GetTotalAddressCount() rows[i].Routes = network.GetRoutes() rows[i].Schedtags = GetSchedtagsDetailsToResourceV2(network, ctx) rows[i].Dns = network.GetDNS(rows[i].Zone) @@ -1874,7 +1879,7 @@ func (manager *SNetworkManager) ValidateCreateData(ctx context.Context, userCred } var ( - ipRange netutils.IPV4AddrRange + ipRange *netutils.IPV4AddrRange masklen int8 netAddr netutils.IPV4Addr ) @@ -1883,7 +1888,8 @@ func (manager *SNetworkManager) ValidateCreateData(ctx context.Context, userCred if err != nil { return input, httperrors.NewInputParameterError("ip_prefix error: %s", err) } - ipRange = prefix.ToIPRange() + ipRangeTmp := prefix.ToIPRange() + ipRange = &ipRangeTmp masklen = prefix.MaskLen netAddr = prefix.Address.NetAddr(masklen) input.GuestIpMask = prefix.MaskLen @@ -1928,7 +1934,8 @@ func (manager *SNetworkManager) ValidateCreateData(ctx context.Context, userCred if err != nil { return input, httperrors.NewInputParameterError("invalid end ip: %s %s", input.GuestIpEnd, err) } - ipRange = netutils.NewIPV4AddrRange(ipStart, ipEnd) + ipRangeTpm := netutils.NewIPV4AddrRange(ipStart, ipEnd) + ipRange = &ipRangeTpm masklen = input.GuestIpMask netAddr = ipStart.NetAddr(masklen) if ipEnd.NetAddr(masklen) != netAddr { @@ -1995,6 +2002,10 @@ func (manager *SNetworkManager) ValidateCreateData(ctx context.Context, userCred } } + if ipRange == nil && ipRange6 == nil { + return input, httperrors.NewBadRequestError("No valid ipv4/ipv6 address input") + } + // do not set default dns // if len(input.GuestDns) == 0 { // input.GuestDns = options.Options.DNSServer @@ -2048,9 +2059,10 @@ func (manager *SNetworkManager) ValidateCreateData(ctx context.Context, userCred } { - if !vpc.containsIPV4Range(ipRange) { + if ipRange != nil && !vpc.containsIPV4Range(*ipRange) { return input, httperrors.NewInputParameterError("Network not in range of VPC cidrblock %s", vpc.CidrBlock) } + if ipRange6 != nil { if !vpc.containsIPV6Range(*ipRange6) { return input, httperrors.NewInputParameterError("Network not in range of VPC ipv6 cidrblock %s", vpc.CidrBlock6) @@ -2064,7 +2076,7 @@ func (manager *SNetworkManager) ValidateCreateData(ctx context.Context, userCred if err != nil { return input, httperrors.NewInternalServerError("fail to GetNetworks of vpc: %v", err) } - if isOverlapNetworks(nets, ipRange) { + if ipRange != nil && isOverlapNetworks(nets, *ipRange) { return input, httperrors.NewInputParameterError("Conflict address space with existing networks in vpc %q", vpc.GetName()) } if ipRange6 != nil && isOverlapNetworks6(nets, *ipRange6) { @@ -2072,8 +2084,10 @@ func (manager *SNetworkManager) ValidateCreateData(ctx context.Context, userCred } } - input.GuestIpStart = ipRange.StartIp().String() - input.GuestIpEnd = ipRange.EndIp().String() + if ipRange != nil { + input.GuestIpStart = ipRange.StartIp().String() + input.GuestIpEnd = ipRange.EndIp().String() + } if ipRange6 != nil { // validate v6 ip addr @@ -3912,6 +3926,10 @@ func (net *SNetwork) PerformSyncAdditionalWires( return nil, nil } +func (net *SNetwork) IsSupportIPv4() bool { + return len(net.GuestIpStart) > 0 && len(net.GuestIpEnd) > 0 +} + func (net *SNetwork) IsSupportIPv6() bool { return len(net.GuestIp6Start) > 0 && len(net.GuestIp6End) > 0 } diff --git a/pkg/hostman/guestfs/fsdriver/linux.go b/pkg/hostman/guestfs/fsdriver/linux.go index f583ca43d4..d36c25bf57 100644 --- a/pkg/hostman/guestfs/fsdriver/linux.go +++ b/pkg/hostman/guestfs/fsdriver/linux.go @@ -965,24 +965,26 @@ func (d *sDebianLikeRootFs) DeployNetworkingScripts(rootFs IDiskPartition, nics cmds.WriteString("\n") } else if nicDesc.Manual { ifname := nicDesc.Name - cmds.WriteString(fmt.Sprintf("iface %s inet static\n", nicDesc.Name)) - if nicDesc.VlanInterface { - cmds.WriteString("\n") - ifname = fmt.Sprintf("%s.%d", nicDesc.Name, nicDesc.Vlan) - cmds.WriteString(fmt.Sprintf("auto %s\n", ifname)) - cmds.WriteString(fmt.Sprintf("iface %s inet static\n", ifname)) + if len(nicDesc.Ip) > 0 { + cmds.WriteString(fmt.Sprintf("iface %s inet static\n", nicDesc.Name)) + if nicDesc.VlanInterface { + cmds.WriteString("\n") + ifname = fmt.Sprintf("%s.%d", nicDesc.Name, nicDesc.Vlan) + cmds.WriteString(fmt.Sprintf("auto %s\n", ifname)) + cmds.WriteString(fmt.Sprintf("iface %s inet static\n", ifname)) + } + netmask := netutils2.Netlen2Mask(int(nicDesc.Masklen)) + cmds.WriteString(fmt.Sprintf(" address %s\n", nicDesc.Ip)) + cmds.WriteString(fmt.Sprintf(" netmask %s\n", netmask)) + cmds.WriteString(fmt.Sprintf(" hwaddress ether %s\n", nicDesc.Mac)) + if len(nicDesc.Gateway) > 0 && nicDesc.Ip == mainIp { + cmds.WriteString(fmt.Sprintf(" gateway %s\n", nicDesc.Gateway)) + } + if nicDesc.Mtu > 0 { + cmds.WriteString(fmt.Sprintf(" mtu %d\n", nicDesc.Mtu)) + } } - netmask := netutils2.Netlen2Mask(int(nicDesc.Masklen)) - cmds.WriteString(fmt.Sprintf(" address %s\n", nicDesc.Ip)) - cmds.WriteString(fmt.Sprintf(" netmask %s\n", netmask)) - cmds.WriteString(fmt.Sprintf(" hwaddress ether %s\n", nicDesc.Mac)) - if len(nicDesc.Gateway) > 0 && nicDesc.Ip == mainIp { - cmds.WriteString(fmt.Sprintf(" gateway %s\n", nicDesc.Gateway)) - } - if nicDesc.Mtu > 0 { - cmds.WriteString(fmt.Sprintf(" mtu %d\n", nicDesc.Mtu)) - } routes4 := make([]netutils2.SRouteInfo, 0) routes6 := make([]netutils2.SRouteInfo, 0) routes4, routes6 = netutils2.AddNicRoutes(routes4, routes6, nicDesc, mainIp, mainIp6, nicCnt) @@ -1002,11 +1004,24 @@ func (d *sDebianLikeRootFs) DeployNetworkingScripts(rootFs IDiskPartition, nics cmds.WriteString(fmt.Sprintf(" dns-search %s\n", nicDesc.Domain)) domains = append(domains, nicDesc.Domain) } + if nicDesc.Mtu > 0 { + cmds.WriteString(fmt.Sprintf(" mtu %d\n", nicDesc.Mtu)) + } + dnslist := netutils2.GetNicDns(nicDesc) + if len(dnslist) > 0 { + cmds.WriteString(fmt.Sprintf(" dns-nameservers %s\n", strings.Join(dnslist, " "))) + dnss = append(dnss, dnslist...) + if len(nicDesc.Domain) > 0 { + cmds.WriteString(fmt.Sprintf(" dns-search %s\n", nicDesc.Domain)) + domains = append(domains, nicDesc.Domain) + } + } + if len(nicDesc.TeamingSlaves) > 0 { + cmds.WriteString(getNicTeamingConfigCmds(nicDesc.TeamingSlaves)) + } + cmds.WriteString("\n") } - if len(nicDesc.TeamingSlaves) > 0 { - cmds.WriteString(getNicTeamingConfigCmds(nicDesc.TeamingSlaves)) - } - cmds.WriteString("\n") + if len(nicDesc.Ip6) > 0 { cmds.WriteString(fmt.Sprintf("iface %s inet6 static\n", ifname)) cmds.WriteString(fmt.Sprintf(" address %s\n", nicDesc.Ip6)) @@ -1017,11 +1032,14 @@ func (d *sDebianLikeRootFs) DeployNetworkingScripts(rootFs IDiskPartition, nics cmds.WriteString("\n") } } else { - cmds.WriteString(fmt.Sprintf("iface %s inet dhcp\n", nicDesc.Name)) - if len(nicDesc.TeamingSlaves) > 0 { - cmds.WriteString(getNicTeamingConfigCmds(nicDesc.TeamingSlaves)) + if len(nicDesc.Ip) > 0 { + cmds.WriteString(fmt.Sprintf("iface %s inet dhcp\n", nicDesc.Name)) + if len(nicDesc.TeamingSlaves) > 0 { + cmds.WriteString(getNicTeamingConfigCmds(nicDesc.TeamingSlaves)) + } + cmds.WriteString("\n") } - cmds.WriteString("\n") + if len(nicDesc.Ip6) > 0 { // ipv6 support static temporarily // TODO @@ -1470,13 +1488,15 @@ func (r *sRedhatLikeRootFs) deployNetworkingScripts(rootFs IDiskPartition, nics return errors.Wrap(err, "deployVlanNetworkingScripts") } } else { - netmask := netutils2.Netlen2Mask(int(nicDesc.Masklen)) - cmds.WriteString("NETMASK=") - cmds.WriteString(netmask) - cmds.WriteString("\n") - cmds.WriteString("IPADDR=") - cmds.WriteString(nicDesc.Ip) - cmds.WriteString("\n") + if len(nicDesc.Ip) > 0 { + netmask := netutils2.Netlen2Mask(int(nicDesc.Masklen)) + cmds.WriteString("NETMASK=") + cmds.WriteString(netmask) + cmds.WriteString("\n") + cmds.WriteString("IPADDR=") + cmds.WriteString(nicDesc.Ip) + cmds.WriteString("\n") + } if len(nicDesc.Gateway) > 0 && nicDesc.Ip == mainIp { cmds.WriteString("GATEWAY=") cmds.WriteString(nicDesc.Gateway) @@ -1500,17 +1520,18 @@ func (r *sRedhatLikeRootFs) deployNetworkingScripts(rootFs IDiskPartition, nics if err := rootFs.FilePutContents(fn, rtblStr, false, false); err != nil { return err } - } - dnslist := netutils2.GetNicDns(nicDesc) - if len(dnslist) > 0 { - cmds.WriteString("PEERDNS=yes\n") - for i := 0; i < len(dnslist); i++ { - cmds.WriteString(fmt.Sprintf("DNS%d=%s\n", i+1, dnslist[i])) - } - if len(nicDesc.Domain) > 0 { - cmds.WriteString(fmt.Sprintf("DOMAIN=%s\n", nicDesc.Domain)) + dnslist := netutils2.GetNicDns(nicDesc) + if len(dnslist) > 0 { + cmds.WriteString("PEERDNS=yes\n") + for i := 0; i < len(dnslist); i++ { + cmds.WriteString(fmt.Sprintf("DNS%d=%s\n", i+1, dnslist[i])) + } + if len(nicDesc.Domain) > 0 { + cmds.WriteString(fmt.Sprintf("DOMAIN=%s\n", nicDesc.Domain)) + } } } + if len(nicDesc.Ip6) > 0 { cmds.WriteString("IPV6INIT=yes\n") cmds.WriteString("DHCPV6C=no\n") @@ -1522,17 +1543,20 @@ func (r *sRedhatLikeRootFs) deployNetworkingScripts(rootFs IDiskPartition, nics } } } else { - cmds.WriteString("BOOTPROTO=dhcp\n") + if len(nicDesc.Ip) > 0 { + cmds.WriteString("BOOTPROTO=dhcp\n") + } + if len(nicDesc.Ip6) > 0 { // IPv6 support static temporarily // TODO cmds.WriteString("IPV6INIT=yes\n") cmds.WriteString("DHCPV6C=yes\n") cmds.WriteString("IPV6_AUTOCONF=yes\n") - // cmds.WriteString(fmt.Sprintf("IPV6ADDR=%s/%d\n", nicDesc.Ip6, nicDesc.Masklen6)) - // if len(nicDesc.Gateway6) > 0 { - // cmds.WriteString("IPV6_DEFROUTE=yes\n", nicDesc.Gateway6)) - // } + cmds.WriteString(fmt.Sprintf("IPV6ADDR=%s/%d\n", nicDesc.Ip6, nicDesc.Masklen6)) + if len(nicDesc.Gateway6) > 0 { + cmds.WriteString("IPV6_DEFROUTE=yes\n") + } } } var fn = fmt.Sprintf("%s/ifcfg-%s", scriptPath, nicDesc.Name) diff --git a/pkg/hostman/guestfs/fsdriver/suse.go b/pkg/hostman/guestfs/fsdriver/suse.go index f799faf79e..f50198d6d3 100644 --- a/pkg/hostman/guestfs/fsdriver/suse.go +++ b/pkg/hostman/guestfs/fsdriver/suse.go @@ -225,16 +225,23 @@ func (r *sSuseLikeRootFs) deployNetworkingScripts(rootFs IDiskPartition, nics [] cmds.WriteString(netutils2.PSEUDO_VIP) cmds.WriteString("\n") } else if nicDesc.Manual { - cmds.WriteString("STARTMODE=auto\n") - cmds.WriteString("BOOTPROTO=static\n") - cmds.WriteString(fmt.Sprintf("IPADDR=%s/%d\n", nicDesc.Ip, nicDesc.Masklen)) + if nicDesc.VlanInterface { + if err := r.deployVlanNetworkingScripts(rootFs, "", mainIp, mainIp6, nicCnt, nicDesc); err != nil { + return err + } + } else { - if len(nicDesc.Ip6) > 0 { - cmds.WriteString("IPV6INIT=yes\n") - cmds.WriteString("IPV6_AUTOCONF=no\n") - cmds.WriteString(fmt.Sprintf("IPADDR_V6=%s/%d\n", nicDesc.Ip6, nicDesc.Masklen6)) + cmds.WriteString("STARTMODE=auto\n") + cmds.WriteString("BOOTPROTO=static\n") + if len(nicDesc.Ip) > 0 { + cmds.WriteString(fmt.Sprintf("IPADDR=%s/%d\n", nicDesc.Ip, nicDesc.Masklen)) + } + if len(nicDesc.Ip6) > 0 { + cmds.WriteString("IPV6INIT=yes\n") + cmds.WriteString("IPV6_AUTOCONF=no\n") + cmds.WriteString(fmt.Sprintf("IPADDR_V6=%s/%d\n", nicDesc.Ip6, nicDesc.Masklen6)) + } } - var routes4 = make([]netutils2.SRouteInfo, 0) var routes6 = make([]netutils2.SRouteInfo, 0) routes4, routes6 = netutils2.AddNicRoutes(routes4, routes6, nicDesc, mainIp, mainIp6, nicCnt) @@ -279,17 +286,21 @@ func (r *sSuseLikeRootFs) deployNetworkingScripts(rootFs IDiskPartition, nics [] if err := rootFs.FilePutContents(fn, rtblStr, false, false); err != nil { return err } - } - dnslist := netutils2.GetNicDns(nicDesc) - for i := 0; i < len(dnslist); i++ { - if !utils.IsInArray(dnslist[i], dnsSrv) { - dnsSrv = append(dnsSrv, dnslist[i]) + dnslist := netutils2.GetNicDns(nicDesc) + for i := 0; i < len(dnslist); i++ { + if !utils.IsInArray(dnslist[i], dnsSrv) { + dnsSrv = append(dnsSrv, dnslist[i]) + } } } + } else { cmds.WriteString("STARTMODE=auto\n") - cmds.WriteString("BOOTPROTO=dhcp4\n") + if len(nicDesc.Ip) > 0 { + cmds.WriteString("BOOTPROTO=dhcp4\n") + } + if len(nicDesc.Ip6) > 0 { // IPv6 support static temporarily // TODO diff --git a/pkg/hostman/hostinfo/hostbridge/hostbridge.go b/pkg/hostman/hostinfo/hostbridge/hostbridge.go index 0b0f007f84..467fdb7c91 100644 --- a/pkg/hostman/hostinfo/hostbridge/hostbridge.go +++ b/pkg/hostman/hostinfo/hostbridge/hostbridge.go @@ -342,7 +342,7 @@ func (d *SBaseBridgeDriver) ConfirmToConfig() (bool, string, error) { if d.bridge.Addr != d.ip { return false, "", fmt.Errorf("bridge %s IP %s is not expected IP %s, mismatch", d.bridge, d.bridge.Addr, d.ip) } - } else { + } else if len(d.ip6) == 0 { if d.inter != nil && len(d.inter.Addr) > 0 { return false, "", fmt.Errorf("interface %s should have no address", d.inter) } @@ -429,14 +429,15 @@ func (d *SBaseBridgeDriver) SetupAddresses() error { addr string masklen int ) - if len(d.ip) == 0 { + if len(d.ip) == 0 && len(d.ip6) == 0 { addr, masklen = netutils2.GetSecretInterfaceAddress() } else { addr = d.ip masklen = d.maskLen } - addrStr := []string{ - fmt.Sprintf("%s/%d", addr, masklen), + addrStr := []string{} + if len(d.ip) > 0 { + addrStr = append(addrStr, fmt.Sprintf("%s/%d", addr, masklen)) } if len(d.ip6) > 0 { addrStr = append(addrStr, fmt.Sprintf("%s/%d", d.ip6, d.mask6Len)) @@ -553,7 +554,7 @@ func addr2Prefix(addrStr string, maskLen int) string { func (d *SBaseBridgeDriver) Setup(o IBridgeDriver) error { var routes []iproute2.RouteSpec var slaveAddrs []netutils2.SNicAddress - if d.inter != nil && len(d.inter.Addr) > 0 { + if d.inter != nil && (len(d.inter.Addr) > 0 || len(d.inter.Addr6) > 0) { routes = d.inter.GetRouteSpecs() slaveAddrs = d.inter.GetSlaveAddresses() log.Infof("to migrate routes: %s slaveAddress: %s", jsonutils.Marshal(routes), jsonutils.Marshal(slaveAddrs)) diff --git a/pkg/hostman/hostinfo/hostinfo.go b/pkg/hostman/hostinfo/hostinfo.go index 3b331c5160..0f76603ef0 100644 --- a/pkg/hostman/hostinfo/hostinfo.go +++ b/pkg/hostman/hostinfo/hostinfo.go @@ -394,14 +394,14 @@ func (h *SHostInfo) parseConfig() error { if len(options.HostOptions.ListenInterface) > 0 { h.MasterNic = netutils2.NewNetInterface(options.HostOptions.ListenInterface) - if len(h.MasterNic.Addr) == 0 { + if len(h.MasterNic.Addr) == 0 && len(h.MasterNic.Addr6) == 0 { return fmt.Errorf("Listen interface %s master have no IP", options.HostOptions.ListenInterface) } } else { // set MasterNic to the first NIC with IP h.MasterNic = nil for _, n := range h.Nics { - if len(n.Ip) > 0 { + if len(n.Ip) > 0 || len(n.Ip6) > 0 { h.MasterNic = netutils2.NewNetInterface(n.Bridge) } } @@ -1135,12 +1135,19 @@ func (h *SHostInfo) detectOvsKOVersion() error { } func (h *SHostInfo) GetMasterNicIpAndMask() (string, int) { - mask, _ := h.MasterNic.Mask.Size() - return h.MasterNic.Addr, mask + if h.MasterNic.Addr != "" { + mask, _ := h.MasterNic.Mask.Size() + return h.MasterNic.Addr, mask + } + mask, _ := h.MasterNic.Mask6.Size() + return h.MasterNic.Addr6, mask } func (h *SHostInfo) GetMasterIp() string { - return h.MasterNic.Addr + if h.MasterNic.Addr != "" { + return h.MasterNic.Addr + } + return h.MasterNic.Addr6 } func (h *SHostInfo) GetMasterMac() string { @@ -1421,12 +1428,12 @@ func (h *SHostInfo) initZoneInfo(zoneId string) error { func (h *SHostInfo) waitMasterNicIp() error { const maxWaitSeconds = 900 waitSeconds := 0 - for h.MasterNic.Addr == "" && waitSeconds < maxWaitSeconds { + for h.MasterNic.Addr == "" && h.MasterNic.Addr6 == "" && waitSeconds < maxWaitSeconds { time.Sleep(time.Second) waitSeconds++ h.MasterNic.FetchConfig() } - if h.MasterNic.Addr == "" { + if h.MasterNic.Addr == "" && h.MasterNic.Addr6 == "" { return errors.Wrap(httperrors.ErrInvalidStatus, "fail to fetch master nic IP address") } if h.MasterNic.GetMac() == "" { @@ -1571,7 +1578,11 @@ func (h *SHostInfo) updateOrCreateHost(hostId string) (*api.HostDetails, error) if options.HostOptions.EnableSsl { schema = "https" } - input.ManagerUri = fmt.Sprintf("%s://%s:%d", schema, masterIp, options.HostOptions.Port) + if strings.Contains(masterIp, ":") { + input.ManagerUri = fmt.Sprintf("%s://[%s]:%d", schema, masterIp, options.HostOptions.Port) + } else { + input.ManagerUri = fmt.Sprintf("%s://%s:%d", schema, masterIp, options.HostOptions.Port) + } input.CpuCount = &h.Cpu.cpuInfoProc.Count nodeCount := int8(h.Cpu.cpuInfoDmi.Nodes) if sysutils.IsHypervisor() { diff --git a/pkg/hostman/hostinfo/hostinfohelper.go b/pkg/hostman/hostinfo/hostinfohelper.go index 643dc89d90..cfe1187b9a 100644 --- a/pkg/hostman/hostinfo/hostinfohelper.go +++ b/pkg/hostman/hostinfo/hostinfohelper.go @@ -266,7 +266,7 @@ func NewNIC(desc string) (*SNIC, error) { nic.Ip6 = data[3] } } else if regutils.MatchIP6Addr(data[2]) { - nic.Ip6 = data[3] + nic.Ip6 = data[2] if len(data) > 3 && regutils.MatchIP4Addr(data[3]) { nic.Ip = data[3] } @@ -366,9 +366,11 @@ func NewNIC(desc string) (*SNIC, error) { } } - nic.dhcpServer, err = hostdhcp.NewGuestDHCPServer(nic.Bridge, options.HostOptions.DhcpServerPort, relayConf) - if err != nil { - return nil, errors.Wrapf(err, "NewGuestDHCPServer(%s, %d, %#v)", nic.Bridge, options.HostOptions.DhcpServerPort, relayConf) + if len(nic.Ip) > 0 { + nic.dhcpServer, err = hostdhcp.NewGuestDHCPServer(nic.Bridge, options.HostOptions.DhcpServerPort, relayConf) + if err != nil { + return nil, errors.Wrapf(err, "NewGuestDHCPServer(%s, %d, %#v)", nic.Bridge, options.HostOptions.DhcpServerPort, relayConf) + } } var relayConf6 *hostdhcp.SDHCPRelayUpstream @@ -382,9 +384,11 @@ func NewNIC(desc string) (*SNIC, error) { } } - nic.dhcpServer6, err = hostdhcp.NewGuestDHCP6Server(nic.Bridge, options.HostOptions.Dhcp6ServerPort, relayConf6) - if err != nil { - return nil, errors.Wrapf(err, "NewGuestDHCP6Server(%s, %d, %#v)", nic.Bridge, options.HostOptions.Dhcp6ServerPort, relayConf6) + if len(nic.Ip6) > 0 { + nic.dhcpServer6, err = hostdhcp.NewGuestDHCP6Server(nic.Bridge, options.HostOptions.Dhcp6ServerPort, relayConf6) + if err != nil { + return nil, errors.Wrapf(err, "NewGuestDHCP6Server(%s, %d, %#v)", nic.Bridge, options.HostOptions.Dhcp6ServerPort, relayConf6) + } } // dhcp server start after guest manager init return nic, nil diff --git a/pkg/hostman/system_service/telegraf.go b/pkg/hostman/system_service/telegraf.go index 55184d6e3a..7d57ac5523 100644 --- a/pkg/hostman/system_service/telegraf.go +++ b/pkg/hostman/system_service/telegraf.go @@ -293,7 +293,7 @@ func (s *STelegraf) GetConfig(kwargs map[string]interface{}) string { conf += "[[inputs.linux_sysctl_fs]]\n" conf += "\n" conf += "[[inputs.http_listener_v2]]\n" - conf += " service_address = \"127.0.0.1:8087\"\n" + conf += " service_address = \"localhost:8087\"\n" conf += " path = \"/write\"\n" conf += " data_source = \"body\"\n" conf += " data_format = \"influx\"\n" @@ -418,7 +418,7 @@ func (s *STelegraf) reloadTelegrafByDocker() error { } func (s *STelegraf) reloadTelegrafByHTTP() error { - telegrafReoladUrl := "http://127.0.0.1:8087/reload" + telegrafReoladUrl := "http://localhost:8087/reload" log.Infof("Reloading telegraf by %q ...", telegrafReoladUrl) if _, _, err := httputils.JSONRequest( httputils.GetDefaultClient(), context.Background(), diff --git a/pkg/mcclient/options/compute/network.go b/pkg/mcclient/options/compute/network.go index e10fcd57f5..a9526f1150 100644 --- a/pkg/mcclient/options/compute/network.go +++ b/pkg/mcclient/options/compute/network.go @@ -67,9 +67,9 @@ func (opts *NetworkListOptions) Params() (jsonutils.JSONObject, error) { type NetworkCreateOptions struct { WIRE string `help:"ID or Name of wire in which the network is created"` NETWORK string `help:"Name of new network"` - STARTIP string `help:"Start of IPv4 address range"` - ENDIP string `help:"End of IPv4 address rnage"` - NETMASK int64 `help:"Length of network mask"` + StartIp string `help:"Start of IPv4 address range"` + EndIp string `help:"End of IPv4 address rnage"` + NetMask int64 `help:"Length of network mask"` Gateway string `help:"Default gateway"` StartIp6 string `help:"IPv6 start ip"` @@ -91,9 +91,16 @@ func (opts *NetworkCreateOptions) Params() (jsonutils.JSONObject, error) { params.Add(jsonutils.NewString(opts.WIRE), "wire") params.Add(jsonutils.NewString(opts.NETWORK), "name") - params.Add(jsonutils.NewString(opts.STARTIP), "guest_ip_start") - params.Add(jsonutils.NewString(opts.ENDIP), "guest_ip_end") - params.Add(jsonutils.NewInt(opts.NETMASK), "guest_ip_mask") + + if len(opts.StartIp) > 0 { + params.Add(jsonutils.NewString(opts.StartIp), "guest_ip_start") + } + if len(opts.EndIp) > 0 { + params.Add(jsonutils.NewString(opts.EndIp), "guest_ip_end") + } + if opts.NetMask > 0 { + params.Add(jsonutils.NewInt(opts.NetMask), "guest_ip_mask") + } if len(opts.Gateway) > 0 { params.Add(jsonutils.NewString(opts.Gateway), "guest_gateway") } diff --git a/pkg/scheduler/algorithm/predicates/error.go b/pkg/scheduler/algorithm/predicates/error.go index 1188cbc982..ca71033ca4 100644 --- a/pkg/scheduler/algorithm/predicates/error.go +++ b/pkg/scheduler/algorithm/predicates/error.go @@ -23,6 +23,7 @@ const ( ErrServerTypeIsNotMatch = `server type is not match` ErrExitIsNotMatch = `exit is not match` ErrWireIsNotMatch = `wire is not match` + ErrNotSupportIpv6 = `not support ipv6` ErrNoPorts = `no ports` ErrNotOwner = `not owner` diff --git a/pkg/scheduler/algorithm/predicates/network_predicate.go b/pkg/scheduler/algorithm/predicates/network_predicate.go index 55bb21b04e..80c1728602 100644 --- a/pkg/scheduler/algorithm/predicates/network_predicate.go +++ b/pkg/scheduler/algorithm/predicates/network_predicate.go @@ -186,6 +186,12 @@ func IsNetworkAvailable( exit := req.Exit wire := req.Wire + if req.RequireIPv6 && !n.IsSupportIPv6() { + return FailReason{ + Reason: fmt.Sprintf("%v(%v): %s", n.Name, n.Id, ErrNotSupportIpv6), + } + } + isMatchServerType := func(network *models.SNetwork) bool { return computeapi.IsInNetworkTypes(network.ServerType, netTypes) } diff --git a/pkg/util/netutils2/iface.go b/pkg/util/netutils2/iface.go index 7f56f68a80..7fd40bd87f 100644 --- a/pkg/util/netutils2/iface.go +++ b/pkg/util/netutils2/iface.go @@ -31,6 +31,8 @@ func getIfaceIPs(iface *net.Interface) ([]net.IP, error) { if ipnet, ok := a.(*net.IPNet); ok && !ipnet.IP.IsLoopback() { if ipnet.IP.To4() != nil { ips = append(ips, ipnet.IP) + } else if ipnet.IP.To16() != nil { + ips = append(ips, ipnet.IP) } } } diff --git a/pkg/util/netutils2/netutils.go b/pkg/util/netutils2/netutils.go index 9c589f45cd..7baee97af2 100644 --- a/pkg/util/netutils2/netutils.go +++ b/pkg/util/netutils2/netutils.go @@ -77,6 +77,195 @@ func MyIPTo(dstIP string) (ip string, err error) { return } +func MyIPSmart() (ip string, err error) { + return MyIPSmartTo("114.114.114.114", "2001:4860:4860::8888") +} + +func MyIPSmartTo(ipv4Target, ipv6Target string) (ip string, err error) { + // try IPv4 connect + if ipv4Target != "" { + conn, err4 := net.Dial("udp4", ipv4Target+":53") + if err4 == nil { + defer conn.Close() + if addr, ok := conn.LocalAddr().(*net.UDPAddr); ok { + return addr.IP.String(), nil + } + } + } + + // try IPv6 connect + if ipv6Target != "" { + conn, err6 := net.Dial("udp6", "["+ipv6Target+"]:53") + if err6 == nil { + defer conn.Close() + if addr, ok := conn.LocalAddr().(*net.UDPAddr); ok { + return addr.IP.String(), nil + } + } + } + + // try locallink + return getLocalIP() +} + +func getLocalIP() (string, error) { + // get default route + if ip := getIPFromDefaultRoute(); ip != "" { + return ip, nil + } + + interfaces, err := net.Interfaces() + if err != nil { + return "", errors.Wrap(err, "get network interfaces") + } + + var candidateIPs []string + for _, iface := range interfaces { + if iface.Flags&net.FlagUp == 0 || iface.Flags&net.FlagLoopback != 0 { + continue + } + + addrs, err := iface.Addrs() + if err != nil { + continue + } + + for _, addr := range addrs { + if ipnet, ok := addr.(*net.IPNet); ok && !ipnet.IP.IsLoopback() { + if ipnet.IP.To4() != nil { + return ipnet.IP.String(), nil + } else if ipnet.IP.To16() != nil && !ipnet.IP.IsLinkLocalUnicast() { + candidateIPs = append(candidateIPs, ipnet.IP.String()) + } + } + } + } + + if len(candidateIPs) > 0 { + return candidateIPs[0], nil + } + + return "", fmt.Errorf("no suitable IP address found") +} + +func getIPFromDefaultRoute() string { + if ip := getIPFromDefaultRouteV4(); ip != "" { + return ip + } + + if ip := getIPFromDefaultRouteV6(); ip != "" { + return ip + } + + return "" +} + +func getIPFromDefaultRouteV4() string { + output, err := procutils.NewCommand("ip", "route", "show", "default").Output() + if err != nil { + return "" + } + + lines := strings.Split(string(output), "\n") + for _, line := range lines { + if strings.Contains(line, "default") && strings.Contains(line, "src") { + fields := strings.Fields(line) + for i, field := range fields { + if field == "src" && i+1 < len(fields) { + return fields[i+1] + } + } + } + if strings.Contains(line, "default") && strings.Contains(line, "dev") { + fields := strings.Fields(line) + for i, field := range fields { + if field == "dev" && i+1 < len(fields) { + if ip := getIPFromInterface(fields[i+1]); ip != "" { + return ip + } + } + } + } + } + + return "" +} + +func getIPFromDefaultRouteV6() string { + output, err := procutils.NewCommand("ip", "-6", "route", "show", "default").Output() + if err != nil { + return "" + } + + lines := strings.Split(string(output), "\n") + for _, line := range lines { + if strings.Contains(line, "default") && strings.Contains(line, "src") { + fields := strings.Fields(line) + for i, field := range fields { + if field == "src" && i+1 < len(fields) { + return fields[i+1] + } + } + } + if strings.Contains(line, "default") && strings.Contains(line, "dev") { + fields := strings.Fields(line) + for i, field := range fields { + if field == "dev" && i+1 < len(fields) { + if ip := getIPFromInterfaceV6(fields[i+1]); ip != "" { + return ip + } + } + } + } + } + + return "" +} + +func getIPFromInterface(ifaceName string) string { + iface, err := net.InterfaceByName(ifaceName) + if err != nil { + return "" + } + + addrs, err := iface.Addrs() + if err != nil { + return "" + } + + for _, addr := range addrs { + if ipnet, ok := addr.(*net.IPNet); ok && !ipnet.IP.IsLoopback() { + if ipv4 := ipnet.IP.To4(); ipv4 != nil { + return ipv4.String() + } + } + } + + return "" +} + +func getIPFromInterfaceV6(ifaceName string) string { + iface, err := net.InterfaceByName(ifaceName) + if err != nil { + return "" + } + + addrs, err := iface.Addrs() + if err != nil { + return "" + } + + for _, addr := range addrs { + if ipnet, ok := addr.(*net.IPNet); ok && !ipnet.IP.IsLoopback() { + if ipv6 := ipnet.IP.To16(); ipv6 != nil && ipnet.IP.To4() == nil && !ipnet.IP.IsLinkLocalUnicast() { + return ipv6.String() + } + } + } + + return "" +} + func GetPrivatePrefixes(privatePrefixes []string) []string { if privatePrefixes != nil { return privatePrefixes diff --git a/pkg/util/netutils2/netutils_linux.go b/pkg/util/netutils2/netutils_linux.go index 643539948b..f3a5ccee62 100644 --- a/pkg/util/netutils2/netutils_linux.go +++ b/pkg/util/netutils2/netutils_linux.go @@ -76,9 +76,12 @@ func (n *SNetInterface) GetRouteSpecs() []iproute2.RouteSpec { } routes6 := getRouteSpecs(routeList.List6) for i := range routes6 { - if routes6[i].Gw == nil { + isDefaultRoute := routes6[i].Dst.String() == "::/0" + + if !isDefaultRoute && routes6[i].Gw == nil { continue } + if strings.HasPrefix(routes6[i].Dst.String(), "fe80:") { continue } diff --git a/pkg/util/ovnutils/ovnutils.go b/pkg/util/ovnutils/ovnutils.go index 59c5118f1b..19d6b13f95 100644 --- a/pkg/util/ovnutils/ovnutils.go +++ b/pkg/util/ovnutils/ovnutils.go @@ -73,9 +73,17 @@ func mustPrepOvsdbConfig(opts SOvnOptions) { ) switch { case strings.HasPrefix(meth, "can-reach:"): - encapIp, err = netutils2.MyIPTo(meth[10:]) + target := meth[10:] + if strings.Contains(target, ":") { + encapIp, err = netutils2.MyIPSmartTo("", target) + } else { + encapIp, err = netutils2.MyIPSmartTo(target, "") + } default: - encapIp, err = netutils2.MyIP() + encapIp, err = netutils2.MyIPSmart() + if err != nil { + encapIp, err = netutils2.MyIP() + } } if err != nil { panic(errors.Wrapf(ErrOvnConfig, "determine encap ip, method: %q: %v", meth, err)) diff --git a/vendor/modules.txt b/vendor/modules.txt index 96569de47a..2b1cfb035d 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1949,7 +1949,7 @@ yunion.io/x/log/hooks yunion.io/x/ovsdb/cli_util yunion.io/x/ovsdb/schema/ovn_nb yunion.io/x/ovsdb/types -# yunion.io/x/pkg v1.10.4-0.20250712095850-7797c1f3ffa6 +# yunion.io/x/pkg v1.10.4-0.20250715023532-99a31ea00303 ## explicit; go 1.18 yunion.io/x/pkg/appctx yunion.io/x/pkg/errors diff --git a/vendor/yunion.io/x/pkg/util/netutils/ipv6.go b/vendor/yunion.io/x/pkg/util/netutils/ipv6.go index 78d55cdae8..72c34c3292 100644 --- a/vendor/yunion.io/x/pkg/util/netutils/ipv6.go +++ b/vendor/yunion.io/x/pkg/util/netutils/ipv6.go @@ -17,6 +17,7 @@ package netutils import ( "encoding/binary" "fmt" + "math/big" "math/rand" "net" "sort" @@ -400,6 +401,23 @@ func (ar IPV6AddrRange) ContainsRange(ar2 IPV6AddrRange) bool { return ar.start.Le(ar2.start) && ar.end.Ge(ar2.end) } +func Uint16ArrayToBigInt(ip [8]uint16) *big.Int { + result := new(big.Int) + for i := 0; i < 8; i++ { + result.Lsh(result, 16) + result.Add(result, big.NewInt(int64(ip[i]))) + } + return result +} + +func (ar IPV6AddrRange) AddressCount() *big.Int { + start := Uint16ArrayToBigInt(ar.start) + end := Uint16ArrayToBigInt(ar.end) + diff := new(big.Int).Sub(end, start) + diff.Add(diff, big.NewInt(1)) + return diff +} + const ( borderEqual = 0 borderLower = 1 diff --git a/vendor/yunion.io/x/pkg/utils/misc.go b/vendor/yunion.io/x/pkg/utils/misc.go index 72d5cb135c..159f64a9c4 100644 --- a/vendor/yunion.io/x/pkg/utils/misc.go +++ b/vendor/yunion.io/x/pkg/utils/misc.go @@ -578,23 +578,69 @@ func TransSQLAchemyURL(pySQLSrc string) (dialect, ret string, err error) { } lastAtIndex := strings.LastIndex(pySQLSrc, "@") + if lastAtIndex == -1 { + err = fmt.Errorf("Incorrect mysql connection url: %s", pySQLSrc) + return + } + firstPart := pySQLSrc[:lastAtIndex] secondPart := pySQLSrc[lastAtIndex+1:] r := regexp.MustCompile(`[/:]+`) firstPartArr := r.Split(firstPart, -1) - secondPartArr := r.Split(secondPart, -1) - - strs := append(firstPartArr, secondPartArr...) - if len(strs) != 6 { + if len(firstPartArr) < 3 { err = fmt.Errorf("Incorrect mysql connection url: %s", pySQLSrc) return } - user, passwd, host, port, dburl := strs[1], strs[2], strs[3], strs[4], strs[5] + + user := firstPartArr[1] + passwd := firstPartArr[2] + + var host, port, dburl string + if strings.HasPrefix(secondPart, "[") { + endBracket := strings.Index(secondPart, "]") + if endBracket == -1 { + err = fmt.Errorf("Incorrect IPv6 address format: %s", pySQLSrc) + return + } + + host = secondPart[:endBracket+1] + remaining := secondPart[endBracket+1:] + if !strings.HasPrefix(remaining, ":") { + err = fmt.Errorf("Incorrect mysql connection url: %s", pySQLSrc) + return + } + remaining = remaining[1:] + slashIndex := strings.Index(remaining, "/") + if slashIndex == -1 { + err = fmt.Errorf("Incorrect mysql connection url: %s", pySQLSrc) + return + } + port = remaining[:slashIndex] + dburl = remaining[slashIndex+1:] + } else { + colonIndex := strings.Index(secondPart, ":") + if colonIndex == -1 { + err = fmt.Errorf("Incorrect mysql connection url: %s", pySQLSrc) + return + } + + host = secondPart[:colonIndex] + remaining := secondPart[colonIndex+1:] + slashIndex := strings.Index(remaining, "/") + if slashIndex == -1 { + err = fmt.Errorf("Incorrect mysql connection url: %s", pySQLSrc) + return + } + port = remaining[:slashIndex] + dburl = remaining[slashIndex+1:] + } + dburl, err = transMysqlQuery(dburl) if err != nil { return } + ret = fmt.Sprintf("%s:%s@tcp(%s:%s)/%s", user, passwd, host, port, dburl) return }