feat(region,host-deployer): ipv6 only guest support (#22896)

This commit is contained in:
wanyaoqi
2025-07-15 16:21:37 +08:00
committed by GitHub
parent ed2d780c64
commit f7caf2b147
24 changed files with 503 additions and 133 deletions
+1 -1
View File
@@ -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
+2 -2
View File
@@ -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=
+4
View File
@@ -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"`
+3
View File
@@ -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=") {
+4
View File
@@ -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地址
+5 -3
View File
@@ -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
+8
View File
@@ -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
+49 -31
View File
@@ -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
}
+69 -45
View File
@@ -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)
+25 -14
View File
@@ -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
@@ -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))
+19 -8
View File
@@ -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() {
+11 -7
View File
@@ -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
+2 -2
View File
@@ -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(),
+13 -6
View File
@@ -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")
}
@@ -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`
@@ -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)
}
+2
View File
@@ -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)
}
}
}
+189
View File
@@ -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
+4 -1
View File
@@ -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
}
+10 -2
View File
@@ -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))
+1 -1
View File
@@ -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
+18
View File
@@ -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
+51 -5
View File
@@ -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
}