diff --git a/cmd/climc/shell/compute/hosts.go b/cmd/climc/shell/compute/hosts.go index 78500d0eba..d1ddc014d2 100644 --- a/cmd/climc/shell/compute/hosts.go +++ b/cmd/climc/shell/compute/hosts.go @@ -263,6 +263,7 @@ func init() { INDEX int64 `help:"nic index"` Type string `help:"Nic type" choices:"admin|ipmi"` IpAddr string `help:"IP address"` + Ip6Addr string `help:"IPv6 address"` Bridge string `help:"Bridge of hostwire"` Interface string `help:"Interface name, eg:eth0, en0"` } @@ -278,6 +279,9 @@ func init() { if len(args.IpAddr) > 0 { params.Add(jsonutils.NewString(args.IpAddr), "ip_addr") } + if len(args.Ip6Addr) > 0 { + params.Add(jsonutils.NewString(args.Ip6Addr), "ip6_addr") + } if len(args.Bridge) > 0 { params.Add(jsonutils.NewString(args.Bridge), "bridge") } @@ -310,7 +314,8 @@ func init() { type HostEnableNetIfOptions struct { ID string `help:"ID or Name of host"` MAC string `help:"MAC of NIC to enable"` - Ip string `help:"IP address"` + Ip string `help:"IPv4 address"` + Ip6 string `help:"IPv6 address"` Network string `help:"network to connect"` Reserved bool `help:"fetch IP from reserved pool"` } @@ -319,6 +324,11 @@ func init() { params.Add(jsonutils.NewString(args.MAC), "mac") if len(args.Ip) > 0 { params.Add(jsonutils.NewString(args.Ip), "ip_addr") + } + if len(args.Ip6) > 0 { + params.Add(jsonutils.NewString(args.Ip6), "ip6_addr") + } + if len(args.Ip) > 0 || len(args.Ip6) > 0 { if args.Reserved { params.Add(jsonutils.JSONTrue, "reserve") } diff --git a/pkg/apis/compute/guestnetwork.go b/pkg/apis/compute/guestnetwork.go index 2c97a9beff..6efb52a721 100644 --- a/pkg/apis/compute/guestnetwork.go +++ b/pkg/apis/compute/guestnetwork.go @@ -127,6 +127,8 @@ type GuestnetworkBaseDesc struct { Id string `json:"id"` Provider string `json:"provider"` MappedIpAddr string `json:"mapped_ip_addr"` + + MappedIp6Addr string `json:"mapped_ip6_addr"` } `json:"vpc"` Networkaddresses jsonutils.JSONObject `json:"networkaddresses"` diff --git a/pkg/apis/compute/host.go b/pkg/apis/compute/host.go index dcdfaa804d..a1d27435d5 100644 --- a/pkg/apis/compute/host.go +++ b/pkg/apis/compute/host.go @@ -672,6 +672,12 @@ type HostAddNetifInput struct { Reserve *bool `json:"reserve"` RequireDesignatedIp *bool `json:"require_designated_ip"` + + Ip6Addr string `json:"ip6_addr"` + + RequireIpv6 *bool `json:"require_ipv6"` + + StrictIpv6 *bool `json:"strict_ipv6"` } type HostEnableNetifInput struct { @@ -690,6 +696,12 @@ type HostEnableNetifInput struct { Reserve *bool `json:"reserve"` RequireDesignatedIp *bool `json:"require_designated_ip"` + + Ip6Addr string `json:"ip6_addr"` + + RequireIpv6 *bool `json:"require_ipv6"` + + StrictIpv6 *bool `json:"strict_ipv6"` } type HostDisableNetifInput struct { diff --git a/pkg/apis/compute/vpc.go b/pkg/apis/compute/vpc.go index 6d5f2962cd..d369879852 100644 --- a/pkg/apis/compute/vpc.go +++ b/pkg/apis/compute/vpc.go @@ -168,6 +168,7 @@ type NetworkTopologyOutput struct { type HostnetworkTopologyOutput struct { IpAddr string `json:"ip_addr"` + Ip6Addr string `json:"ip6_addr"` MacAddr string `json:"mac_addr"` } diff --git a/pkg/apis/compute/vpcs_ovn.go b/pkg/apis/compute/vpcs_ovn.go index bd99d15434..57589bf1ff 100644 --- a/pkg/apis/compute/vpcs_ovn.go +++ b/pkg/apis/compute/vpcs_ovn.go @@ -16,6 +16,7 @@ package compute import ( "fmt" + "strings" "yunion.io/x/cloudmux/pkg/apis/compute" "yunion.io/x/pkg/util/netutils" @@ -51,12 +52,22 @@ const ( sVpcInterExtIP2 = "100.65.0.2" VpcInterExtMac1 = "ee:ee:ee:ee:ee:f0" VpcInterExtMac2 = "ee:ee:ee:ee:ee:f1" + + sVpcInterCidr6 = "fc00::fffe:100:65:0:0/100" + sVpcInterExtCidr6 = "fc00::fffe:100:65:0:0/112" + VpcInterExtMask6 = 112 + sVpcInterExtIP16 = "fc00::fffe:100:65:0:1" + sVpcInterExtIP26 = "fc00::fffe:100:65:0:2" ) var ( vpcInterCidr netutils.IPV4Prefix vpcInterExtIP1 netutils.IPV4Addr vpcInterExtIP2 netutils.IPV4Addr + + vpcInterCidr6 netutils.IPV6Prefix + vpcInterExtIP16 netutils.IPV6Addr + vpcInterExtIP26 netutils.IPV6Addr ) func VpcInterCidr() netutils.IPV4Prefix { @@ -71,12 +82,28 @@ func VpcInterExtIP2() netutils.IPV4Addr { return vpcInterExtIP2 } +func VpcInterCidr6() netutils.IPV6Prefix { + return vpcInterCidr6 +} + +func VpcInterExtIP16() netutils.IPV6Addr { + return vpcInterExtIP16 +} + +func VpcInterExtIP26() netutils.IPV6Addr { + return vpcInterExtIP26 +} + const ( sVpcMappedCidr = "100.64.0.0/17" VpcMappedIPMask = 17 sVpcMappedGatewayIP = "100.64.0.1" VpcMappedGatewayMac = "ee:ee:ee:ee:ee:ee" + sVpcMappedCidr6 = "fc00::fffe:100:64:0:0/100" + VpcMappedIPMask6 = 100 + sVpcMappedGatewayIP6 = "fc00::fffe:100:64:0:1" + // reserved: [100.64.0.2, 100.64.0.127] // [128, 2176 (128+2048)] @@ -98,18 +125,31 @@ const ( sVpcEipGatewayIP = "100.64.128.2" VpcEipGatewayMac = "ee:ee:ee:ee:ee:ef" + sVpcEipGatewayCidr6 = "fc00::fffe:100:64:f000:0/100" + VpcEipGatewayIPMask6 = 100 + sVpcEipGatewayIP6 = "fc00::fffe:100:64:f000:2" + sVpcEipGatewayIP3 = "100.64.128.3" VpcEipGatewayMac3 = "ee:ee:ee:ee:ee:f0" + + sVpcEipGatewayIP63 = "fc00::fffe:100:64:f000:3" ) var ( vpcMappedCidr netutils.IPV4Prefix vpcMappedGatewayIP netutils.IPV4Addr + vpcMappedCidr6 netutils.IPV6Prefix + vpcMappedGatewayIP6 netutils.IPV6Addr + vpcEipGatewayCidr netutils.IPV4Prefix vpcEipGatewayIP netutils.IPV4Addr vpcEipGatewayIP3 netutils.IPV4Addr + vpcEipGatewayCidr6 netutils.IPV6Prefix + vpcEipGatewayIP6 netutils.IPV6Addr + vpcEipGatewayIP63 netutils.IPV6Addr + vpcMappedHostIPStart netutils.IPV4Addr vpcMappedHostIPEnd netutils.IPV4Addr @@ -131,17 +171,41 @@ func init() { return v } + mi6 := func(v netutils.IPV6Addr, err error) netutils.IPV6Addr { + if err != nil { + panic(err.Error()) + } + return v + } + mp6 := func(v netutils.IPV6Prefix, err error) netutils.IPV6Prefix { + if err != nil { + panic(err.Error()) + } + return v + } + vpcInterCidr = mp(netutils.NewIPV4Prefix(sVpcInterCidr)) vpcInterExtIP1 = mi(netutils.NewIPV4Addr(sVpcInterExtIP1)) vpcInterExtIP2 = mi(netutils.NewIPV4Addr(sVpcInterExtIP2)) + vpcInterCidr6 = mp6(netutils.NewIPV6Prefix(sVpcInterCidr6)) + vpcInterExtIP16 = mi6(netutils.NewIPV6Addr(sVpcInterExtIP16)) + vpcInterExtIP26 = mi6(netutils.NewIPV6Addr(sVpcInterExtIP26)) + vpcMappedCidr = mp(netutils.NewIPV4Prefix(sVpcMappedCidr)) vpcMappedGatewayIP = mi(netutils.NewIPV4Addr(sVpcMappedGatewayIP)) + vpcMappedCidr6 = mp6(netutils.NewIPV6Prefix(sVpcMappedCidr6)) + vpcMappedGatewayIP6 = mi6(netutils.NewIPV6Addr(sVpcMappedGatewayIP6)) + vpcEipGatewayCidr = mp(netutils.NewIPV4Prefix(sVpcEipGatewayCidr)) vpcEipGatewayIP = mi(netutils.NewIPV4Addr(sVpcEipGatewayIP)) vpcEipGatewayIP3 = mi(netutils.NewIPV4Addr(sVpcEipGatewayIP3)) + vpcEipGatewayCidr6 = mp6(netutils.NewIPV6Prefix(sVpcEipGatewayCidr6)) + vpcEipGatewayIP6 = mi6(netutils.NewIPV6Addr(sVpcEipGatewayIP6)) + vpcEipGatewayIP63 = mi6(netutils.NewIPV6Addr(sVpcEipGatewayIP63)) + vpcMappedHostIPStart = mi(netutils.NewIPV4Addr(sVpcMappedHostIPStart)) vpcMappedHostIPEnd = mi(netutils.NewIPV4Addr(sVpcMappedHostIPEnd)) @@ -153,22 +217,42 @@ func VpcMappedCidr() netutils.IPV4Prefix { return vpcMappedCidr } +func VpcMappedCidr6() netutils.IPV6Prefix { + return vpcMappedCidr6 +} + func VpcMappedGatewayIP() netutils.IPV4Addr { return vpcMappedGatewayIP } +func VpcMappedGatewayIP6() netutils.IPV6Addr { + return vpcMappedGatewayIP6 +} + func VpcEipGatewayCidr() netutils.IPV4Prefix { return vpcEipGatewayCidr } +func VpcEipGatewayCidr6() netutils.IPV6Prefix { + return vpcEipGatewayCidr6 +} + func VpcEipGatewayIP() netutils.IPV4Addr { return vpcEipGatewayIP } +func VpcEipGatewayIP6() netutils.IPV6Addr { + return vpcEipGatewayIP6 +} + func VpcEipGatewayIP3() netutils.IPV4Addr { return vpcEipGatewayIP3 } +func VpcEipGatewayIP63() netutils.IPV6Addr { + return vpcEipGatewayIP63 +} + func VpcMappedHostIPStart() netutils.IPV4Addr { return vpcMappedHostIPStart } @@ -188,3 +272,8 @@ func VpcMappedIPEnd() netutils.IPV4Addr { func VpcOvnEncapCostStr() string { return fmt.Sprintf("%d", VPC_OVN_ENCAP_COST) } + +func GenVpcMappedIP6(ip string) string { + segs := strings.Split(ip, ".") + return fmt.Sprintf("fc00::fffe:100:64:%s:%s", segs[2], segs[3]) +} diff --git a/pkg/apis/const.go b/pkg/apis/const.go index 54a5a2727c..2b8a60247e 100644 --- a/pkg/apis/const.go +++ b/pkg/apis/const.go @@ -73,7 +73,7 @@ const ( SKU_STATUS_AVAILABLE = "available" SKU_STATUS_SOLDOUT = "soldout" - MetaServiceMonitorAgentUrl = "http://169.254.169.254/monitor" + MetaServiceMonitorAgentUrl = "http://%s/monitor" ) var ( diff --git a/pkg/baremetal/nic.go b/pkg/baremetal/nic.go index 38f08ed1eb..a54b095aa6 100644 --- a/pkg/baremetal/nic.go +++ b/pkg/baremetal/nic.go @@ -115,12 +115,12 @@ func GetNicDHCPConfig( conf.Gateway = net.ParseIP(n.Gateway) } if n.Ip6Addr != "" { - ipAddr6, err := netutils.NewIPV6Addr(n.Ip6Addr) + ip6Addr, err := netutils.NewIPV6Addr(n.Ip6Addr) if err != nil { return nil, errors.Wrapf(err, "Parse IPv6 address error: %q", n.Ip6Addr) } - conf.ClientIP6 = net.ParseIP(ipAddr6.String()) + conf.ClientIP6 = net.ParseIP(ip6Addr.String()) conf.PrefixLen6 = n.Masklen6 conf.Gateway6 = net.ParseIP(n.Gateway6) } diff --git a/pkg/compute/guestdrivers/baremetals.go b/pkg/compute/guestdrivers/baremetals.go index c5a4941fd2..fecb0ff821 100644 --- a/pkg/compute/guestdrivers/baremetals.go +++ b/pkg/compute/guestdrivers/baremetals.go @@ -163,9 +163,10 @@ func (self *SBaremetalGuestDriver) GetNamedNetworkConfiguration(guest *models.SG reuseAddr := false hn := host.GetAttach2Network(netConfig.Network) if hn != nil && options.Options.BaremetalServerReuseHostIp { - if netConfig.Address == "" || netConfig.Address == hn.IpAddr { + if (netConfig.Address == "" && netConfig.Address6 == "") || (netConfig.Address == hn.IpAddr && netConfig.Address6 == hn.Ip6Addr) { // try to reuse host network IP address netConfig.Address = hn.IpAddr + netConfig.Address6 = hn.Ip6Addr reuseAddr = true } } @@ -248,17 +249,20 @@ func (self *SBaremetalGuestDriver) Attach2RandomNetwork(guest *models.SGuest, ct nicConfs = append(nicConfs, nicConf) } address := "" + address6 := "" reuseAddr := false hn := host.GetAttach2Network(net.Id) if hn != nil && options.Options.BaremetalServerReuseHostIp { // try to reuse host network IP address address = hn.IpAddr + address6 = hn.Ip6Addr reuseAddr = true } return guest.Attach2Network(ctx, userCred, models.Attach2NetworkArgs{ Network: net, PendingUsage: pendingUsage, IpAddr: address, + Ip6Addr: address6, NicDriver: netConfig.Driver, BwLimit: netConfig.BwLimit, Virtual: netConfig.Vip, diff --git a/pkg/compute/guestdrivers/cloudpods-baremetals.go b/pkg/compute/guestdrivers/cloudpods-baremetals.go index f363607f9b..f840f3aefc 100644 --- a/pkg/compute/guestdrivers/cloudpods-baremetals.go +++ b/pkg/compute/guestdrivers/cloudpods-baremetals.go @@ -163,9 +163,10 @@ func (self *SCloudpodsBaremetalGuestDriver) GetNamedNetworkConfiguration(guest * reuseAddr := false hn := host.GetAttach2Network(netConfig.Network) if hn != nil && options.Options.BaremetalServerReuseHostIp { - if netConfig.Address == "" || netConfig.Address == hn.IpAddr { + if (netConfig.Address == "" && netConfig.Address6 == "") || (netConfig.Address == hn.IpAddr && netConfig.Address6 == hn.Ip6Addr) { // try to reuse host network IP address netConfig.Address = hn.IpAddr + netConfig.Address6 = hn.Ip6Addr reuseAddr = true } } @@ -248,17 +249,20 @@ func (self *SCloudpodsBaremetalGuestDriver) Attach2RandomNetwork(guest *models.S nicConfs = append(nicConfs, nicConf) } address := "" + address6 := "" reuseAddr := false hn := host.GetAttach2Network(net.Id) if hn != nil && options.Options.BaremetalServerReuseHostIp { // try to reuse host network IP address address = hn.IpAddr + address6 = hn.Ip6Addr reuseAddr = true } return guest.Attach2Network(ctx, userCred, models.Attach2NetworkArgs{ Network: net, PendingUsage: pendingUsage, IpAddr: address, + Ip6Addr: address6, NicDriver: netConfig.Driver, BwLimit: netConfig.BwLimit, Virtual: netConfig.Vip, diff --git a/pkg/compute/guestdrivers/kvm.go b/pkg/compute/guestdrivers/kvm.go index ed6d4b3860..625bd7cc64 100644 --- a/pkg/compute/guestdrivers/kvm.go +++ b/pkg/compute/guestdrivers/kvm.go @@ -1203,7 +1203,17 @@ func (self *SKVMGuestDriver) RequestQgaCommand(ctx context.Context, userCred mcc func (self *SKVMGuestDriver) FetchMonitorUrl(ctx context.Context, guest *models.SGuest) string { if options.Options.KvmMonitorAgentUseMetadataService && !guest.IsSriov() { - return apis.MetaServiceMonitorAgentUrl + var metadataIp string + strictIpv6, err := guest.IsStrictIpv6() + if err != nil { + log.Errorf("IsStrictIpv6 for guest %s error: %v", guest.Id, err) + } + if strictIpv6 { + metadataIp = "[" + options.Options.MetadataServerIp6s[0] + "]" + } else { + metadataIp = options.Options.MetadataServerIp4s[0] + } + return fmt.Sprintf(apis.MetaServiceMonitorAgentUrl, metadataIp) } return self.SVirtualizedGuestDriver.FetchMonitorUrl(ctx, guest) } diff --git a/pkg/compute/hostdrivers/base.go b/pkg/compute/hostdrivers/base.go index 0206888750..809333120e 100644 --- a/pkg/compute/hostdrivers/base.go +++ b/pkg/compute/hostdrivers/base.go @@ -139,8 +139,12 @@ func (self *SBaseHostDriver) FinishUnconvert(ctx context.Context, userCred mccli if adminNic == nil { return fmt.Errorf("admin nic is nil") } + accessIp := adminNic.IpAddr + if accessIp == "" { + accessIp = adminNic.Ip6Addr + } db.Update(host, func() error { - host.AccessIp = adminNic.IpAddr + host.AccessIp = accessIp host.SetEnabled(true) host.HostType = api.HOST_TYPE_BAREMETAL host.HostStatus = api.HOST_OFFLINE diff --git a/pkg/compute/models/groupnetworks.go b/pkg/compute/models/groupnetworks.go index f02427ce0d..f327ae5e81 100644 --- a/pkg/compute/models/groupnetworks.go +++ b/pkg/compute/models/groupnetworks.go @@ -64,7 +64,7 @@ type SGroupnetwork struct { // IPv6地址 Ip6Addr string `width:"64" charset:"ascii" nullable:"true" list:"user" create:"optional"` // Column(VARCHAR(64, charset='ascii'), nullable=True) - Index int8 `nullable:"false" default:"0" list:"user" list:"user" update:"user" create:"optional"` // Column(TINYINT, nullable=False, default=0) + Index int8 `nullable:"false" default:"0" list:"user" update:"user" create:"optional"` // Column(TINYINT, nullable=False, default=0) EipId string `width:"36" charset:"ascii" nullable:"true" list:"user"` // Column(VARCHAR(36, charset='ascii'), nullable=True) } diff --git a/pkg/compute/models/groups.go b/pkg/compute/models/groups.go index a424e33c26..596ea92542 100644 --- a/pkg/compute/models/groups.go +++ b/pkg/compute/models/groups.go @@ -649,13 +649,13 @@ func (grp *SGroup) PerformAttachnetwork(ctx context.Context, userCred mcclient.T return nil, errors.Wrapf(httperrors.ErrConflict, "candidate ip %s is occupied!", input.IpAddr) } - var ipAddr6 string + var ip6Addr string if len(input.Ip6Addr) > 0 || input.RequireIPv6 { - ipAddr6, err = net.GetFreeIP(ctx, userCred, nil, nil, input.Ip6Addr, input.AllocDir, input.Reserved != nil && *input.Reserved, api.AddressTypeIPv6) + ip6Addr, err = net.GetFreeIP(ctx, userCred, nil, nil, input.Ip6Addr, input.AllocDir, input.Reserved != nil && *input.Reserved, api.AddressTypeIPv6) if err != nil { return nil, errors.Wrap(err, "GetFreeIPv6") } - if len(input.Ip6Addr) > 0 && ipAddr6 != input.Ip6Addr && input.RequireDesignatedIp != nil && *input.RequireDesignatedIp { + if len(input.Ip6Addr) > 0 && ip6Addr != input.Ip6Addr && input.RequireDesignatedIp != nil && *input.RequireDesignatedIp { return nil, errors.Wrapf(httperrors.ErrConflict, "candidate v6 ip %s is occupied!", input.Ip6Addr) } } @@ -664,7 +664,7 @@ func (grp *SGroup) PerformAttachnetwork(ctx context.Context, userCred mcclient.T gn.NetworkId = net.Id gn.GroupId = grp.Id gn.IpAddr = ipAddr - gn.Ip6Addr = ipAddr6 + gn.Ip6Addr = ip6Addr gn.SetModelManager(GroupnetworkManager, &gn) diff --git a/pkg/compute/models/guest_migrate_network.go b/pkg/compute/models/guest_migrate_network.go index 4b9413dbe9..3175e3bfc1 100644 --- a/pkg/compute/models/guest_migrate_network.go +++ b/pkg/compute/models/guest_migrate_network.go @@ -125,7 +125,8 @@ func (guest *SGuest) PerformMigrateNetwork(ctx context.Context, userCred mcclien // perform the database change _, err = db.Update(&srcNic, func() error { srcNic.NetworkId = destNet.Id - srcNic.MappedIpAddr = "" // reset MappedIpAddr anyway + srcNic.MappedIpAddr = "" // reset MappedIpAddr anyway + srcNic.MappedIp6Addr = "" // reset MappedIp6Addr anyway return nil }) if err != nil { diff --git a/pkg/compute/models/guestnetworks.go b/pkg/compute/models/guestnetworks.go index 07d412c013..f720285eaa 100644 --- a/pkg/compute/models/guestnetworks.go +++ b/pkg/compute/models/guestnetworks.go @@ -112,6 +112,8 @@ type SGuestnetwork struct { // IPv4映射地址,当子网属于私有云vpc的时候分配,用于访问外网 MappedIpAddr string `width:"16" charset:"ascii" nullable:"true" list:"user"` + // IPv6映射地址,当子网属于私有云vpc的时候分配,用于访问外网 + MappedIp6Addr string `width:"64" charset:"ascii" nullable:"true" list:"user"` // 网卡关联的Eip实例 EipId string `width:"36" charset:"ascii" nullable:"true" list:"user"` @@ -359,18 +361,8 @@ func (manager *SGuestnetworkManager) newGuestNetwork( gn.IpAddr = ipAddr } } - - if vpc.Id != api.DEFAULT_VPC_ID && provider == api.CLOUD_PROVIDER_ONECLOUD { - var err error - GuestnetworkManager.lockAllocMappedAddr(ctx) - defer GuestnetworkManager.unlockAllocMappedAddr(ctx) - gn.MappedIpAddr, err = GuestnetworkManager.allocMappedIpAddr(ctx) - if err != nil { - return nil, err - } - } - } + var err error if ipBindMac := NetworkIpMacManager.GetMacFromIp(network.Id, gn.IpAddr); ipBindMac != "" { gn.MacAddr = ipBindMac @@ -416,6 +408,17 @@ func (manager *SGuestnetworkManager) newGuestNetwork( } } + if vpc.Id != api.DEFAULT_VPC_ID && provider == api.CLOUD_PROVIDER_ONECLOUD && len(gn.IpAddr) > 0 || len(gn.Ip6Addr) > 0 { + var err error + GuestnetworkManager.lockAllocMappedAddr(ctx) + defer GuestnetworkManager.unlockAllocMappedAddr(ctx) + gn.MappedIpAddr, err = GuestnetworkManager.allocMappedIpAddr(ctx) + if err != nil { + return nil, errors.Wrap(err, "GuestnetworkManager.allocMappedIpAddr") + } + gn.MappedIp6Addr = api.GenVpcMappedIP6(gn.MappedIpAddr) + } + ifname, err = gn.checkOrAllocateIfname(network, ifname) if err != nil { return nil, err @@ -609,12 +612,22 @@ func (gn *SGuestnetwork) getJsonDescOneCloudVpc(network *SNetwork) *api.Guestnet } else { if _, err := db.Update(gn, func() error { gn.MappedIpAddr = addr + gn.MappedIp6Addr = api.GenVpcMappedIP6(addr) return nil }); err != nil { log.Errorf("getJsonDescOneCloudVpc: row %d: db update mapped addr: %v", gn.RowId, err) gn.MappedIpAddr = "" + gn.MappedIp6Addr = "" } } + } else if gn.MappedIp6Addr == "" { + if _, err := db.Update(gn, func() error { + gn.MappedIp6Addr = api.GenVpcMappedIP6(gn.MappedIpAddr) + return nil + }); err != nil { + log.Errorf("getJsonDescOneCloudVpc for v6: row %d: db update mapped addr6: %v", gn.RowId, err) + gn.MappedIp6Addr = "" + } } desc := gn.getJsonDesc() @@ -623,6 +636,7 @@ func (gn *SGuestnetwork) getJsonDescOneCloudVpc(network *SNetwork) *api.Guestnet desc.Vpc.Id = vpc.Id desc.Vpc.Provider = api.VPC_PROVIDER_OVN desc.Vpc.MappedIpAddr = gn.MappedIpAddr + desc.Vpc.MappedIp6Addr = gn.MappedIp6Addr return desc } @@ -1364,3 +1378,70 @@ func (manager *SGuestnetworkManager) ListItemExportKeys(ctx context.Context, return q, nil } + +func (manager *SGuestnetworkManager) InitializeData() error { + err := manager.initOvnMappedIps() + if err != nil { + return errors.Wrap(err, "initOvnMappedIps") + } + return nil +} + +func (manager *SGuestnetworkManager) initOvnMappedIps() error { + q := manager.Query() + networksQ := NetworkManager.Query().SubQuery() + wiresQ := WireManager.Query().SubQuery() + vpcQ := VpcManager.Query().SubQuery() + regionQ := CloudregionManager.Query().SubQuery() + + q = q.Join(networksQ, sqlchemy.Equals(q.Field("network_id"), networksQ.Field("id"))) + q = q.Join(wiresQ, sqlchemy.Equals(networksQ.Field("wire_id"), wiresQ.Field("id"))) + q = q.Join(vpcQ, sqlchemy.Equals(wiresQ.Field("vpc_id"), vpcQ.Field("id"))) + q = q.Join(regionQ, sqlchemy.Equals(vpcQ.Field("cloudregion_id"), regionQ.Field("id"))) + + q = q.Filter(sqlchemy.Equals(regionQ.Field("provider"), api.CLOUD_PROVIDER_ONECLOUD)) + q = q.Filter(sqlchemy.NotEquals(vpcQ.Field("id"), api.DEFAULT_VPC_ID)) + q = q.Filter(sqlchemy.OR( + sqlchemy.IsEmpty(q.Field("mapped_ip_addr")), + sqlchemy.IsEmpty(q.Field("mapped_ip6_addr")), + )) + + gns := make([]SGuestnetwork, 0) + err := db.FetchModelObjects(manager, q, &gns) + if err != nil { + return errors.Wrap(err, "FetchModelObjects") + } + + for i := range gns { + gn := &gns[i] + var v4addr string + if gn.MappedIpAddr == "" { + addr, err := manager.allocMappedIpAddr(context.Background()) + if err != nil { + return errors.Wrap(err, "allocMappedIpAddr") + } + v4addr = addr + } else { + v4addr = gn.MappedIpAddr + } + if _, err := db.Update(gn, func() error { + gn.MappedIpAddr = v4addr + gn.MappedIp6Addr = api.GenVpcMappedIP6(v4addr) + return nil + }); err != nil { + return errors.Wrap(err, "db.Update") + } + } + + return nil +} + +func (guest *SGuest) IsStrictIpv6() (bool, error) { + q := GuestnetworkManager.Query().Equals("guest_id", guest.Id) + q = q.IsNotEmpty("ip6_addr").IsNullOrEmpty("ip_addr").IsFalse("virtual") + cnt, err := q.CountWithError() + if err != nil { + return false, errors.Wrap(err, "CountWithError") + } + return cnt > 0, nil +} diff --git a/pkg/compute/models/guests.go b/pkg/compute/models/guests.go index 75c3bf7c16..fbda6a5f85 100644 --- a/pkg/compute/models/guests.go +++ b/pkg/compute/models/guests.go @@ -3730,7 +3730,6 @@ func (self *SGuest) attach2NetworkOnce( teamWithMac = args.teamWithMac ) network.updateDnsRecord(guestnic, true) - network.updateGuestNetmap(guestnic) if pendingUsage != nil && len(teamWithMac) == 0 { cancelUsage := SRegionQuota{} if network.IsExitNetwork() { diff --git a/pkg/compute/models/host_recycle.go b/pkg/compute/models/host_recycle.go index c1e60f1523..b87dfdf3ec 100644 --- a/pkg/compute/models/host_recycle.go +++ b/pkg/compute/models/host_recycle.go @@ -196,6 +196,7 @@ func (self *SGuest) doPrepaidRecycleNoLock(ctx context.Context, userCred mcclien 1, net.WireId, "", + "", 1000, nicType, i, @@ -205,7 +206,10 @@ func (self *SGuest) doPrepaidRecycleNoLock(ctx context.Context, userCred mcclien &ifname, &brname, false, - false) + false, + false, + false, + ) if err != nil { log.Errorf("fail to addNetInterface %d: %s", i, err) fakeHost.RealDelete(ctx, userCred) @@ -546,7 +550,7 @@ func (self *SHost) BorrowIpAddrsFromGuest(ctx context.Context, userCred mcclient return fmt.Errorf(msg) } - err = self.EnableNetif(ctx, userCred, netif, "", guestnics[i].IpAddr, "", "", false, false) + err = self.EnableNetif(ctx, userCred, netif, "", guestnics[i].IpAddr, guestnics[i].Ip6Addr, "", "", false, false, false, false) if err != nil { log.Errorf("fail to enable netif %s %s", guestnics[i].IpAddr, err) return err @@ -586,6 +590,7 @@ func (host *SHost) SetGuestCreateNetworkAndDiskParams(ctx context.Context, userC Network: hn.NetworkId, Mac: netifs[i].Mac, Address: hn.IpAddr, + Address6: hn.Ip6Addr, Reserved: true, }) netIdx += 1 diff --git a/pkg/compute/models/hostnetworks.go b/pkg/compute/models/hostnetworks.go index 789a19c5ff..8acce8ce48 100644 --- a/pkg/compute/models/hostnetworks.go +++ b/pkg/compute/models/hostnetworks.go @@ -62,6 +62,8 @@ type SHostnetwork struct { NetworkId string `width:"36" charset:"ascii" nullable:"false" list:"domain"` // IP地址 IpAddr string `width:"16" charset:"ascii" list:"domain"` + // IPv6地址 + Ip6Addr string `width:"64" charset:"ascii" list:"domain"` // MAC地址 MacAddr string `width:"18" charset:"ascii" list:"domain"` // VLAN diff --git a/pkg/compute/models/hosts.go b/pkg/compute/models/hosts.go index 455acb3741..693d21cc78 100644 --- a/pkg/compute/models/hosts.go +++ b/pkg/compute/models/hosts.go @@ -209,6 +209,8 @@ type SHost struct { // IPv4地址,作为私有云vpc访问外网时的网关 OvnMappedIpAddr string `width:"16" charset:"ascii" nullable:"true" list:"user"` + // IPv6地址,作为私有云vpc访问外网时的网关 + OvnMappedIp6Addr string `width:"64" charset:"ascii" nullable:"true" list:"user"` // UEFI详情 UefiInfo jsonutils.JSONObject `nullable:"true" get:"domain" update:"domain" create:"domain_optional"` @@ -2148,8 +2150,13 @@ func (hh *SHost) DeleteBaremetalnetwork(ctx context.Context, userCred mcclient.T net := bn.GetNetwork() bn.Delete(ctx, userCred) db.OpsLog.LogDetachEvent(ctx, hh, net, userCred, nil) - if reserve && net != nil && len(bn.IpAddr) > 0 && regutils.MatchIP4Addr(bn.IpAddr) { - ReservedipManager.ReserveIP(ctx, userCred, net, bn.IpAddr, "Delete baremetalnetwork to reserve", api.AddressTypeIPv4) + if reserve && net != nil { + if len(bn.IpAddr) > 0 && regutils.MatchIP4Addr(bn.IpAddr) { + ReservedipManager.ReserveIP(ctx, userCred, net, bn.IpAddr, "Delete baremetalnetwork to reserve", api.AddressTypeIPv4) + } + if len(bn.Ip6Addr) > 0 && regutils.MatchIP6Addr(bn.Ip6Addr) { + ReservedipManager.ReserveIP(ctx, userCred, net, bn.Ip6Addr, "Delete baremetalnetwork to reserve", api.AddressTypeIPv6) + } } } @@ -3675,11 +3682,14 @@ func fetchHostNics(hostIds []string) (map[string][]*types.SNic, error) { wires.Field("name").Label("wire"), wires.Field("bandwidth"), hn.Field("ip_addr"), + hn.Field("ip6_addr"), networks.Field("guest_gateway").Label("gateway"), + networks.Field("guest_gateway6").Label("gateway6"), networks.Field("guest_dns").Label("dns"), networks.Field("guest_domain").Label("domain"), networks.Field("guest_ntp").Label("ntp"), networks.Field("guest_ip_mask").Label("masklen"), + networks.Field("guest_ip6_mask").Label("masklen6"), networks.Field("name").Label("net"), networks.Field("id").Label("net_id"), zones.Field("name").Label("zone"), @@ -4306,6 +4316,7 @@ func (hh *SHost) PostCreate( if hh.OvnVersion != "" && hh.OvnMappedIpAddr == "" { HostManager.lockAllocOvnMappedIpAddr(ctx) defer HostManager.unlockAllocOvnMappedIpAddr(ctx) + addr, err := HostManager.allocOvnMappedIpAddr(ctx) if err != nil { log.Errorf("host %s(%s): alloc vpc mapped addr: %v", @@ -4313,6 +4324,7 @@ func (hh *SHost) PostCreate( } if _, err := db.Update(hh, func() error { hh.OvnMappedIpAddr = addr + hh.OvnMappedIp6Addr = api.GenVpcMappedIP6(addr) return nil }); err != nil { log.Errorf("host %s(%s): db update vpc mapped addr: %v", @@ -4722,6 +4734,7 @@ func (hh *SHost) PostUpdate(ctx context.Context, userCred mcclient.TokenCredenti if hh.OvnVersion != "" && hh.OvnMappedIpAddr == "" { HostManager.lockAllocOvnMappedIpAddr(ctx) defer HostManager.unlockAllocOvnMappedIpAddr(ctx) + addr, err := HostManager.allocOvnMappedIpAddr(ctx) if err != nil { log.Errorf("host %s(%s): alloc vpc mapped addr: %v", @@ -4730,6 +4743,7 @@ func (hh *SHost) PostUpdate(ctx context.Context, userCred mcclient.TokenCredenti } if _, err := db.Update(hh, func() error { hh.OvnMappedIpAddr = addr + hh.OvnMappedIp6Addr = api.GenVpcMappedIP6(addr) return nil }); err != nil { log.Errorf("host %s(%s): db update vpc mapped addr: %v", @@ -5590,6 +5604,10 @@ func (h *SHost) PerformAddNetif( if len(ipAddr) > 0 && !regutils.MatchIP4Addr(ipAddr) { return nil, errors.Wrapf(httperrors.ErrInputParameter, "invalid ip_addr %s", ipAddr) } + ip6Addr := input.Ip6Addr + if len(ip6Addr) > 0 && !regutils.MatchIP6Addr(ip6Addr) { + return nil, errors.Wrapf(httperrors.ErrInputParameter, "invalid ip6_addr %s", ip6Addr) + } rate := input.Rate nicType := input.NicType index := input.Index @@ -5600,6 +5618,8 @@ func (h *SHost) PerformAddNetif( bridge := input.Bridge reserve := (input.Reserve != nil && *input.Reserve) requireDesignatedIp := (input.RequireDesignatedIp != nil && *input.RequireDesignatedIp) + requireIpv6 := (input.RequireIpv6 != nil && *input.RequireIpv6) + strictIpv6 := (input.StrictIpv6 != nil && *input.StrictIpv6) isLinkUp := tristate.None if linkUp != "" { @@ -5610,16 +5630,16 @@ func (h *SHost) PerformAddNetif( } } - err = h.addNetif(ctx, userCred, mac, vlan, wire, ipAddr, int(rate), nicType, index, isLinkUp, - int16(mtu), reset, netIf, bridge, reserve, requireDesignatedIp) + err = h.addNetif(ctx, userCred, mac, vlan, wire, ipAddr, ip6Addr, int(rate), nicType, index, isLinkUp, + int16(mtu), reset, netIf, bridge, reserve, requireDesignatedIp, requireIpv6, strictIpv6) return nil, errors.Wrap(err, "addNetif") } func (h *SHost) addNetif(ctx context.Context, userCred mcclient.TokenCredential, - mac string, vlanId int, wire string, ipAddr string, + mac string, vlanId int, wire string, ipAddr string, ip6Addr string, rate int, nicType compute.TNicType, index int, linkUp tristate.TriState, mtu int16, reset bool, strInterface *string, strBridge *string, - reserve bool, requireDesignatedIp bool, + reserve bool, requireDesignatedIp bool, requireIpv6 bool, strictIpv6 bool, ) error { var sw *SWire if len(wire) > 0 { @@ -5632,32 +5652,78 @@ func (h *SHost) addNetif(ctx context.Context, userCred mcclient.TokenCredential, } } sw = iWire.(*SWire) - if len(ipAddr) > 0 { - iIpAddr, err := netutils.NewIPV4Addr(ipAddr) - if err != nil { - return httperrors.NewInputParameterError("invalid ipaddr %s", ipAddr) + if len(ipAddr) > 0 || len(ip6Addr) > 0 { + var v4addr *netutils.IPV4Addr + var v6addr *netutils.IPV6Addr + if len(ipAddr) > 0 { + iIpAddr, err := netutils.NewIPV4Addr(ipAddr) + if err != nil { + return httperrors.NewInputParameterError("invalid ipaddr %s", ipAddr) + } + v4addr = &iIpAddr } - findAddr := false + if len(ip6Addr) > 0 { + iIp6Addr, err := netutils.NewIPV6Addr(ip6Addr) + if err != nil { + return httperrors.NewInputParameterError("invalid ip6addr %s", ip6Addr) + } + v6addr = &iIp6Addr + } + + var v4net, v6net *SNetwork swNets, err := sw.getNetworks(ctx, userCred, userCred, NetworkManager.AllowScope(userCred)) if err != nil { return httperrors.NewInputParameterError("no networks on wire %s", wire) } for i := range swNets { - if swNets[i].IsAddressInRange(iIpAddr) { - findAddr = true + if v4net == nil && v4addr != nil && swNets[i].IsAddressInRange(*v4addr) { + v4net = &swNets[i] + } + if v6net == nil && v6addr != nil && swNets[i].IsAddress6InRange(*v6addr) { + v6net = &swNets[i] + } + if v4net != nil && v6net != nil { break } } - if !findAddr { - return httperrors.NewBadRequestError("IP %s not attach to wire %s", ipAddr, wire) + if v4net == nil && v6net == nil { + var addrs []string + if len(ipAddr) > 0 { + addrs = append(addrs, ipAddr) + } + if len(ip6Addr) > 0 { + addrs = append(addrs, ip6Addr) + } + return httperrors.NewBadRequestError("IP %s not attach to wire %s", strings.Join(addrs, ","), wire) + } + if v4net != nil && v6net != nil && v4net.Id != v6net.Id { + return httperrors.NewConflictError("IPv4 %s and IPv6 %s must be on the same network", ipAddr, ip6Addr) } } - } else if len(ipAddr) > 0 && len(wire) == 0 { - ipWire, err := WireManager.GetOnPremiseWireOfIp(ipAddr) - if err != nil { - return httperrors.NewBadRequestError("IP %s not attach to any wire", ipAddr) + } else { + var v4wire, v6wire *SWire + if len(ipAddr) > 0 { + ipWire, err := WireManager.GetOnPremiseWireOfIp(ipAddr) + if err != nil { + return httperrors.NewBadRequestError("IP %s not attach to any wire", ipAddr) + } + v4wire = ipWire + } + if len(ip6Addr) > 0 { + ipWire, err := WireManager.GetOnPremiseWireOfIp6(ip6Addr) + if err != nil { + return httperrors.NewBadRequestError("IPv6 %s not attach to any wire", ip6Addr) + } + v6wire = ipWire + } + if v4wire != nil && v6wire != nil && v4wire.Id != v6wire.Id { + return httperrors.NewConflictError("IPv4 %s and IPv6 %s must be on the same wire", ipAddr, ip6Addr) + } + if v4wire != nil { + sw = v4wire + } else if v6wire != nil { + sw = v6wire } - sw = ipWire } netif, err := NetInterfaceManager.FetchByMacVlan(mac, vlanId) if err != nil { @@ -5762,8 +5828,8 @@ func (h *SHost) addNetif(ctx context.Context, userCred mcclient.TokenCredential, } } } - if len(ipAddr) > 0 { - err = h.EnableNetif(ctx, userCred, netif, "", ipAddr, "", "", reserve, requireDesignatedIp) + if len(ipAddr) > 0 || len(ip6Addr) > 0 { + err = h.EnableNetif(ctx, userCred, netif, "", ipAddr, ip6Addr, "", "", reserve, requireDesignatedIp, requireIpv6, strictIpv6) if err != nil { return httperrors.NewBadRequestError("%v", err) } @@ -5795,8 +5861,10 @@ func (h *SHost) PerformEnableNetif( reserve := (input.Reserve != nil && *input.Reserve) requireDesignatedIp := (input.RequireDesignatedIp != nil && *input.RequireDesignatedIp) + requireIpv6 := (input.RequireIpv6 != nil && *input.RequireIpv6) + strictIpv6 := (input.StrictIpv6 != nil && *input.StrictIpv6) - err = h.EnableNetif(ctx, userCred, netif, input.NetworkId, input.IpAddr, input.AllocDir, input.NetType, reserve, requireDesignatedIp) + err = h.EnableNetif(ctx, userCred, netif, input.NetworkId, input.IpAddr, input.Ip6Addr, input.AllocDir, input.NetType, reserve, requireDesignatedIp, requireIpv6, strictIpv6) if err != nil { return nil, httperrors.NewBadRequestError("%v", err) } @@ -5804,27 +5872,64 @@ func (h *SHost) PerformEnableNetif( } func (h *SHost) EnableNetif(ctx context.Context, userCred mcclient.TokenCredential, netif *SNetInterface, - network, ipAddr, allocDir string, netType api.TNetworkType, reserve, requireDesignatedIp bool) error { - bn := netif.GetHostNetwork() - if bn != nil { - log.Debugf("Netif has been attach2network? %s", jsonutils.Marshal(bn)) - return nil - } - var net *SNetwork + network, ipAddr, ip6Addr, allocDir string, netType api.TNetworkType, reserve, requireDesignatedIp bool, + requireIpv6 bool, strictIpv6 bool) error { + // bn := netif.GetHostNetwork() + // if bn != nil { + // log.Debugf("Netif has been attach2network? %s", jsonutils.Marshal(bn)) + // return nil + // } + var v4net, v6net *SNetwork var err error - if len(ipAddr) > 0 { - net, err = netif.GetCandidateNetworkForIp(ctx, userCred, userCred, NetworkManager.AllowScope(userCred), ipAddr) + if len(ipAddr) > 0 && !strictIpv6 { + net, err := netif.GetCandidateNetworkForIp(ctx, userCred, userCred, NetworkManager.AllowScope(userCred), ipAddr) if net != nil { - log.Infof("find network %s for ip %s", net.GetName(), ipAddr) + log.Infof("find network %s for ip4 %s", net.GetName(), ipAddr) + v4net = net } else if requireDesignatedIp { - log.Errorf("Cannot allocate IP %s, not reachable", ipAddr) - return fmt.Errorf("Cannot allocate IP %s, not reachable", ipAddr) + log.Errorf("Cannot allocate IP %s, not reachable: %s", ipAddr, err) + return fmt.Errorf("Cannot allocate IP %s, not reachable: %s", ipAddr, err) } else { - log.Infof("not found network with scope: %s, ip_addr: %s", NetworkManager.AllowScope(userCred), ipAddr) + log.Infof("not found network with scope: %s, ip_addr: %s, err: %s", NetworkManager.AllowScope(userCred), ipAddr, err) // the ipaddr is not usable, should be reset to empty ipAddr = "" } } + if len(ip6Addr) > 0 { + if v4net != nil { + ip6, err := netutils.NewIPV6Addr(ip6Addr) + if err != nil { + return errors.Wrapf(err, "netutils.NewIPV6Addr: %s", ip6Addr) + } + if v4net.IsAddress6InRange(ip6) { + v6net = v4net + } + } else { + net, err := netif.GetCandidateNetworkForIp6(ctx, userCred, userCred, NetworkManager.AllowScope(userCred), ip6Addr) + if net != nil { + log.Infof("find network %s for ip %s", net.GetName(), ip6Addr) + v6net = net + } else if requireIpv6 { + log.Errorf("Cannot allocate IPv6 %s, not reachable: %s", ip6Addr, err) + return fmt.Errorf("Cannot allocate IPv6 %s, not reachable: %s", ip6Addr, err) + } else { + log.Infof("not found network with scope: %s, ip6_addr: %s, err: %s", NetworkManager.AllowScope(userCred), ip6Addr, err) + // the ipaddr is not usable, should be reset to empty + ip6Addr = "" + } + } + } + var net *SNetwork + if v4net != nil && v6net != nil { + if v4net.Id != v6net.Id { + return errors.Wrap(httperrors.ErrConflict, "v4net and v6net must be on the same network") + } + net = v4net + } else if v6net != nil { + net = v6net + } else if v4net != nil { + net = v4net + } wire := netif.GetWire() if wire == nil { return fmt.Errorf("No wire attached") @@ -5879,20 +5984,28 @@ func (h *SHost) EnableNetif(ctx context.Context, userCred mcclient.TokenCredenti allocDir: allocDir, reserved: reserve, requireDesignatedIp: requireDesignatedIp, + + ip6Addr: ip6Addr, + requireIpv6: requireIpv6, + strictIpv6: strictIpv6, } - bn, err = h.Attach2Network(ctx, userCred, attachOpt) + bn, err := h.attach2Network(ctx, userCred, attachOpt) if err != nil { return errors.Wrap(err, "hh.Attach2Network") } + bnIP := bn.IpAddr + if len(bnIP) == 0 { + bnIP = bn.Ip6Addr + } switch netif.NicType { case api.NIC_TYPE_IPMI: - err = h.setIpmiIp(userCred, bn.IpAddr) + err = h.setIpmiIp(userCred, bnIP) if err != nil { return errors.Wrap(err, "setIpmiIp") } case api.NIC_TYPE_ADMIN: - err = h.setAccessIp(userCred, bn.IpAddr) + err = h.setAccessIp(userCred, bnIP) if err != nil { return errors.Wrap(err, "setAccessIp") } @@ -5928,20 +6041,21 @@ func (hh *SHost) PerformDisableNetif( */ func (hh *SHost) DisableNetif(ctx context.Context, userCred mcclient.TokenCredential, netif *SNetInterface, reserve bool) error { bn := netif.GetHostNetwork() - var ipAddr string + var ipAddr, ip6Addr string if bn != nil { ipAddr = bn.IpAddr + ip6Addr = bn.Ip6Addr hh.UpdateDnsRecord(netif, false) hh.DeleteBaremetalnetwork(ctx, userCred, bn, reserve) } var err error switch netif.NicType { case api.NIC_TYPE_IPMI: - if ipAddr == hh.IpmiIp { + if ipAddr == hh.IpmiIp || ip6Addr == hh.IpmiIp { err = hh.setIpmiIp(userCred, "") } case api.NIC_TYPE_ADMIN: - if ipAddr == hh.AccessIp { + if ipAddr == hh.AccessIp || ip6Addr == hh.AccessIp { err = hh.setAccessIp(userCred, "") } } @@ -5955,16 +6069,21 @@ type hostAttachNetworkOption struct { allocDir string reserved bool requireDesignatedIp bool + + ip6Addr string + requireIpv6 bool + strictIpv6 bool } -func (hh *SHost) IsIpAddrWithinConvertedGuest(ctx context.Context, userCred mcclient.TokenCredential, ipAddr string, netif *SNetInterface) error { +func (hh *SHost) IsIpAddrWithinConvertedGuest(ctx context.Context, userCred mcclient.TokenCredential, ipAddr, ip6Addr string, netif *SNetInterface) error { if !hh.IsBaremetal { return httperrors.NewNotAcceptableError("Not a baremetal") } - if hh.HostType == api.HOST_TYPE_KVM { - return httperrors.NewNotAcceptableError("Not being convert to hypervisor") - } + // ? + // if hh.HostType == api.HOST_TYPE_KVM { + // return httperrors.NewNotAcceptableError("Not being convert to hypervisor") + // } bmServer := hh.GetBaremetalServer() if bmServer == nil { @@ -5987,59 +6106,118 @@ func (hh *SHost) IsIpAddrWithinConvertedGuest(ctx context.Context, userCred mccl return httperrors.NewNotFoundError("Not found guest nic by mac %s", netif.Mac) } - if findNic.IpAddr != ipAddr { + if len(ipAddr) > 0 && findNic.IpAddr != ipAddr { return httperrors.NewNotAcceptableError("Guest nic ip addr %s not equal %s", findNic.IpAddr, ipAddr) } + if len(ip6Addr) > 0 && findNic.Ip6Addr != ip6Addr { + return httperrors.NewNotAcceptableError("Guest nic ip addr6 %s not equal %s", findNic.Ip6Addr, ip6Addr) + } return nil } -func (hh *SHost) Attach2Network( +func (hh *SHost) attach2Network( ctx context.Context, userCred mcclient.TokenCredential, opt *hostAttachNetworkOption, ) (*SHostnetwork, error) { + log.Infof("host attach2Network: %s", jsonutils.Marshal(opt)) + netif := opt.netif net := opt.net ipAddr := opt.ipAddr + ip6Addr := opt.ip6Addr allocDir := opt.allocDir reserved := opt.reserved requireDesignatedIp := opt.requireDesignatedIp + bn := opt.netif.GetHostNetwork() + lockman.LockObject(ctx, net) defer lockman.ReleaseObject(ctx, net) - usedAddrs := net.GetUsedAddresses(ctx) - if ipAddr != "" { - // converted baremetal can resuse related guest network ip - if err := hh.IsIpAddrWithinConvertedGuest(ctx, userCred, ipAddr, netif); err == nil { - // force remove used server addr for reuse - delete(usedAddrs, ipAddr) - } else { - log.Warningf("check IsIpAddrWithinConvertedGuest: %v", err) + var freeIp4, freeIp6 string + if (!opt.strictIpv6 || len(ipAddr) > 0) && (bn == nil || bn.IpAddr != ipAddr) { + // allocate ipv4 address + usedAddrs := net.GetUsedAddresses(ctx) + if ipAddr != "" { + // converted baremetal can resuse related guest network ip + if err := hh.IsIpAddrWithinConvertedGuest(ctx, userCred, ipAddr, "", netif); err == nil { + // force remove used server addr for reuse + delete(usedAddrs, ipAddr) + } else { + log.Warningf("check IsIpAddrWithinConvertedGuest: %v", err) + } } + freeIp, err := net.GetFreeIP(ctx, userCred, usedAddrs, nil, ipAddr, api.IPAllocationDirection(allocDir), reserved, api.AddressTypeIPv4) + if err != nil { + return nil, errors.Wrap(err, "net.GetFreeIPv4") + } + if len(ipAddr) > 0 && ipAddr != freeIp && requireDesignatedIp { + return nil, fmt.Errorf("IPv4 address %s is occupied, get %s instead", ipAddr, freeIp) + } + freeIp4 = freeIp + } + if (opt.requireIpv6 || len(ip6Addr) > 0) && (bn == nil || bn.Ip6Addr != ip6Addr) { + usedAddrs6 := net.GetUsedAddresses6(ctx) + if ip6Addr != "" { + // converted baremetal can resuse related guest network ip + if err := hh.IsIpAddrWithinConvertedGuest(ctx, userCred, "", ip6Addr, netif); err == nil { + // force remove used server addr for reuse + delete(usedAddrs6, ip6Addr) + } else { + log.Warningf("check IsIpAddrWithinConvertedGuest: %v", err) + } + } + freeIp, err := net.GetFreeIP(ctx, userCred, usedAddrs6, nil, ip6Addr, api.IPAllocationDirection(allocDir), reserved, api.AddressTypeIPv6) + if err != nil { + return nil, errors.Wrap(err, "net.GetFreeIPv6") + } + if len(ip6Addr) > 0 && ip6Addr != freeIp && requireDesignatedIp { + return nil, fmt.Errorf("IPv6 address %s is occupied, get %s instead", ip6Addr, freeIp) + } + freeIp6 = freeIp } - freeIp, err := net.GetFreeIP(ctx, userCred, usedAddrs, nil, ipAddr, api.IPAllocationDirection(allocDir), reserved, api.AddressTypeIPv4) - if err != nil { - return nil, errors.Wrap(err, "net.GetFreeIP") + if bn == nil { + bn = &SHostnetwork{} + bn.SetModelManager(HostnetworkManager, bn) + bn.BaremetalId = hh.Id + bn.NetworkId = net.Id + bn.MacAddr = netif.Mac + bn.IpAddr = freeIp4 + bn.Ip6Addr = freeIp6 + err := HostnetworkManager.TableSpec().Insert(ctx, bn) + if err != nil { + return nil, errors.Wrap(err, "HostnetworkManager.TableSpec().Insert") + } + } else if (freeIp4 != "" && freeIp4 != bn.IpAddr) || (freeIp6 != "" && freeIp6 != bn.Ip6Addr) { + _, err := db.Update(bn, func() error { + if freeIp4 != "" { + bn.IpAddr = freeIp4 + } + if freeIp6 != "" { + bn.Ip6Addr = freeIp6 + } + return nil + }) + if err != nil { + return nil, errors.Wrap(err, "HostnetworkManager.TableSpec().Insert") + } + } else { + return bn, nil } - if len(ipAddr) > 0 && ipAddr != freeIp && requireDesignatedIp { - return nil, fmt.Errorf("IP address %s is occupied, get %s instead", ipAddr, freeIp) + + var addrs []string + if len(freeIp4) > 0 { + addrs = append(addrs, freeIp4) } - bn := &SHostnetwork{} - bn.BaremetalId = hh.Id - bn.SetModelManager(HostnetworkManager, bn) - bn.NetworkId = net.Id - bn.IpAddr = freeIp - bn.MacAddr = netif.Mac - err = HostnetworkManager.TableSpec().Insert(ctx, bn) - if err != nil { - return nil, errors.Wrap(err, "HostnetworkManager.TableSpec().Insert") + if len(freeIp6) > 0 { + addrs = append(addrs, freeIp6) } - db.OpsLog.LogAttachEvent(ctx, hh, net, userCred, jsonutils.NewString(freeIp)) + db.OpsLog.LogAttachEvent(ctx, hh, net, userCred, jsonutils.NewString(strings.Join(addrs, ","))) hh.UpdateDnsRecord(netif, true) - net.UpdateBaremetalNetmap(bn, hh.GetNetifName(netif)) + // net.UpdateBaremetalNetmap(bn, hh.GetNetifName(netif)) return bn, nil } @@ -6587,7 +6765,7 @@ func (host *SHost) SyncHostExternalNics(ctx context.Context, userCred mcclient.T netif := host.GetNetInterface(enables[i].GetMac(), enables[i].GetVlanId()) // always true reserved address pool log.Debugf("enable netif %s", enables[i].GetMac()) - err = host.EnableNetif(ctx, userCred, netif, "", enables[i].GetIpAddr(), "", "", true, true) + err = host.EnableNetif(ctx, userCred, netif, "", enables[i].GetIpAddr(), "", "", "", true, true, false, false) if err != nil { result.AddError(err) } else { @@ -6618,9 +6796,9 @@ func (host *SHost) SyncHostExternalNics(ctx context.Context, userCred mcclient.T wireId = wire.Id } } - err = host.addNetif(ctx, userCred, extNic.GetMac(), extNic.GetVlanId(), wireId, extNic.GetIpAddr(), 0, + err = host.addNetif(ctx, userCred, extNic.GetMac(), extNic.GetVlanId(), wireId, extNic.GetIpAddr(), "", 0, compute.TNicType(extNic.GetNicType()), int(extNic.GetIndex()), - extNic.IsLinkUp(), int16(extNic.GetMtu()), false, strNetIf, strBridge, true, true) + extNic.IsLinkUp(), int16(extNic.GetMtu()), false, strNetIf, strBridge, true, true, false, false) if err != nil { result.AddError(err) } else { @@ -7499,8 +7677,54 @@ func (manager *SHostManager) initHostname() error { return nil } +func (manager *SHostManager) initOvnMappedIp6Addr() error { + hosts := []SHost{} + q := manager.Query().IsNotEmpty("ovn_version") + q = q.Filter( + sqlchemy.OR( + sqlchemy.IsNullOrEmpty(q.Field("ovn_mapped_ip6_addr")), + sqlchemy.IsNullOrEmpty(q.Field("ovn_mapped_ip_addr")), + ), + ) + err := db.FetchModelObjects(manager, q, &hosts) + if err != nil { + return errors.Wrapf(err, "db.FetchModelObjects") + } + for i := range hosts { + hh := &hosts[i] + var v4addr string + if hh.OvnMappedIp6Addr == "" { + addr, err := HostManager.allocOvnMappedIpAddr(context.Background()) + if err != nil { + return errors.Wrapf(err, "host %s(%s): alloc vpc mapped addr", hh.Name, hh.Id) + } + v4addr = addr + } else { + v4addr = hh.OvnMappedIpAddr + } + + if _, err := db.Update(hh, func() error { + hh.OvnMappedIpAddr = v4addr + hh.OvnMappedIp6Addr = api.GenVpcMappedIP6(v4addr) + return nil + }); err != nil { + return errors.Wrapf(err, "host %s(%s): db update vpc mapped addr", hh.Name, hh.Id) + } + } + return nil +} + func (manager *SHostManager) InitializeData() error { - return manager.initHostname() + var err error + err = manager.initHostname() + if err != nil { + return errors.Wrapf(err, "initHostname") + } + err = manager.initOvnMappedIp6Addr() + if err != nil { + return errors.Wrapf(err, "initOvnMappedIp6Addr") + } + return nil } func (hh *SHost) PerformProbeIsolatedDevices(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) { diff --git a/pkg/compute/models/initdb.go b/pkg/compute/models/initdb.go index c0c6b53cbe..9d1b6dcddf 100644 --- a/pkg/compute/models/initdb.go +++ b/pkg/compute/models/initdb.go @@ -50,6 +50,7 @@ func InitDB() error { NetworkAddressManager, NetworkIpMacManager, GuestManager, + GuestnetworkManager, HostManager, LoadbalancerCertificateManager, LoadbalancerAclManager, diff --git a/pkg/compute/models/netinterfaces.go b/pkg/compute/models/netinterfaces.go index 84b8dc9b1c..41f6c9c827 100644 --- a/pkg/compute/models/netinterfaces.go +++ b/pkg/compute/models/netinterfaces.go @@ -169,10 +169,13 @@ func (netIf *SNetInterface) hostNetworkToJson(bn *SHostnetwork) *jsonutils.JSOND } }*/ -func (netIf *SNetInterface) networkToNic(ipAddr string, network *SNetwork, nic *types.SNic) *types.SNic { +func (netIf *SNetInterface) networkToNic(ipAddr, ip6Addr string, network *SNetwork, nic *types.SNic) *types.SNic { if len(ipAddr) > 0 { nic.IpAddr = ipAddr } + if len(ip6Addr) > 0 { + nic.Ip6Addr = ip6Addr + } if network != nil { if len(network.GuestGateway) > 0 && regutils.MatchIP4Addr(network.GuestGateway) { @@ -190,6 +193,11 @@ func (netIf *SNetInterface) networkToNic(ipAddr string, network *SNetwork, nic * nic.Masklen = network.GuestIpMask nic.Net = network.Name nic.NetId = network.Id + + nic.Masklen6 = network.GuestIp6Mask + if len(network.GuestGateway6) > 0 && regutils.MatchIP6Addr(network.GuestGateway6) { + nic.Gateway6 = network.GuestGateway6 + } } return nic @@ -256,7 +264,7 @@ func (netif *SNetInterface) getBaremetalJsonDesc() *types.SNic { } bn := netif.GetHostNetwork() if bn != nil { - nic = netif.networkToNic(bn.IpAddr, bn.GetNetwork(), nic) + nic = netif.networkToNic(bn.IpAddr, bn.Ip6Addr, bn.GetNetwork(), nic) } return nic } @@ -290,6 +298,15 @@ func (netIf *SNetInterface) GetCandidateNetworkForIp(ctx context.Context, userCr return wire.GetCandidateNetworkForIp(ctx, userCred, ownerId, scope, ipAddr) } +func (netIf *SNetInterface) GetCandidateNetworkForIp6(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, scope rbacscope.TRbacScope, ip6Addr string) (*SNetwork, error) { + wire := netIf.GetWire() + if wire == nil { + return nil, nil + } + log.Infof("ip6Addr: %s, netiName: %s, wire: %s", ip6Addr, netIf.GetName(), wire.GetName()) + return wire.GetCandidateNetworkForIp6(ctx, userCred, ownerId, scope, ip6Addr) +} + func (netif *SNetInterface) IsUsableServernic() bool { if netif.NicType == api.NIC_TYPE_IPMI { return false diff --git a/pkg/compute/models/networks.go b/pkg/compute/models/networks.go index 2fc19facea..c5f576d5cb 100644 --- a/pkg/compute/models/networks.go +++ b/pkg/compute/models/networks.go @@ -21,7 +21,6 @@ import ( "math" "math/big" "sort" - "strconv" "strings" "time" @@ -122,9 +121,9 @@ type SNetwork struct { // IPv6网关 GuestGateway6 string `width:"64" charset:"ascii" nullable:"true" list:"user" update:"user" create:"optional"` // IPv6域名服务器 - GuestDns6 string `width:"64" charset:"ascii" nullable:"true"` + // GuestDns6 string `width:"64" charset:"ascii" nullable:"true"` - GuestDomain6 string `width:"128" charset:"ascii" nullable:"true"` + // GuestDomain6 string `width:"128" charset:"ascii" nullable:"true"` VlanId int `nullable:"false" default:"1" list:"user" update:"user" create:"optional"` @@ -582,19 +581,6 @@ func (snet *SNetwork) _updateDnsRecord(name string, ipAddr string, isAdd bool) { } } -func (snet *SNetwork) updateGuestNetmap(nic *SGuestnetwork) { - // TODO - -} - -func (snet *SNetwork) UpdateBaremetalNetmap(nic *SHostnetwork, name string) { - snet.UpdateNetmap(nic.IpAddr, auth.AdminCredential().GetTenantId(), name) -} - -func (snet *SNetwork) UpdateNetmap(ip, project, name string) { - // TODO ?? -} - type DNSUpdateKeySecret struct { Key string Secret string @@ -951,6 +937,23 @@ func (manager *SNetworkManager) GetOnPremiseNetworkOfIP(ipAddr string, serverTyp return nil, sql.ErrNoRows } +func (manager *SNetworkManager) GetOnPremiseNetworkOfIP6(ip6Addr string, serverType string, isPublic tristate.TriState) (*SNetwork, error) { + address, err := netutils.NewIPV6Addr(ip6Addr) + if err != nil { + return nil, errors.Wrap(err, "NewIPV6Addr") + } + nets, err := manager.fetchAllOnpremiseNetworks(serverType, isPublic) + if err != nil { + return nil, errors.Wrap(err, "fetchAllOnpremiseNetworks") + } + for _, n := range nets { + if n.IsAddress6InRange(address) { + return &n, nil + } + } + return nil, sql.ErrNoRows +} + func (manager *SNetworkManager) allNetworksQ(providers []string, brands []string, cloudEnv string, rangeObjs []db.IStandaloneModel) *sqlchemy.SQuery { networks := manager.Query().SubQuery() wires := WireManager.Query().SubQuery() @@ -2579,7 +2582,7 @@ func (snet *SNetwork) isOneCloudVpcNetwork() bool { return IsOneCloudVpcResource(snet) } -func parseIpToIntArray(ip string) ([]int, error) { +/*func parseIpToIntArray(ip string) ([]int, error) { ipSp := strings.Split(strings.Trim(ip, "."), ".") if len(ipSp) > 4 { return nil, httperrors.NewInputParameterError("Parse Ip Failed") @@ -2596,7 +2599,7 @@ func parseIpToIntArray(ip string) ([]int, error) { ipIa = append(ipIa, val) } return ipIa, nil -} +}*/ // IP子网列表 func (manager *SNetworkManager) ListItemFilter( diff --git a/pkg/compute/models/networks_id_change_handlers.go b/pkg/compute/models/networks_id_change_handlers.go index 97a2169c16..a493551c04 100644 --- a/pkg/compute/models/networks_id_change_handlers.go +++ b/pkg/compute/models/networks_id_change_handlers.go @@ -66,8 +66,20 @@ func (manager *SHostnetworkManager) handleNetworkIdChange(ctx context.Context, a return err } for _, hn := range hns { - addr, _ := netutils.NewIPV4Addr(hn.IpAddr) - if args.newNet.IsAddressInRange(addr) { + inNet := false + if len(hn.IpAddr) > 0 { + addr, _ := netutils.NewIPV4Addr(hn.IpAddr) + if args.newNet.IsAddressInRange(addr) { + inNet = true + } + } + if len(hn.Ip6Addr) > 0 { + addr, _ := netutils.NewIPV6Addr(hn.Ip6Addr) + if args.newNet.IsAddress6InRange(addr) { + inNet = true + } + } + if inNet { _, err = db.Update(&hn, func() error { hn.NetworkId = args.newNet.Id return nil diff --git a/pkg/compute/models/vpcs.go b/pkg/compute/models/vpcs.go index 14c21ddc43..0a4db0151f 100644 --- a/pkg/compute/models/vpcs.go +++ b/pkg/compute/models/vpcs.go @@ -1982,6 +1982,7 @@ func (svpc *SVpc) GetDetailsTopology(ctx context.Context, userCred mcclient.Toke for j := range hns { host.Networks = append(host.Networks, api.HostnetworkTopologyOutput{ IpAddr: hns[j].IpAddr, + Ip6Addr: hns[j].Ip6Addr, MacAddr: hns[j].MacAddr, }) } diff --git a/pkg/compute/models/wires.go b/pkg/compute/models/wires.go index cab821ccfa..003e7f1e9a 100644 --- a/pkg/compute/models/wires.go +++ b/pkg/compute/models/wires.go @@ -1036,7 +1036,7 @@ func (swire *SWire) GetCandidateNetworkForIp(ctx context.Context, userCred mccli } netPublics, err := swire.getPublicNetworks(ctx, userCred, ownerId, scope) if err != nil { - return nil, err + return nil, errors.Wrapf(err, "swire.getPublicNetworks %s", swire.GetId()) } for _, net := range netPublics { if net.IsAddressInRange(ip) { @@ -1046,6 +1046,32 @@ func (swire *SWire) GetCandidateNetworkForIp(ctx context.Context, userCred mccli return nil, nil } +func (swire *SWire) GetCandidateNetworkForIp6(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, scope rbacscope.TRbacScope, ip6Addr string) (*SNetwork, error) { + ip6, err := netutils.NewIPV6Addr(ip6Addr) + if err != nil { + return nil, errors.Wrapf(err, "netutils.NewIPV6Addr: %s", ip6Addr) + } + netPrivates, err := swire.getPrivateNetworks(ctx, userCred, ownerId, scope) + if err != nil { + return nil, errors.Wrapf(err, "swire.getPrivateNetworks %s", swire.GetId()) + } + for _, net := range netPrivates { + if net.IsAddress6InRange(ip6) { + return &net, nil + } + } + netPublics, err := swire.getPublicNetworks(ctx, userCred, ownerId, scope) + if err != nil { + return nil, errors.Wrapf(err, "swire.getPublicNetworks %s", swire.GetId()) + } + for _, net := range netPublics { + if net.IsAddress6InRange(ip6) { + return &net, nil + } + } + return nil, nil +} + func ChooseNetworkByAddressCount(nets []*SNetwork) (*SNetwork, *SNetwork) { return chooseNetworkByAddressCount(nets) } @@ -1332,6 +1358,19 @@ func (manager *SWireManager) GetOnPremiseWireOfIp(ipAddr string) (*SWire, error) } } +func (manager *SWireManager) GetOnPremiseWireOfIp6(ip6Addr string) (*SWire, error) { + net, err := NetworkManager.GetOnPremiseNetworkOfIP6(ip6Addr, "", tristate.None) + if err != nil { + return nil, err + } + wire, _ := net.GetWire() + if wire != nil { + return wire, nil + } else { + return nil, fmt.Errorf("Wire not found") + } +} + func (w *SWire) PerformMergeNetwork(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.WireMergeNetworkInput) (jsonutils.JSONObject, error) { return nil, w.StartMergeNetwork(ctx, userCred, "") } @@ -1855,6 +1894,7 @@ func (swire *SWire) GetDetailsTopology(ctx context.Context, userCred mcclient.To for j := range hns { host.Networks = append(host.Networks, api.HostnetworkTopologyOutput{ IpAddr: hns[j].IpAddr, + Ip6Addr: hns[j].Ip6Addr, MacAddr: hns[j].MacAddr, }) } diff --git a/pkg/hostman/hostinfo/hostinfo.go b/pkg/hostman/hostinfo/hostinfo.go index 2dfdaa104e..e426a4e09c 100644 --- a/pkg/hostman/hostinfo/hostinfo.go +++ b/pkg/hostman/hostinfo/hostinfo.go @@ -36,6 +36,7 @@ import ( "yunion.io/x/jsonutils" "yunion.io/x/log" "yunion.io/x/pkg/errors" + "yunion.io/x/pkg/util/regutils" "yunion.io/x/pkg/util/version" "yunion.io/x/pkg/utils" @@ -1588,7 +1589,7 @@ func (h *SHostInfo) updateOrCreateHost(hostId string) (*api.HostDetails, error) if options.HostOptions.EnableSsl { schema = "https" } - if strings.Contains(masterIp, ":") { + if regutils.MatchIP6Addr(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) @@ -1906,7 +1907,7 @@ func (h *SHostInfo) uploadNetworkInfo() error { func (h *SHostInfo) doSendPhysicalNicInfo(nic *types.SNicDevInfo) error { log.Infof("upload physical nic: %s(%s)", nic.Dev, nic.Mac) - _, err := h.doUploadNicInfoInternal(nic.Dev, nic.Mac.String(), 1, "", "", "", nic.Up != nil && *nic.Up) + _, err := h.doUploadNicInfoInternal(nic.Dev, nic.Mac.String(), 1, "", "", "", "", nic.Up != nil && *nic.Up) if err != nil { return errors.Wrap(err, "doUploadNicInfoInternal") } @@ -1914,15 +1915,15 @@ func (h *SHostInfo) doSendPhysicalNicInfo(nic *types.SNicDevInfo) error { } func (h *SHostInfo) doUploadNicInfo(nic *SNIC) (*api.HostDetails, error) { - hostDetails, err := h.doUploadNicInfoInternal(nic.Inter, nic.BridgeDev.GetMac(), nic.BridgeDev.GetVlanId(), nic.Wire, nic.Bridge, nic.Ip, true) + hostDetails, err := h.doUploadNicInfoInternal(nic.Inter, nic.BridgeDev.GetMac(), nic.BridgeDev.GetVlanId(), nic.Wire, nic.Bridge, nic.Ip, nic.Ip6, true) if err != nil { return nil, errors.Wrap(err, "doUploadNicInfoInternal") } return hostDetails, nil } -func (h *SHostInfo) doUploadNicInfoInternal(ifname, mac string, vlanId int, wire, bridge, ipaddr string, isUp bool) (*api.HostDetails, error) { - log.Infof("Upload NIC br:%s if:%s", bridge, ifname) +func (h *SHostInfo) doUploadNicInfoInternal(ifname, mac string, vlanId int, wire, bridge, ipaddr, ip6addr string, isUp bool) (*api.HostDetails, error) { + log.Infof("Upload NIC br:%s if:%s ip:%s ip6:%s", bridge, ifname, ipaddr, ip6addr) content := jsonutils.NewDict() content.Set("mac", jsonutils.NewString(mac)) content.Set("vlan_id", jsonutils.NewInt(int64(vlanId))) @@ -1942,6 +1943,14 @@ func (h *SHostInfo) doUploadNicInfoInternal(ifname, mac string, vlanId int, wire // always try to allocate from reserved pool content.Set("reserve", jsonutils.JSONTrue) } + if len(ip6addr) > 0 { + content.Set("ip6_addr", jsonutils.NewString(ip6addr)) + if ip6addr == h.GetMasterIp() { + content.Set("nic_type", jsonutils.NewString(string(api.NIC_TYPE_ADMIN))) + } + // always try to allocate from reserved pool + content.Set("reserve", jsonutils.JSONTrue) + } res, err := modules.Hosts.PerformAction(h.GetSession(), h.HostId, "add-netif", content) if err != nil { return nil, errors.Wrapf(err, "modules.Hosts.PerformAction add-netif: %s", content.String()) diff --git a/pkg/mcclient/modules/compute/mod_baremetalnetworks.go b/pkg/mcclient/modules/compute/mod_baremetalnetworks.go index 2db0cba921..1ce4dc0bb6 100644 --- a/pkg/mcclient/modules/compute/mod_baremetalnetworks.go +++ b/pkg/mcclient/modules/compute/mod_baremetalnetworks.go @@ -28,7 +28,7 @@ func init() { "baremetalnetwork", "baremetalnetworks", []string{"Baremetal_ID", "Host", - "Network_ID", "Network", "IP_addr", "Mac_addr", + "Network_ID", "Network", "IP_addr", "IP6_addr", "Mac_addr", "Nic_Type"}, []string{}, &Hosts, diff --git a/pkg/mcclient/modules/compute/mod_hosts.go b/pkg/mcclient/modules/compute/mod_hosts.go index cb507ed141..772de87495 100644 --- a/pkg/mcclient/modules/compute/mod_hosts.go +++ b/pkg/mcclient/modules/compute/mod_hosts.go @@ -167,7 +167,8 @@ var ( func init() { Hosts = HostManager{modules.NewComputeManager("host", "hosts", - []string{"ID", "Name", "Access_mac", "Access_ip", "Ipmi_Ip", "Ovn_Mapped_Ip_Addr", + []string{"ID", "Name", "Access_mac", "Access_ip", "Ipmi_Ip", + "Ovn_Mapped_Ip_Addr", "Ovn_Mapped_Ip6_Addr", "Manager_URI", "Status", "enabled", "host_status", "Guests", "Running_guests", diff --git a/pkg/util/ssh/ssh.go b/pkg/util/ssh/ssh.go index 2bb653707e..54633af5bd 100644 --- a/pkg/util/ssh/ssh.go +++ b/pkg/util/ssh/ssh.go @@ -21,6 +21,7 @@ import ( "io" "net" "os" + "strconv" "strings" "time" @@ -75,7 +76,7 @@ func (conf ClientConfig) Connect() (*ssh.Client, error) { if err != nil { return nil, err } - addr := fmt.Sprintf("%s:%d", conf.Host, conf.Port) + addr := net.JoinHostPort(conf.Host, strconv.Itoa(conf.Port)) client, err := ssh.Dial("tcp", addr, cliConfig) if err != nil { return nil, err @@ -89,7 +90,7 @@ func (conf ClientConfig) ConnectContext(ctx context.Context) (*ssh.Client, error return nil, err } - addr := fmt.Sprintf("%s:%d", conf.Host, conf.Port) + addr := net.JoinHostPort(conf.Host, strconv.Itoa(conf.Port)) d := &net.Dialer{} netconn, err := d.DialContext(ctx, "tcp", addr) if err != nil { diff --git a/pkg/vpcagent/ovn/keeper.go b/pkg/vpcagent/ovn/keeper.go index 6d2c2484fe..a1f81c955d 100644 --- a/pkg/vpcagent/ovn/keeper.go +++ b/pkg/vpcagent/ovn/keeper.go @@ -122,9 +122,12 @@ func (keeper *OVNNorthboundKeeper) ClaimVpc(ctx context.Context, vpc *agentmodel Name: vpcExtLsName(vpc.Id), } vpcR1extp = &ovn_nb.LogicalRouterPort{ - Name: vpcR1extpName(vpc.Id), - Mac: apis.VpcInterExtMac1, - Networks: []string{fmt.Sprintf("%s/%d", apis.VpcInterExtIP1(), apis.VpcInterExtMask)}, + Name: vpcR1extpName(vpc.Id), + Mac: apis.VpcInterExtMac1, + Networks: []string{ + fmt.Sprintf("%s/%d", apis.VpcInterExtIP1(), apis.VpcInterExtMask), + fmt.Sprintf("%s/%d", apis.VpcInterExtIP16(), apis.VpcInterExtMask6), + }, } vpcExtr1p = &ovn_nb.LogicalSwitchPort{ Name: vpcExtr1pName(vpc.Id), @@ -135,9 +138,12 @@ func (keeper *OVNNorthboundKeeper) ClaimVpc(ctx context.Context, vpc *agentmodel }, } vpcR2extp = &ovn_nb.LogicalRouterPort{ - Name: vpcR2extpName(vpc.Id), - Mac: apis.VpcInterExtMac2, - Networks: []string{fmt.Sprintf("%s/%d", apis.VpcInterExtIP2(), apis.VpcInterExtMask)}, + Name: vpcR2extpName(vpc.Id), + Mac: apis.VpcInterExtMac2, + Networks: []string{ + fmt.Sprintf("%s/%d", apis.VpcInterExtIP2(), apis.VpcInterExtMask), + fmt.Sprintf("%s/%d", apis.VpcInterExtIP26(), apis.VpcInterExtMask6), + }, } vpcExtr2p = &ovn_nb.LogicalSwitchPort{ Name: vpcExtr2pName(vpc.Id), @@ -175,9 +181,12 @@ func (keeper *OVNNorthboundKeeper) ClaimVpc(ctx context.Context, vpc *agentmodel Name: vpcHostLsName(vpc.Id), } vpcRhp = &ovn_nb.LogicalRouterPort{ - Name: vpcRhpName(vpc.Id), - Mac: apis.VpcMappedGatewayMac, - Networks: []string{fmt.Sprintf("%s/%d", apis.VpcMappedGatewayIP(), apis.VpcMappedIPMask)}, + Name: vpcRhpName(vpc.Id), + Mac: apis.VpcMappedGatewayMac, + Networks: []string{ + fmt.Sprintf("%s/%d", apis.VpcMappedGatewayIP(), apis.VpcMappedIPMask), + fmt.Sprintf("%s/%d", apis.VpcMappedGatewayIP6(), apis.VpcMappedIPMask6), + }, } vpcHrp = &ovn_nb.LogicalSwitchPort{ Name: vpcHrpName(vpc.Id), @@ -205,9 +214,12 @@ func (keeper *OVNNorthboundKeeper) ClaimVpc(ctx context.Context, vpc *agentmodel Name: vpcEipLsName(vpc.Id), } vpcRep = &ovn_nb.LogicalRouterPort{ - Name: vpcRepName(vpc.Id), - Mac: apis.VpcEipGatewayMac, - Networks: []string{fmt.Sprintf("%s/%d", apis.VpcEipGatewayIP(), apis.VpcEipGatewayIPMask)}, + Name: vpcRepName(vpc.Id), + Mac: apis.VpcEipGatewayMac, + Networks: []string{ + fmt.Sprintf("%s/%d", apis.VpcEipGatewayIP(), apis.VpcEipGatewayIPMask), + fmt.Sprintf("%s/%d", apis.VpcEipGatewayIP6(), apis.VpcEipGatewayIPMask6), + }, } vpcErp = &ovn_nb.LogicalSwitchPort{ Name: vpcErpName(vpc.Id), @@ -265,7 +277,7 @@ func (keeper *OVNNorthboundKeeper) ClaimNetwork(ctx context.Context, network *ag vpc = network.Vpc rpMac = mac.HashSubnetRouterPortMac(network.Id) mdMac = mac.HashSubnetMetadataMac(network.Id) - mdIp = "169.254.169.254" + // mdIp = "169.254.169.254" ) netLs := &ovn_nb.LogicalSwitch{ Name: netLsName(network.Id), @@ -275,16 +287,22 @@ func (keeper *OVNNorthboundKeeper) ClaimNetwork(ctx context.Context, network *ag } if len(network.GuestGateway6) > 0 && network.GuestIp6Mask > 0 { networks = append(networks, fmt.Sprintf("%s/%d", network.GuestGateway6, network.GuestIp6Mask)) + for _, ip6 := range opts.MetadataServerIp6s { + networks = append(networks, fmt.Sprintf("%s/128", ip6)) + } } netRnp := &ovn_nb.LogicalRouterPort{ Name: netRnpName(network.Id), Mac: rpMac, Networks: networks, } + mtu := opts.OvnUnderlayMtu + mtu -= apis.VPC_OVN_ENCAP_COST if len(network.GuestGateway6) > 0 && network.GuestIp6Mask > 0 { netRnp.Ipv6RaConfigs = map[string]string{ - "address_mode": "dhcpv6_stateful", - // "send_periodic": "false", + "address_mode": "dhcpv6_stateful", + "mtu": fmt.Sprintf("%d", mtu), + "send_periodic": "true", } } netNrp := &ovn_nb.LogicalSwitchPort{ @@ -295,10 +313,13 @@ func (keeper *OVNNorthboundKeeper) ClaimNetwork(ctx context.Context, network *ag "router-port": netRnpName(network.Id), }, } + netMdpAddrs := []string{mdMac} + netMdpAddrs = append(netMdpAddrs, opts.MetadataServerIp4s...) + netMdpAddrs = append(netMdpAddrs, opts.MetadataServerIp6s...) netMdp := &ovn_nb.LogicalSwitchPort{ Name: netMdpName(network.Id), Type: "localport", - Addresses: []string{fmt.Sprintf("%s %s", mdMac, mdIp)}, + Addresses: []string{strings.Join(netMdpAddrs, " ")}, } var ( vpcExtBackRoute *ovn_nb.LogicalRouterStaticRoute @@ -306,7 +327,7 @@ func (keeper *OVNNorthboundKeeper) ClaimNetwork(ctx context.Context, network *ag vpcExtBackRoute6 *ovn_nb.LogicalRouterStaticRoute vpcExtBackRoute6IdRef string ) - { + if len(network.GuestGateway) > 0 && network.GuestIpMask > 0 { if ipAddr, err := netutils.NewIPV4Addr(network.GuestGateway); err != nil { return errors.Wrap(err, "NewIPV4Addr GuestGateway") } else { @@ -325,7 +346,7 @@ func (keeper *OVNNorthboundKeeper) ClaimNetwork(ctx context.Context, network *ag vpcExtBackRouteIdRef = "vpcExtBackRoute" } } - /*if len(network.GuestGateway6) > 0 && network.GuestIp6Mask > 0 { + if len(network.GuestGateway6) > 0 && network.GuestIp6Mask > 0 { if ip6Addr, err := netutils.NewIPV6Addr(network.GuestGateway6); err != nil { return errors.Wrap(err, "NewIPV6Addr GuestGateway6") } else { @@ -335,7 +356,7 @@ func (keeper *OVNNorthboundKeeper) ClaimNetwork(ctx context.Context, network *ag vpcExtBackRoute6 = &ovn_nb.LogicalRouterStaticRoute{ Policy: ptr("dst-ip"), IpPrefix: netAddrCidr6, - Nexthop: apis.VpcInterExtIP1().String(), + Nexthop: apis.VpcInterExtIP16().String(), OutputPort: ptr(vpcR2extpName(vpc.Id)), ExternalIds: map[string]string{ externalKeyOcRef: ocStaticRoute6Ref, @@ -343,7 +364,7 @@ func (keeper *OVNNorthboundKeeper) ClaimNetwork(ctx context.Context, network *ag } vpcExtBackRoute6IdRef = "vpcExtBackRoute6" } - }*/ + } var ( args []string @@ -354,7 +375,9 @@ func (keeper *OVNNorthboundKeeper) ClaimNetwork(ctx context.Context, network *ag netRnp, netNrp, netMdp, - vpcExtBackRoute, + } + if vpcExtBackRoute != nil { + irows = append(irows, vpcExtBackRoute) } if vpcExtBackRoute6 != nil { irows = append(irows, vpcExtBackRoute6) @@ -368,13 +391,17 @@ func (keeper *OVNNorthboundKeeper) ClaimNetwork(ctx context.Context, network *ag args = append(args, ovnCreateArgs(netRnp, netRnp.Name)...) args = append(args, ovnCreateArgs(netNrp, netNrp.Name)...) args = append(args, ovnCreateArgs(netMdp, netMdp.Name)...) - args = append(args, ovnCreateArgs(vpcExtBackRoute, vpcExtBackRouteIdRef)...) + if vpcExtBackRoute != nil { + args = append(args, ovnCreateArgs(vpcExtBackRoute, vpcExtBackRouteIdRef)...) + } if vpcExtBackRoute6 != nil { args = append(args, ovnCreateArgs(vpcExtBackRoute6, vpcExtBackRoute6IdRef)...) } args = append(args, "--", "add", "Logical_Switch", netLs.Name, "ports", "@"+netNrp.Name, "@"+netMdp.Name) args = append(args, "--", "add", "Logical_Router", vpcLrName(vpc.Id), "ports", "@"+netRnp.Name) - args = append(args, "--", "add", "Logical_Router", vpcExtLrName(vpc.Id), "static_routes", "@"+vpcExtBackRouteIdRef) + if vpcExtBackRoute != nil { + args = append(args, "--", "add", "Logical_Router", vpcExtLrName(vpc.Id), "static_routes", "@"+vpcExtBackRouteIdRef) + } if vpcExtBackRoute6 != nil { args = append(args, "--", "add", "Logical_Router", vpcExtLrName(vpc.Id), "static_routes", "@"+vpcExtBackRoute6IdRef) } @@ -387,7 +414,7 @@ func (keeper *OVNNorthboundKeeper) ClaimVpcHost(ctx context.Context, vpc *agentm ) vpcHostLsp := &ovn_nb.LogicalSwitchPort{ Name: vpcHostLspName(vpc.Id, host.Id), - Addresses: []string{fmt.Sprintf("%s %s", mac.HashVpcHostDistgwMac(host.Id), host.OvnMappedIpAddr)}, + Addresses: []string{fmt.Sprintf("%s %s %s", mac.HashVpcHostDistgwMac(host.Id), host.OvnMappedIpAddr, host.OvnMappedIp6Addr)}, } if m := keeper.DB.LogicalSwitchPort.FindOneMatchNonZeros(vpcHostLsp); m != nil { m.SetExternalId(externalKeyOcVersion, ocVersion) @@ -413,10 +440,11 @@ func (keeper *OVNNorthboundKeeper) ClaimVpcEipgw(ctx context.Context, vpc *agent var ( ocVersion = fmt.Sprintf("%s.%d", vpc.UpdatedAt, vpc.UpdateVersion) eipgwVip = apis.VpcEipGatewayIP3().String() + eipgwVip6 = apis.VpcEipGatewayIP63().String() ) vpcEipLsp := &ovn_nb.LogicalSwitchPort{ Name: vpcEipLspName(vpc.Id, eipgwVip), - Addresses: []string{fmt.Sprintf("%s %s", apis.VpcEipGatewayMac3, eipgwVip)}, + Addresses: []string{fmt.Sprintf("%s %s %s", apis.VpcEipGatewayMac3, eipgwVip, eipgwVip6)}, } if m := keeper.DB.LogicalSwitchPort.FindOneMatchNonZeros(vpcEipLsp); m != nil { m.SetExternalId(externalKeyOcVersion, ocVersion) @@ -457,7 +485,7 @@ func generateDhcpOptions(ctx context.Context, guestnetwork *agentmodels.Guestnet var ( network = guestnetwork.Network dhcpMac = mac.HashSubnetDhcpMac(network.Id) - mdIp = "169.254.169.254/32" + mdIp = fmt.Sprintf("%s/32", opts.MetadataServerIp4s[0]) ocDhcpRef = fmt.Sprintf("dhcp/%s/%s", guestnetwork.GuestId, guestnetwork.Ifname) ) @@ -618,6 +646,8 @@ func (keeper *OVNNorthboundKeeper) ClaimGuestnetwork(ctx context.Context, guestn ocAclRef = fmt.Sprintf("acl/%s/%s/%s", network.Id, guestnetwork.GuestId, guestnetwork.Ifname) ocQosRef = fmt.Sprintf("qos/%s/%s/%s", network.Id, guestnetwork.GuestId, guestnetwork.Ifname) ocQosEipRef = fmt.Sprintf("qos-eip/%s/%s/%s/v2", vpc.Id, guestnetwork.GuestId, guestnetwork.Ifname) + + ocGnrDefault6Ref = fmt.Sprintf("gnrDefault6/%s/%s/%s", vpc.Id, guestnetwork.GuestId, guestnetwork.Ifname) ) var ( @@ -708,10 +738,11 @@ func (keeper *OVNNorthboundKeeper) ClaimGuestnetwork(ctx context.Context, guestn } var ( - gnrDefault *ovn_nb.LogicalRouterStaticRoute - qosEipIn *ovn_nb.QoS - qosEipOut *ovn_nb.QoS - hasQoSEip bool + gnrDefault *ovn_nb.LogicalRouterStaticRoute + gnrDefault6 *ovn_nb.LogicalRouterStaticRoute + qosEipIn *ovn_nb.QoS + qosEipOut *ovn_nb.QoS + hasQoSEip bool ) { gnrDefaultPolicy := "src-ip" @@ -759,14 +790,27 @@ func (keeper *OVNNorthboundKeeper) ClaimGuestnetwork(ctx context.Context, guestn } } else if vpcHasDistgw(vpc) { - gnrDefault = &ovn_nb.LogicalRouterStaticRoute{ - Policy: &gnrDefaultPolicy, - IpPrefix: guestnetwork.IpAddr + "/32", - Nexthop: host.OvnMappedIpAddr, - OutputPort: ptr(vpcRhpName(vpc.Id)), - ExternalIds: map[string]string{ - externalKeyOcRef: ocGnrDefaultRef, - }, + if len(guestnetwork.IpAddr) > 0 { + gnrDefault = &ovn_nb.LogicalRouterStaticRoute{ + Policy: &gnrDefaultPolicy, + IpPrefix: guestnetwork.IpAddr + "/32", + Nexthop: host.OvnMappedIpAddr, + OutputPort: ptr(vpcRhpName(vpc.Id)), + ExternalIds: map[string]string{ + externalKeyOcRef: ocGnrDefaultRef, + }, + } + } + if len(guestnetwork.Ip6Addr) > 0 { + gnrDefault6 = &ovn_nb.LogicalRouterStaticRoute{ + Policy: &gnrDefaultPolicy, + IpPrefix: guestnetwork.Ip6Addr + "/32", + Nexthop: host.OvnMappedIp6Addr, + OutputPort: ptr(vpcRhpName(vpc.Id)), + ExternalIds: map[string]string{ + externalKeyOcRef: ocGnrDefault6Ref, + }, + } } } } @@ -794,7 +838,9 @@ func (keeper *OVNNorthboundKeeper) ClaimGuestnetwork(ctx context.Context, guestn irows := []types.IRow{ gnp, - dhcpOpt, + } + if len(guestnetwork.IpAddr) > 0 { + irows = append(irows, dhcpOpt) } if len(guestnetwork.Ip6Addr) > 0 { irows = append(irows, dhcp6Opt) @@ -802,6 +848,9 @@ func (keeper *OVNNorthboundKeeper) ClaimGuestnetwork(ctx context.Context, guestn if gnrDefault != nil { irows = append(irows, gnrDefault) } + if gnrDefault6 != nil { + irows = append(irows, gnrDefault6) + } for _, acl := range acls { irows = append(irows, acl) } @@ -828,6 +877,10 @@ func (keeper *OVNNorthboundKeeper) ClaimGuestnetwork(ctx context.Context, guestn args = append(args, ovnCreateArgs(gnrDefault, "gnrDefault")...) args = append(args, "--", "add", "Logical_Router", vpcExtLrName(vpc.Id), "static_routes", "@gnrDefault") } + if gnrDefault6 != nil { + args = append(args, ovnCreateArgs(gnrDefault6, "gnrDefault6")...) + args = append(args, "--", "add", "Logical_Router", vpcExtLrName(vpc.Id), "static_routes", "@gnrDefault6") + } for i, acl := range acls { ref := fmt.Sprintf("acl%d", i) args = append(args, ovnCreateArgs(acl, ref)...) diff --git a/pkg/webconsole/helper/helper.go b/pkg/webconsole/helper/helper.go index 3e5049245a..a8c05cbb21 100644 --- a/pkg/webconsole/helper/helper.go +++ b/pkg/webconsole/helper/helper.go @@ -16,7 +16,8 @@ package helper import ( "context" - "fmt" + "net" + "strconv" "time" "golang.org/x/crypto/ssh" @@ -82,7 +83,7 @@ func GetValidPrivateKey(host string, port int, username string, projectId string ssh.PublicKeys(signer), }, } - addr := fmt.Sprintf("%s:%d", host, port) + addr := net.JoinHostPort(host, strconv.Itoa(port)) client, err := ssh.Dial("tcp", addr, config) if err != nil { errs = append(errs, errors.Wrapf(err, "dial %s by %s", addr, username)) diff --git a/pkg/webconsole/service/handlers.go b/pkg/webconsole/service/handlers.go index ee1a9f7b74..1fe6f0bd43 100644 --- a/pkg/webconsole/service/handlers.go +++ b/pkg/webconsole/service/handlers.go @@ -254,6 +254,7 @@ func handleSshShell(ctx context.Context, w http.ResponseWriter, r *http.Request) } func handleSshSession(ctx context.Context, session *session.SSshSession, w http.ResponseWriter) { + log.Infof("handleSshSession %s", jsonutils.Marshal(session).String()) handleDataSession(ctx, session, w, "ws", nil, false) } diff --git a/pkg/webconsole/session/resolve_sshinfo.go b/pkg/webconsole/session/resolve_sshinfo.go index 138c61b12c..d5e9200f41 100644 --- a/pkg/webconsole/session/resolve_sshinfo.go +++ b/pkg/webconsole/session/resolve_sshinfo.go @@ -107,15 +107,13 @@ func resolveServerIPPortById(ctx context.Context, s *mcclient.ClientSession, id ip = guestNicDetails.EipAddr } else if len(guestNicDetails.IpAddr) > 0 { ip = guestNicDetails.IpAddr + } else if len(guestNicDetails.Ip6Addr) > 0 { + ip = guestNicDetails.Ip6Addr } else { return "", 0, nil, errors.Wrap(httperrors.ErrNotSupported, "no valid ipv4 addr") } } - if ip == guestNicDetails.Ip6Addr { - return "", 0, nil, errors.Wrap(httperrors.ErrNotSupported, "ipv6 not supported") - } - if ip == guestNicDetails.IpAddr && len(guestNicDetails.MappedIpAddr) > 0 { // need to do open forward ip, port, err = acquireForward(ctx, s, guestDetails.Id, ip, "tcp", port) diff --git a/pkg/webconsole/session/ssh_session.go b/pkg/webconsole/session/ssh_session.go index 87cc81d4bd..d8b32ed6b1 100644 --- a/pkg/webconsole/session/ssh_session.go +++ b/pkg/webconsole/session/ssh_session.go @@ -16,8 +16,9 @@ package session import ( "context" - "fmt" + "net" "os/exec" + "strconv" "time" "golang.org/x/crypto/ssh" @@ -111,7 +112,7 @@ func (s *SSshSession) IsNeedLogin() (bool, error) { ssh.Password(s.Password), }, } - addr := fmt.Sprintf("%s:%d", s.Host, s.Port) + addr := net.JoinHostPort(s.Host, strconv.Itoa(s.Port)) client, err := ssh.Dial("tcp", addr, config) if err != nil { return true, err