refactor(region): optimize preparenet of cloudaccount

1. 从vcenter中获取host和vm的信息时,只需要获取网络相关的字段就可以了。
2. 每个host的vms,单独启用一个goroutine。
3. 优化了处理vms ip的逻辑,现在的时间复杂度是O(n)。
This commit is contained in:
rainzm
2020-09-10 15:37:58 +08:00
parent 6ea5995242
commit ed484f4e21
4 changed files with 385 additions and 82 deletions
+156 -82
View File
@@ -49,6 +49,7 @@ import (
"yunion.io/x/onecloud/pkg/httperrors"
"yunion.io/x/onecloud/pkg/mcclient"
"yunion.io/x/onecloud/pkg/mcclient/auth"
"yunion.io/x/onecloud/pkg/multicloud/esxi"
"yunion.io/x/onecloud/pkg/multicloud/esxi/vcenter"
"yunion.io/x/onecloud/pkg/util/choices"
"yunion.io/x/onecloud/pkg/util/httputils"
@@ -418,9 +419,11 @@ func (scm *SCloudaccountManager) PerformPrepareNets(ctx context.Context, userCre
if err != nil {
return output, errors.Wrap(err, "provider.GetOnPremiseIRegion")
}
hosts, err := iregion.GetIHosts()
// hack
client := iregion.(*esxi.SESXiClient)
hostIps, simpleVms, err := client.HostVmIPs(ctx)
if err != nil {
return output, errors.Wrap(err, "unable to get hosts")
return output, errors.Wrap(err, "unable to fetch ips of hosts and vms")
}
// fetch networks
networks := make([][]SNetwork, len(wires))
@@ -431,20 +434,15 @@ func (scm *SCloudaccountManager) PerformPrepareNets(ctx context.Context, userCre
}
networks[i] = nets
}
// fetch host ips
// key of ipHosts is host's ip
ipHosts := make(map[netutils.IPV4Addr]cloudprovider.ICloudHost, len(hosts))
for i := range hosts {
ip := hosts[i].GetAccessIp()
if len(ip) == 0 {
log.Errorf("unable to get accessip of host %q", hosts[i].GetName())
continue
}
ipHosts := make(map[netutils.IPV4Addr]string, len(hostIps))
for name, ip := range hostIps {
addr, err := netutils.NewIPV4Addr(ip)
if err != nil {
return output, err
}
ipHosts[addr] = hosts[i]
ipHosts[addr] = name
}
// Find suitable wire and the network containing the Host IP in suitable wire.
var (
@@ -491,15 +489,15 @@ func (scm *SCloudaccountManager) PerformPrepareNets(ctx context.Context, userCre
// Give the suggested network configuration for the Host IP that does not have a corresponding suitable network.
noNetHostIP := make([]netutils.IPV4Addr, 0, len(ipHosts))
for hip, host := range ipHosts {
for ip, name := range ipHosts {
rnet := api.CAHostNet{
Name: host.GetName(),
IP: host.GetAccessIp(),
Name: name,
IP: ip.String(),
}
if net, ok := suitableNetworks[hip]; ok {
if net, ok := suitableNetworks[ip]; ok {
rnet.SuitableNetwork = net.GetId()
} else {
noNetHostIP = append(noNetHostIP, hip)
noNetHostIP = append(noNetHostIP, ip)
}
output.Hosts = append(output.Hosts, rnet)
}
@@ -516,7 +514,7 @@ func (scm *SCloudaccountManager) PerformPrepareNets(ctx context.Context, userCre
// Find the suitable network containing the VM IP in Project 'input.Project', and if not, give the corresponding suggested network configuration in this project.
project := input.ProjectId
nets := []*SNetwork{}
excludeNets := []*SNetwork{}
var allNets []SNetwork
if suitableWire != nil {
allNets, err = suitableWire.getNetworks(userCred, rbacutils.ScopeSystem)
@@ -525,85 +523,161 @@ func (scm *SCloudaccountManager) PerformPrepareNets(ctx context.Context, userCre
}
for i := range allNets {
if allNets[i].ProjectId == project {
nets = append(nets, &allNets[i])
excludeNets = append(excludeNets, &allNets[i])
}
}
}
// find suitable network
vms := make([]cloudprovider.ICloudVM, 0, len(hosts))
for _, host := range hosts {
vs, err := host.GetIVMs()
if err != nil {
return output, errors.Wrapf(err, "unable to get VMs of host %q", host.GetName())
}
vms = append(vms, vs...)
type vm struct {
FakeID int
IP netutils.IPV4Addr
Name string
}
ipVMs := make(map[netutils.IPV4Addr]int, len(vms))
for i, vm := range vms {
nics, err := vm.GetINics()
if err != nil {
return output, errors.Wrapf(err, "unable get nics of vm %q", vm.GetName())
}
for _, nic := range nics {
ipStr := nic.GetIP()
if len(ipStr) == 0 {
continue
}
ip, err := netutils.NewIPV4Addr(ipStr)
if err != nil {
return output, errors.Wrapf(err, "unable to new IPV4Addr for ip %q", nic.GetIP())
}
ipVMs[ip] = i
}
output.Guests = append(output.Guests, api.CAGuestNet{
Name: vm.GetName(),
IPNets: make([]api.CAIPNet, 0, 1),
})
}
suitableNetworks = make(map[netutils.IPV4Addr]*SNetwork)
if len(nets) != 0 {
for vip := range ipVMs {
for i := range nets {
ipRange := nets[i].GetIPRange()
if !ipRange.Contains(vip) {
continue
vms := make([]vm, 0, len(simpleVms))
var nip netutils.IPV4Addr
guestMap := make(map[int]*api.CAGuestNet)
for i := range simpleVms {
id := i
if len(simpleVms[i].IPs) == 0 {
if _, ok := guestMap[id]; !ok {
guestMap[id] = &api.CAGuestNet{
Name: simpleVms[i].Name,
IPNets: []api.CAIPNet{},
}
suitableNetworks[vip] = nets[i]
break
}
}
}
noNetVMIPs := make([]netutils.IPV4Addr, 0, len(ipVMs))
for vip, i := range ipVMs {
ipnet := api.CAIPNet{
IP: vip.String(),
for _, ip := range simpleVms[i].IPs {
nip, err = netutils.NewIPV4Addr(ip)
if err != nil {
return output, err
}
vms = append(vms, vm{
FakeID: i,
IP: nip,
Name: simpleVms[i].Name,
})
}
if net, ok := suitableNetworks[vip]; ok {
ipnet.SuitableNetwork = net.GetId()
} else {
noNetVMIPs = append(noNetVMIPs, vip)
}
output.Guests[i].IPNets = append(output.Guests[ipVMs[vip]].IPNets, ipnet)
}
// sort vms via vm's ip
sort.Slice(vms, func(i, j int) bool {
return vms[i].IP < vms[j].IP
})
// excludedIRs is to prevent network conflicts
excludedIRs := make([]netutils.IPV4AddrRange, len(allNets))
for i := range excludedIRs {
excludedIRs[i] = allNets[i].GetIPRange()
excludeNets = make([]*SNetwork, 0, len(allNets)+len(output.HostSuggestedNetworks))
for i := range allNets {
excludeNets = append(excludeNets, &allNets[i])
}
for i := range output.HostSuggestedNetworks {
startIPStr := output.HostSuggestedNetworks[i].GuestIpStart
endIPStr := output.HostSuggestedNetworks[i].GuestIpEnd
startIP, _ := netutils.NewIPV4Addr(startIPStr)
endIP, _ := netutils.NewIPV4Addr(endIPStr)
excludedIRs = append(excludedIRs, netutils.NewIPV4AddrRange(startIP, endIP))
startipStr := output.HostSuggestedNetworks[i].GuestIpStart
endipStr := output.HostSuggestedNetworks[i].GuestIpEnd
excludeNets = append(excludeNets, &SNetwork{GuestIpEnd: endipStr, GuestIpStart: startipStr})
}
if len(noNetVMIPs) > 0 {
sConfs := scm.suggestVMNetwors(noNetVMIPs, excludedIRs)
confs := make([]api.CANetConf, len(sConfs))
// sort excludeNets via their GuestIpStart
sort.Slice(excludeNets, func(i, j int) bool {
return excludeNets[i].GuestIpStart < excludeNets[j].GuestIpStart
})
svNets := make([]netutils.IPV4AddrRange, 0, 5)
var vmi, neti int
lastEndIp := netutils.IPV4Addr(0)
Loop:
for neti = 0; neti < len(excludeNets); {
startIp, _ := netutils.NewIPV4Addr(excludeNets[neti].GuestIpStart)
endIp, _ := netutils.NewIPV4Addr(excludeNets[neti].GuestIpEnd)
switch {
case vms[vmi].IP > endIp:
lastEndIp = endIp
neti++
case vms[vmi].IP >= startIp:
for vms[vmi].IP <= endIp {
id := vms[vmi].FakeID
if _, ok := guestMap[id]; !ok {
guestMap[id] = &api.CAGuestNet{
Name: vms[vmi].Name,
IPNets: []api.CAIPNet{},
}
}
guestMap[id].IPNets = append(guestMap[id].IPNets, api.CAIPNet{
IP: vms[vmi].IP.String(),
SuitableNetwork: excludeNets[neti].Id,
})
vmi += 1
if vmi == len(vms) {
break Loop
}
}
lastEndIp = endIp
neti++
default:
for vms[vmi].IP < startIp {
suggestStartIp := vms[vmi].IP.NetAddr(24) + 1
if suggestStartIp <= lastEndIp {
suggestStartIp = lastEndIp + 1
}
if suggestStartIp >= startIp {
break
}
suggestEndIp := suggestStartIp.NetAddr(24) + 255
if suggestEndIp >= startIp {
suggestEndIp = startIp - 1
}
svNets = append(svNets, netutils.NewIPV4AddrRange(suggestStartIp, suggestEndIp))
for vmi < len(vms) && vms[vmi].IP <= suggestEndIp {
id := vms[vmi].FakeID
if _, ok := guestMap[id]; !ok {
guestMap[id] = &api.CAGuestNet{
Name: vms[vmi].Name,
IPNets: []api.CAIPNet{},
}
}
guestMap[id].IPNets = append(guestMap[id].IPNets, api.CAIPNet{
IP: vms[vmi].IP.String(),
})
vmi++
}
if vmi == len(vms) {
break Loop
}
}
}
}
for vmi < len(vms) {
suggestStartIp := vms[vmi].IP.NetAddr(24) + 1
if suggestStartIp <= lastEndIp {
suggestStartIp = lastEndIp + 1
}
suggestEndIp := suggestStartIp.NetAddr(24) + 255
svNets = append(svNets, netutils.NewIPV4AddrRange(suggestStartIp, suggestEndIp))
for vmi < len(vms) && vms[vmi].IP <= suggestEndIp {
id := vms[vmi].FakeID
if _, ok := guestMap[id]; !ok {
guestMap[id] = &api.CAGuestNet{
Name: vms[vmi].Name,
IPNets: []api.CAIPNet{},
}
}
guestMap[id].IPNets = append(guestMap[id].IPNets, api.CAIPNet{
IP: vms[vmi].IP.String(),
})
vmi++
}
}
for _, guest := range guestMap {
output.Guests = append(output.Guests, *guest)
}
if len(svNets) > 0 {
confs := make([]api.CANetConf, len(svNets))
for i := range confs {
confs[i].CASimpleNetConf = sConfs[i]
confs[i].CASimpleNetConf = api.CASimpleNetConf{
GuestIpStart: svNets[i].StartIp().String(),
GuestIpEnd: svNets[i].EndIp().String(),
GuestIpMask: 24,
GuestGateway: (svNets[i].StartIp().NetAddr(24) + 1).String(),
}
confs[i].Name = fmt.Sprintf("%s-guest-network-%d", input.Name, i+1)
}
output.GuestSuggestedNetworks = confs
+92
View File
@@ -0,0 +1,92 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package esxi
import (
"github.com/vmware/govmomi/vim25/mo"
"yunion.io/x/pkg/errors"
"yunion.io/x/pkg/util/regutils"
)
var HOST_PROPS = []string{"name", "config.network", "vm"}
var VM_PROPS = []string{"name", "guest.net", "config.template"}
func (cli *SESXiClient) AllHostIP() (map[string]string, []mo.HostSystem, error) {
var hosts []mo.HostSystem
err := cli.scanAllMObjects(HOST_PROPS, &hosts)
if err != nil {
return nil, nil, errors.Wrap(err, "scanAllMObjects")
}
ret := make(map[string]string, len(hosts))
for i := range hosts {
// find ip
host := &SHost{SManagedObject: newManagedObject(cli, &hosts[i], nil)}
ip := host.GetAccessIp()
ret[host.GetName()] = ip
}
return ret, hosts, nil
}
func (cli *SESXiClient) VMIP(host mo.HostSystem) (map[string][]string, error) {
var vms []mo.VirtualMachine
err := cli.references2Objects(host.Vm, VM_PROPS, &vms)
if err != nil {
return nil, errors.Wrap(err, "references2Objects")
}
ret := make(map[string][]string, len(vms))
for i := range vms {
vm := vms[i]
if vm.Config.Template {
continue
}
guestIps := make([]string, 0)
for _, net := range vm.Guest.Net {
for _, ip := range net.IpAddress {
if regutils.MatchIP4Addr(ip) {
guestIps = append(guestIps, ip)
}
}
}
ret[vm.Name] = guestIps
}
return ret, nil
}
func (cli *SESXiClient) VMIP2() (map[string][]string, error) {
var vms []mo.VirtualMachine
err := cli.scanAllMObjects(VM_PROPS, &vms)
if err != nil {
return nil, errors.Wrap(err, "scanAllMObjects")
}
ret := make(map[string][]string, len(vms))
for i := range vms {
vm := vms[i]
if vm.Config.Template {
continue
}
guestIps := make([]string, 0)
for _, net := range vm.Guest.Net {
for _, ip := range net.IpAddress {
if regutils.MatchIP4Addr(ip) {
guestIps = append(guestIps, ip)
}
}
}
ret[vm.Name] = guestIps
}
return ret, nil
}
+82
View File
@@ -31,10 +31,12 @@ import (
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/soap"
"github.com/vmware/govmomi/vim25/types"
"golang.org/x/sync/errgroup"
"yunion.io/x/jsonutils"
"yunion.io/x/log"
"yunion.io/x/pkg/errors"
"yunion.io/x/pkg/util/regutils"
"yunion.io/x/pkg/utils"
api "yunion.io/x/onecloud/pkg/apis/compute"
@@ -597,3 +599,83 @@ func (cli *SESXiClient) MoveDisk(ctx context.Context, src, dst string, isForce b
}
return task.Wait(ctx)
}
var (
SIMPLE_HOST_PROPS = []string{"name", "config.network", "vm"}
SIMPLE_VM_PROPS = []string{"name", "guest.net", "config.template"}
)
type SSimpleVM struct {
Name string
IPs []string
}
func (cli *SESXiClient) HostVmIPs(ctx context.Context) (map[string]string, []SSimpleVM, error) {
var hosts []mo.HostSystem
err := cli.scanAllMObjects(SIMPLE_HOST_PROPS, &hosts)
if err != nil {
return nil, nil, errors.Wrap(err, "scanAllMObjects")
}
group, ctx := errgroup.WithContext(ctx)
collection := make([][]SSimpleVM, len(hosts))
for i := range hosts {
j := i
group.Go(func() error {
vmIps, err := cli.vmIPs(&hosts[j])
if err != nil {
return err
}
collection[j] = vmIps
return nil
})
}
hostIps := make(map[string]string, len(hosts))
for i := range hosts {
// find ip
host := &SHost{SManagedObject: newManagedObject(cli, &hosts[i], nil)}
ip := host.GetAccessIp()
hostIps[host.GetName()] = ip
}
err = group.Wait()
if err != nil {
return nil, nil, err
}
// length
length := 0
for i := range collection {
length += len(collection[i])
}
svms := make([]SSimpleVM, 0, length)
for i := range collection {
for j := range collection[i] {
svms = append(svms, collection[i][j])
}
}
return hostIps, svms, nil
}
func (cli *SESXiClient) vmIPs(host *mo.HostSystem) ([]SSimpleVM, error) {
var vms []mo.VirtualMachine
err := cli.references2Objects(host.Vm, VM_PROPS, &vms)
if err != nil {
return nil, errors.Wrap(err, "references2Objects")
}
ret := make([]SSimpleVM, 0, len(vms))
for i := range vms {
vm := vms[i]
if vm.Config.Template {
continue
}
guestIps := make([]string, 0)
for _, net := range vm.Guest.Net {
for _, ip := range net.IpAddress {
if regutils.MatchIP4Addr(ip) {
guestIps = append(guestIps, ip)
}
}
}
ret = append(ret, SSimpleVM{vm.Name, guestIps})
}
return ret, nil
}
+55
View File
@@ -0,0 +1,55 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package shell
import (
"fmt"
"yunion.io/x/onecloud/pkg/multicloud/esxi"
"yunion.io/x/onecloud/pkg/util/shellutils"
)
func init() {
type IPOption struct {
Host bool
}
shellutils.R(&IPOption{}, "ip-all", "List all ip", func(cli *esxi.SESXiClient, args *IPOption) error {
hostIps, _, err := cli.AllHostIP()
if err != nil {
return err
}
for name, ip := range hostIps {
fmt.Printf("name: %s, ip: %s\n", name, ip)
}
// for i := range hosts {
// fmt.Printf("host %s: \n", hosts[i].Name)
// vmips, err := cli.VMIP(hosts[i])
// if err != nil {
// return err
// }
// for name, ip := range vmips {
// fmt.Printf("\tname: %s, ip: %s\n", name, ip)
// }
// }
vmips, err := cli.VMIP2()
if err != nil {
return err
}
for name, ip := range vmips {
fmt.Printf("\tname: %s, ip: %s\n", name, ip)
}
return nil
})
}