diff --git a/pkg/multicloud/esxi/manager.go b/pkg/multicloud/esxi/manager.go index 43fa2157ae..cd0ca69b2e 100644 --- a/pkg/multicloud/esxi/manager.go +++ b/pkg/multicloud/esxi/manager.go @@ -19,7 +19,6 @@ import ( "fmt" "net/url" "reflect" - "sort" "strings" "sync" @@ -33,13 +32,10 @@ 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/netutils" - "yunion.io/x/pkg/util/regutils" "yunion.io/x/pkg/utils" api "yunion.io/x/onecloud/pkg/apis/compute" @@ -688,265 +684,3 @@ 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", "config.hardware.device"} - SIMPLE_DVPG_PROPS = []string{"key", "config.defaultPortConfig", "config.distributedVirtualSwitch"} - SIMPLE_NETWORK_PROPS = []string{"name"} -) - -type SIPVlan struct { - IP netutils.IPV4Addr - VlanId int32 -} - -type SSimpleVM struct { - Name string - IPVlans []SIPVlan -} - -func (cli *SESXiClient) scanAllDvPortgroups() ([]*SDistributedVirtualPortgroup, error) { - var modvpgs []mo.DistributedVirtualPortgroup - err := cli.scanAllMObjects(SIMPLE_DVPG_PROPS, &modvpgs) - if err != nil { - return nil, errors.Wrap(err, "scanAllMObjects") - } - dvpgs := make([]*SDistributedVirtualPortgroup, 0, len(modvpgs)) - for i := range modvpgs { - dvpgs = append(dvpgs, NewDistributedVirtualPortgroup(cli, &modvpgs[i], nil)) - } - return dvpgs, nil -} - -func (cli *SESXiClient) scanAllNetworks() ([]mo.Network, error) { - var monets []mo.Network - err := cli.scanAllMObjects(SIMPLE_NETWORK_PROPS, &monets) - if err != nil { - return nil, errors.Wrap(err, "scanAllMObjects") - } - return monets, nil -} - -func (cli *SESXiClient) networkName(refV string) (string, error) { - if cli.networkQueryMap == nil { - nets, err := cli.scanAllNetworks() - if err != nil { - return "", err - } - cli.networkQueryMap = &sync.Map{} - for i := range nets { - cli.networkQueryMap.Store(nets[i].Reference().Value, nets[i].Name) - } - } - iter, ok := cli.networkQueryMap.Load(refV) - if !ok { - return "", nil - } - return iter.(string), nil -} - -func (cli *SESXiClient) HostVmIPsInDc(ctx context.Context, dc *SDatacenter) (SNetworkInfo, error) { - var hosts []mo.HostSystem - err := cli.scanMObjects(dc.object.Entity().Self, HOST_SYSTEM_PROPS, &hosts) - if err != nil { - return SNetworkInfo{}, errors.Wrap(err, "scanMObjects") - } - return cli.hostVMIPs(ctx, hosts) -} - -func (cli *SESXiClient) HostVmIPsInCluster(ctx context.Context, cluster *SCluster) (SNetworkInfo, error) { - var hosts []mo.HostSystem - err := cli.scanMObjects(cluster.object.Entity().Self, HOST_SYSTEM_PROPS, &hosts) - if err != nil { - return SNetworkInfo{}, errors.Wrap(err, "scanMObjects") - } - return cli.hostVMIPs(ctx, hosts) -} - -type SNetworkInfo struct { - HostIps map[string]netutils.IPV4Addr - VMs []SSimpleVM - VlanIps map[int32][]netutils.IPV4Addr - IPPool SIPPool -} - -func (cli *SESXiClient) hostVMIPs(ctx context.Context, hosts []mo.HostSystem) (SNetworkInfo, error) { - ret := SNetworkInfo{} - dvpgMap, err := cli.getDVPGMap() - if err != nil { - return ret, errors.Wrap(err, "unable to get dvpgKeyVlanMap") - } - group, ctx := errgroup.WithContext(ctx) - collection := make([]SNetworkInfo, len(hosts)) - for i := range hosts { - j := i - group.Go(func() error { - nInfo, err := cli.vmIPs(&hosts[j], dvpgMap) - if err != nil { - return err - } - collection[j] = nInfo - return nil - }) - } - - hostIps := make(map[string]netutils.IPV4Addr, len(hosts)) - for i := range hosts { - // find ip - host := &SHost{SManagedObject: newManagedObject(cli, &hosts[i], nil)} - ipStr := host.GetAccessIp() - ip, err := netutils.NewIPV4Addr(ipStr) - if err != nil { - return ret, errors.Wrapf(err, "invalid host ip %q", ipStr) - } - hostIps[host.GetName()] = ip - } - err = group.Wait() - if err != nil { - return ret, err - } - // length - if len(collection) == 0 { - ret.HostIps = hostIps - return ret, nil - } - ni := cli.mergeNetworInfo(collection) - ni.HostIps = hostIps - return ni, nil -} - -func (cli *SESXiClient) mergeNetworInfo(nInfos []SNetworkInfo) SNetworkInfo { - var vmsLen, vlanIpLen, ipPoolLen int - for i := range nInfos { - vmsLen += len(nInfos[i].VMs) - vlanIpLen += len(nInfos[i].VlanIps) - ipPoolLen += nInfos[i].IPPool.Len() - } - ret := SNetworkInfo{ - VMs: make([]SSimpleVM, 0, vmsLen), - VlanIps: make(map[int32][]netutils.IPV4Addr, vlanIpLen), - IPPool: NewIPPool(ipPoolLen), - } - for i := range nInfos { - ret.VMs = append(ret.VMs, nInfos[i].VMs...) - for vlan, ips := range nInfos[i].VlanIps { - ret.VlanIps[vlan] = append(ret.VlanIps[vlan], ips...) - } - ret.IPPool.Merge(&nInfos[i].IPPool) - } - for _, ips := range ret.VlanIps { - sort.Slice(ips, func(i, j int) bool { - return ips[i] < ips[j] - }) - } - return ret -} - -func (cli *SESXiClient) HostVmIPs(ctx context.Context) (SNetworkInfo, error) { - var hosts []mo.HostSystem - err := cli.scanAllMObjects(SIMPLE_HOST_PROPS, &hosts) - if err != nil { - return SNetworkInfo{}, errors.Wrap(err, "scanAllMObjects") - } - return cli.hostVMIPs(ctx, hosts) -} - -func (cli *SESXiClient) macVlanMap(mohost *mo.HostSystem, movm *mo.VirtualMachine, dvpgMap sVPGMap) map[string]int32 { - vpgMap := cli.getVPGMap(mohost) - ret := make(map[string]int32, 2) - for _, device := range movm.Config.Hardware.Device { - bcard, ok := device.(types.BaseVirtualEthernetCard) - if !ok { - continue - } - card := bcard.GetVirtualEthernetCard() - mac := card.MacAddress - switch bk := card.Backing.(type) { - case *types.VirtualEthernetCardDistributedVirtualPortBackingInfo: - key := bk.Port.PortgroupKey - proc, ok := dvpgMap.Get(key) - if !ok { - log.Errorf("dvpg %s not found in key-vlanid map", key) - ret[mac] = 0 - continue - } - ret[mac] = proc.vlanId - case *types.VirtualEthernetCardNetworkBackingInfo: - netName, err := cli.networkName(bk.Network.Value) - if err != nil { - log.Errorf("get getNetworkName of %q: %v", bk.Network.Value, err) - ret[mac] = 0 - continue - } - key := fmt.Sprintf("%s-%s", mohost.Reference().Value, netName) - proc, ok := vpgMap.Get(key) - if !ok { - log.Errorf("vpg %s not found in key-vlanid map", key) - ret[mac] = 0 - continue - } - ret[mac] = proc.vlanId - default: - ret[mac] = 0 - } - } - return ret -} - -func (cli *SESXiClient) vmIPs(host *mo.HostSystem, vpgMap sVPGMap) (SNetworkInfo, error) { - nInfo := SNetworkInfo{ - VMs: make([]SSimpleVM, 0, len(host.Vm)), - VlanIps: make(map[int32][]netutils.IPV4Addr), - IPPool: NewIPPool(), - } - if len(host.Vm) == 0 { - return nInfo, nil - } - var vms []mo.VirtualMachine - err := cli.references2Objects(host.Vm, SIMPLE_VM_PROPS, &vms) - if err != nil { - return nInfo, errors.Wrap(err, "references2Objects") - } - for i := range vms { - vm := vms[i] - if vm.Config == nil || vm.Config.Template { - continue - } - if vm.Guest == nil { - continue - } - macVlanMap := cli.macVlanMap(host, &vm, vpgMap) - guestIps := make([]SIPVlan, 0) - for _, net := range vm.Guest.Net { - if len(net.Network) == 0 { - continue - } - mac := net.MacAddress - for _, ip := range net.IpAddress { - if !regutils.MatchIP4Addr(ip) { - continue - } - if !vmIPV4Filter.Contains(ip) { - continue - } - ipaddr, _ := netutils.NewIPV4Addr(ip) - if netutils.IsLinkLocal(ipaddr) { - continue - } - vlan := macVlanMap[mac] - guestIps = append(guestIps, SIPVlan{ - IP: ipaddr, - VlanId: vlan, - }) - nInfo.VlanIps[vlan] = append(nInfo.VlanIps[vlan], ipaddr) - nInfo.IPPool.Insert(ipaddr, SIPProc{ - VlanId: vlan, - }) - break - } - } - nInfo.VMs = append(nInfo.VMs, SSimpleVM{vm.Name, guestIps}) - } - return nInfo, nil -} diff --git a/pkg/multicloud/esxi/net_topology.go b/pkg/multicloud/esxi/net_topology.go new file mode 100644 index 0000000000..42d5fb6826 --- /dev/null +++ b/pkg/multicloud/esxi/net_topology.go @@ -0,0 +1,193 @@ +// 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 ( + "context" + "sort" + + "github.com/vmware/govmomi/vim25/mo" + "golang.org/x/sync/errgroup" + + "yunion.io/x/pkg/errors" + "yunion.io/x/pkg/util/netutils" + "yunion.io/x/pkg/util/regutils" +) + +func (cli *SESXiClient) HostVmIPsInDc(ctx context.Context, dc *SDatacenter) (SNetworkInfo, error) { + var hosts []mo.HostSystem + err := cli.scanMObjects(dc.object.Entity().Self, HOST_SYSTEM_PROPS, &hosts) + if err != nil { + return SNetworkInfo{}, errors.Wrap(err, "scanMObjects") + } + return cli.hostVMIPs(ctx, hosts) +} + +func (cli *SESXiClient) HostVmIPsInCluster(ctx context.Context, cluster *SCluster) (SNetworkInfo, error) { + var hosts []mo.HostSystem + err := cli.scanMObjects(cluster.object.Entity().Self, HOST_SYSTEM_PROPS, &hosts) + if err != nil { + return SNetworkInfo{}, errors.Wrap(err, "scanMObjects") + } + return cli.hostVMIPs(ctx, hosts) +} + +type SNetworkInfo struct { + SNetworkInfoBase + + HostIps map[string]netutils.IPV4Addr + VlanIps map[int32][]netutils.IPV4Addr +} + +func (s *SNetworkInfo) Insert(proc SIPProc, ip netutils.IPV4Addr) { + s.SNetworkInfoBase.Insert(proc, ip) + s.VlanIps[proc.VlanId] = append(s.VlanIps[proc.VlanId], ip) +} + +func (cli *SESXiClient) hostVMIPs(ctx context.Context, hosts []mo.HostSystem) (SNetworkInfo, error) { + ret := SNetworkInfo{} + dvpgMap, err := cli.getDVPGMap() + if err != nil { + return ret, errors.Wrap(err, "unable to get dvpgKeyVlanMap") + } + group, ctx := errgroup.WithContext(ctx) + collection := make([]*SNetworkInfo, len(hosts)) + for i := range hosts { + j := i + group.Go(func() error { + nInfo, err := cli.vmIPs(&hosts[j], dvpgMap) + if err != nil { + return err + } + collection[j] = nInfo.(*SNetworkInfo) + return nil + }) + } + + hostIps := make(map[string]netutils.IPV4Addr, len(hosts)) + for i := range hosts { + // find ip + host := &SHost{SManagedObject: newManagedObject(cli, &hosts[i], nil)} + ipStr := host.GetAccessIp() + ip, err := netutils.NewIPV4Addr(ipStr) + if err != nil { + return ret, errors.Wrapf(err, "invalid host ip %q", ipStr) + } + hostIps[host.GetName()] = ip + } + err = group.Wait() + if err != nil { + return ret, err + } + // length + if len(collection) == 0 { + ret.HostIps = hostIps + return ret, nil + } + ni := cli.mergeNetworInfo(collection) + ni.HostIps = hostIps + return ni, nil +} + +func (cli *SESXiClient) mergeNetworInfo(nInfos []*SNetworkInfo) SNetworkInfo { + var vmsLen, vlanIpLen, ipPoolLen int + for i := range nInfos { + vmsLen += len(nInfos[i].VMs) + vlanIpLen += len(nInfos[i].VlanIps) + ipPoolLen += nInfos[i].IPPool.Len() + } + ret := SNetworkInfo{ + SNetworkInfoBase: SNetworkInfoBase{ + IPPool: NewIPPool(ipPoolLen), + VMs: make([]SSimpleVM, 0, vmsLen), + }, + VlanIps: make(map[int32][]netutils.IPV4Addr, vlanIpLen), + } + for i := range nInfos { + ret.VMs = append(ret.VMs, nInfos[i].VMs...) + for vlan, ips := range nInfos[i].VlanIps { + ret.VlanIps[vlan] = append(ret.VlanIps[vlan], ips...) + } + ret.IPPool.Merge(&nInfos[i].IPPool) + } + for _, ips := range ret.VlanIps { + sort.Slice(ips, func(i, j int) bool { + return ips[i] < ips[j] + }) + } + return ret +} + +func (cli *SESXiClient) HostVmIPs(ctx context.Context) (SNetworkInfo, error) { + var hosts []mo.HostSystem + err := cli.scanAllMObjects(SIMPLE_HOST_PROPS, &hosts) + if err != nil { + return SNetworkInfo{}, errors.Wrap(err, "scanAllMObjects") + } + return cli.hostVMIPs(ctx, hosts) +} + +func (cli *SESXiClient) vmIPs(host *mo.HostSystem, vpgMap sVPGMap) (INetworkInfo, error) { + nInfo := NewNetworkInfo(false, len(host.Vm)) + if len(host.Vm) == 0 { + return nInfo, nil + } + var vms []mo.VirtualMachine + err := cli.references2Objects(host.Vm, SIMPLE_VM_PROPS, &vms) + if err != nil { + return nInfo, errors.Wrap(err, "references2Objects") + } + for i := range vms { + vm := vms[i] + if vm.Config == nil || vm.Config.Template { + continue + } + if vm.Guest == nil { + continue + } + macVlanMap, err := cli.macVlanMap(host, &vm, vpgMap) + if err != nil { + return nInfo, errors.Wrap(err, "macVlanMap") + } + guestIps := make([]SIPVlan, 0) + for _, net := range vm.Guest.Net { + if len(net.Network) == 0 { + continue + } + mac := net.MacAddress + for _, ip := range net.IpAddress { + if !regutils.MatchIP4Addr(ip) { + continue + } + if !vmIPV4Filter.Contains(ip) { + continue + } + ipaddr, _ := netutils.NewIPV4Addr(ip) + if netutils.IsLinkLocal(ipaddr) { + continue + } + proc := macVlanMap[mac] + guestIps = append(guestIps, SIPVlan{ + IP: ipaddr, + VlanId: proc.VlanId, + }) + nInfo.Insert(proc, ipaddr) + break + } + } + nInfo.AppendVMs(vm.Name, guestIps) + } + return nInfo, nil +} diff --git a/pkg/multicloud/esxi/net_topology_pro.go b/pkg/multicloud/esxi/net_topology_pro.go new file mode 100644 index 0000000000..c7858810f5 --- /dev/null +++ b/pkg/multicloud/esxi/net_topology_pro.go @@ -0,0 +1,671 @@ +// 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 ( + "context" + "fmt" + "sort" + "sync" + + "github.com/vmware/govmomi/vim25/mo" + "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/netutils" + "yunion.io/x/pkg/util/regutils" +) + +var ( + SIMPLE_HOST_PROPS = []string{"name", "config.network", "vm"} + SIMPLE_VM_PROPS = []string{"name", "guest.net", "config.template", "config.hardware.device"} + SIMPLE_DVPG_PROPS = []string{"key", "config.defaultPortConfig", "config.distributedVirtualSwitch"} + SIMPLE_DVS_PROPS = []string{"name", "config"} + SIMPLE_NETWORK_PROPS = []string{"name"} +) + +type SIPVlan struct { + IP netutils.IPV4Addr + VlanId int32 +} + +type SSimpleVM struct { + Name string + IPVlans []SIPVlan +} + +type SSimpleHostDev struct { + Name string + Id string + Mac string +} + +func (cli *SESXiClient) scanAllDvPortgroups() ([]*SDistributedVirtualPortgroup, error) { + var modvpgs []mo.DistributedVirtualPortgroup + err := cli.scanAllMObjects(SIMPLE_DVPG_PROPS, &modvpgs) + if err != nil { + return nil, errors.Wrap(err, "scanAllMObjects") + } + dvpgs := make([]*SDistributedVirtualPortgroup, 0, len(modvpgs)) + for i := range modvpgs { + dvpgs = append(dvpgs, NewDistributedVirtualPortgroup(cli, &modvpgs[i], nil)) + } + return dvpgs, nil +} + +func (cli *SESXiClient) scanAllNetworks() ([]mo.Network, error) { + var monets []mo.Network + err := cli.scanAllMObjects(SIMPLE_NETWORK_PROPS, &monets) + if err != nil { + return nil, errors.Wrap(err, "scanAllMObjects") + } + return monets, nil +} + +func (cli *SESXiClient) networkName(refV string) (string, error) { + if cli.networkQueryMap == nil { + nets, err := cli.scanAllNetworks() + if err != nil { + return "", err + } + cli.networkQueryMap = &sync.Map{} + for i := range nets { + cli.networkQueryMap.Store(nets[i].Reference().Value, nets[i].Name) + } + } + iter, ok := cli.networkQueryMap.Load(refV) + if !ok { + return "", nil + } + return iter.(string), nil +} + +func (cli *SESXiClient) hostVMIPsPro(ctx context.Context, hosts []mo.HostSystem) (SNetworkInfoPro, error) { + ret := SNetworkInfoPro{ + SNetworkInfoBase: SNetworkInfoBase{ + VMs: []SSimpleVM{}, + IPPool: SIPPool{}, + }, + HostIps: make(map[string][]netutils.IPV4Addr), + } + vsMap, err := cli.getVirtualSwitchs(hosts) + if err != nil { + return ret, errors.Wrap(err, "unable to getVirtualSwitchs") + } + ret.VsMap = vsMap + dvpgMap, err := cli.getDVPGMap() + if err != nil { + return ret, errors.Wrap(err, "unable to get dvpgKeyVlanMap") + } + group, ctx := errgroup.WithContext(ctx) + collection := make([]*SNetworkInfoPro, len(hosts)) + for i := range hosts { + j := i + group.Go(func() error { + nInfo, err := cli.vmIPsPro(&hosts[j], dvpgMap) + if err != nil { + return err + } + collection[j] = nInfo.(*SNetworkInfoPro) + return nil + }) + } + + err = group.Wait() + if err != nil { + return ret, err + } + cli.mergeNetworInfoPro(&ret, collection) + vsList := vsMap.List() + for i := range vsList { + vs := vsList[i] + for hName, ips := range vs.HostIps { + sort.Slice(ips, func(i, j int) bool { + return ips[i] < ips[j] + }) + for _, ip := range ips { + ret.IPPool.Insert(ip, SIPProc{ + VlanId: 0, + VSId: vs.Id, + IsHost: true, + }) + } + ret.HostIps[hName] = append(ret.HostIps[hName], ips...) + } + } + return ret, nil +} + +func (cli *SESXiClient) mergeNetworInfoPro(ret *SNetworkInfoPro, nInfos []*SNetworkInfoPro) { + log.Infof("nInfos before mergeNetworInfoPro: %s", jsonutils.Marshal(nInfos)) + var vmsLen, ipPoolLen int + for i := range nInfos { + vmsLen += len(nInfos[i].VMs) + ipPoolLen += nInfos[i].IPPool.Len() + } + ret.VMs = make([]SSimpleVM, 0, vmsLen) + ret.IPPool = NewIPPool(ipPoolLen) + for i := range nInfos { + ret.VMs = append(ret.VMs, nInfos[i].VMs...) + ret.IPPool.Merge(&nInfos[i].IPPool) + ret.VsMap.Merge(&nInfos[i].VsMap) + } +} + +func (cli *SESXiClient) HostVmIPsPro(ctx context.Context) (SNetworkInfoPro, error) { + var hosts []mo.HostSystem + err := cli.scanAllMObjects(SIMPLE_HOST_PROPS, &hosts) + if err != nil { + return SNetworkInfoPro{}, errors.Wrap(err, "scanAllMObjects") + } + return cli.hostVMIPsPro(ctx, hosts) +} + +func vpgMapKey(prefix, key string) string { + if len(prefix) == 0 { + return key + } + return fmt.Sprintf("%s-%s", prefix, key) +} + +func (cli *SESXiClient) macVlanMap(mohost *mo.HostSystem, movm *mo.VirtualMachine, dvpgMap sVPGMap) (map[string]SIPProc, error) { + vpgMap := cli.getVPGMap(mohost) + ret := make(map[string]SIPProc, 2) + for _, device := range movm.Config.Hardware.Device { + bcard, ok := device.(types.BaseVirtualEthernetCard) + if !ok { + continue + } + card := bcard.GetVirtualEthernetCard() + mac := card.MacAddress + switch bk := card.Backing.(type) { + case *types.VirtualEthernetCardDistributedVirtualPortBackingInfo: + key := vpgMapKey("", bk.Port.PortgroupKey) + proc, ok := dvpgMap.Get(key) + if !ok { + log.Errorf("dvpg %s not found in key-vlanid map", key) + continue + } + ret[mac] = proc + case *types.VirtualEthernetCardNetworkBackingInfo: + netName, err := cli.networkName(bk.Network.Value) + if err != nil { + return nil, errors.Wrapf(err, "cli.networkName of %q", bk.Network.Value) + } + key := vpgMapKey(mohost.Reference().Value, netName) + proc, ok := vpgMap.Get(key) + if !ok { + log.Errorf("vpg %s not found in key-vlanid map", key) + continue + } + ret[mac] = proc + } + } + return ret, nil +} + +type SNetworkInfoPro struct { + SNetworkInfoBase + + HostIps map[string][]netutils.IPV4Addr + VsMap SVirtualSwitchMap +} + +func (s *SNetworkInfoPro) Insert(proc SIPProc, ip netutils.IPV4Addr) { + s.SNetworkInfoBase.Insert(proc, ip) + s.VsMap.AddVlanIp(proc.VSId, proc.VlanId, ip) +} + +type SNetworkInfoBase struct { + VMs []SSimpleVM + IPPool SIPPool +} + +func (s *SNetworkInfoBase) Insert(proc SIPProc, ip netutils.IPV4Addr) { + s.IPPool.Insert(ip, proc) +} + +func (s *SNetworkInfoBase) AppendVMs(name string, IpVlans []SIPVlan) { + s.VMs = append(s.VMs, SSimpleVM{name, IpVlans}) +} + +type INetworkInfo interface { + Insert(SIPProc, netutils.IPV4Addr) + AppendVMs(name string, IpVlans []SIPVlan) +} + +func NewNetworkInfo(dvs bool, size int) INetworkInfo { + base := SNetworkInfoBase{ + VMs: make([]SSimpleVM, 0, size), + IPPool: NewIPPool(int(size)), + } + if dvs { + return &SNetworkInfoPro{ + SNetworkInfoBase: base, + VsMap: NewVirtualSwitchMap(), + } + } + return &SNetworkInfo{ + SNetworkInfoBase: base, + HostIps: make(map[string]netutils.IPV4Addr), + VlanIps: make(map[int32][]netutils.IPV4Addr), + } +} + +func (cli *SESXiClient) vmIPsPro(host *mo.HostSystem, vpgMap sVPGMap) (INetworkInfo, error) { + nInfo := NewNetworkInfo(true, len(host.Vm)) + if len(host.Vm) == 0 { + return nInfo, nil + } + var vms []mo.VirtualMachine + err := cli.references2Objects(host.Vm, SIMPLE_VM_PROPS, &vms) + if err != nil { + return nInfo, errors.Wrap(err, "references2Objects") + } + for i := range vms { + vm := vms[i] + if vm.Config == nil || vm.Config.Template { + continue + } + if vm.Guest == nil { + continue + } + macVlanMap, err := cli.macVlanMap(host, &vm, vpgMap) + if err != nil { + return nInfo, err + } + guestIps := make([]SIPVlan, 0) + for _, net := range vm.Guest.Net { + if len(net.Network) == 0 { + continue + } + mac := net.MacAddress + for _, ip := range net.IpAddress { + if !regutils.MatchIP4Addr(ip) { + continue + } + if !vmIPV4Filter.Contains(ip) { + continue + } + ipaddr, _ := netutils.NewIPV4Addr(ip) + if netutils.IsLinkLocal(ipaddr) { + continue + } + proc := macVlanMap[mac] + guestIps = append(guestIps, SIPVlan{ + IP: ipaddr, + VlanId: proc.VlanId, + }) + nInfo.Insert(proc, ipaddr) + break + } + } + nInfo.AppendVMs(vm.Name, guestIps) + } + return nInfo, nil +} + +type SVirtualSwitchSpec struct { + Name string + Id string + Distributed bool + Hosts []SSimpleHostDev + HostIps map[string][]netutils.IPV4Addr + Vlans map[int32][]netutils.IPV4Addr +} + +func NewVirtualSwitch() *SVirtualSwitchSpec { + return &SVirtualSwitchSpec{ + HostIps: make(map[string][]netutils.IPV4Addr), + Vlans: make(map[int32][]netutils.IPV4Addr), + } +} + +func (vs *SVirtualSwitchSpec) Merge(vsc *SVirtualSwitchSpec) { + for vlan, ips := range vsc.Vlans { + vs.Vlans[vlan] = insert(vs.Vlans[vlan], ips) + } +} + +func insert(base []netutils.IPV4Addr, add []netutils.IPV4Addr) []netutils.IPV4Addr { + for _, ip := range add { + index := sort.Search(len(base), func(n int) bool { + return base[n] > ip + }) + base = append(base, 0) + base = append(base[:index+1], base[index:len(base)-1]...) + base[index] = ip + } + return base +} + +type SVirtualSwitchMap struct { + vsList []SVirtualSwitchSpec + vsMap map[string]*SVirtualSwitchSpec +} + +func NewVirtualSwitchMap(length ...int) SVirtualSwitchMap { + initLen := 0 + if len(length) > 0 { + initLen = length[0] + } + return SVirtualSwitchMap{ + vsList: make([]SVirtualSwitchSpec, 0, initLen), + vsMap: make(map[string]*SVirtualSwitchSpec, initLen), + } +} + +func (vsm *SVirtualSwitchMap) Insert(id string, vs SVirtualSwitchSpec) { + ovs, ok := vsm.vsMap[id] + if ok { + ovs.Merge(&vs) + return + } + vsm.vsList = append(vsm.vsList, vs) + vsm.vsMap[id] = &vsm.vsList[len(vsm.vsList)-1] +} + +func (vms *SVirtualSwitchMap) Merge(ovsm *SVirtualSwitchMap) { + for i := range ovsm.vsList { + vs := ovsm.vsList[i] + vms.Insert(vs.Id, vs) + } +} + +func (vsm *SVirtualSwitchMap) AddVlanIp(id string, vlanId int32, ip netutils.IPV4Addr) bool { + vs, ok := vsm.vsMap[id] + if !ok { + vs = NewVirtualSwitch() + vs.Id = id + vsm.Insert(id, *vs) + vs = vsm.vsMap[id] + } + vs.Vlans[vlanId] = append(vs.Vlans[vlanId], ip) + return true +} + +func (vsm *SVirtualSwitchMap) List() []SVirtualSwitchSpec { + return vsm.vsList +} + +func findIp(hs *mo.HostSystem, nic string) netutils.IPV4Addr { + for i := range hs.Config.Network.Pnic { + pnic := hs.Config.Network.Pnic[i] + if pnic.Device != nic { + continue + } + ip := pnic.Spec.Ip.IpAddress + if len(ip) == 0 { + return netutils.IPV4Addr(0) + } + if !regutils.MatchIP4Addr(ip) { + return netutils.IPV4Addr(0) + } + ipaddr, _ := netutils.NewIPV4Addr(ip) + if netutils.IsLinkLocal(ipaddr) { + return netutils.IPV4Addr(0) + } + return ipaddr + } + return netutils.IPV4Addr(0) +} + +func getNicDevice(vs *types.HostVirtualSwitch) []string { + bridge := vs.Spec.Bridge + switch b := bridge.(type) { + case *types.HostVirtualSwitchAutoBridge: + return b.ExcludedNicDevice + case *types.HostVirtualSwitchBondBridge: + return b.NicDevice + case *types.HostVirtualSwitchSimpleBridge: + return []string{b.NicDevice} + default: + return nil + } +} + +func (cli *SESXiClient) getVirtualSwitchs(hss []mo.HostSystem) (SVirtualSwitchMap, error) { + nic2Ip := make(map[string]netutils.IPV4Addr, len(hss)) + nic2Mac := make(map[string]string, len(hss)) + for j := range hss { + hs := hss[j] + macToIp := make(map[string]string, len(hs.Config.Network.Vnic)) + for i := range hs.Config.Network.Vnic { + vnic := hs.Config.Network.Vnic[i] + if len(vnic.Spec.Ip.IpAddress) == 0 { + continue + } + macToIp[vnic.Spec.Mac] = vnic.Spec.Ip.IpAddress + } + for i := range hs.Config.Network.ConsoleVnic { + vnic := hs.Config.Network.Vnic[i] + if len(vnic.Spec.Ip.IpAddress) == 0 { + continue + } + macToIp[vnic.Spec.Mac] = vnic.Spec.Ip.IpAddress + } + for i := range hs.Config.Network.Pnic { + pnic := hs.Config.Network.Pnic[i] + ip := pnic.Spec.Ip.IpAddress + nicKey := fmt.Sprintf("%s-%s", hs.Self.Value, pnic.Device) + nic2Mac[nicKey] = pnic.Mac + if len(ip) == 0 { + ip = macToIp[pnic.Mac] + } + if len(ip) == 0 { + log.Warningf("host %s pnic %s has no ipaddress", hs.Name, pnic.Device) + continue + } + if !regutils.MatchIP4Addr(ip) { + log.Warningf("host %s pnic %s ipaddress %q is not IPV4Addr", hs.Name, pnic.Device, ip) + continue + } + ipaddr, _ := netutils.NewIPV4Addr(ip) + if netutils.IsLinkLocal(ipaddr) { + log.Warningf("host %s pnic %s ipaddress %q is link local addr", hs.Name, pnic.Device, ip) + continue + } + nic2Ip[nicKey] = ipaddr + } + } + value2name := make(map[string]string, len(hss)) + for i := range hss { + value2name[hss[i].Self.Value] = hss[i].Name + } + // dvs + var dvss []mo.DistributedVirtualSwitch + err := cli.scanAllMObjects(SIMPLE_DVS_PROPS, &dvss) + if err != nil { + return SVirtualSwitchMap{}, err + } + ret := NewVirtualSwitchMap(len(dvss) + len(hss)) + for i := range dvss { + dvs := dvss[i] + vsId := dvs.Self.Value + vsName := fmt.Sprintf("%s", dvs.Name) + hosts := dvs.Config.GetDVSConfigInfo().Host + vs := SVirtualSwitchSpec{ + Name: vsName, + Id: vsId, + Distributed: true, + HostIps: make(map[string][]netutils.IPV4Addr, len(hosts)), + Vlans: make(map[int32][]netutils.IPV4Addr), + } + for _, host := range hosts { + hostName := value2name[host.Config.Host.Value] + vs.HostIps[hostName] = []netutils.IPV4Addr{} + hostDev := SSimpleHostDev{ + Id: host.Config.Host.Value, + Name: hostName, + } + switch back := host.Config.Backing.(type) { + case *types.DistributedVirtualSwitchHostMemberPnicBacking: + for i := range back.PnicSpec { + nicDevice := back.PnicSpec[i].PnicDevice + nicKey := fmt.Sprintf("%s-%s", host.Config.Host.Value, nicDevice) + hostDev.Mac = nic2Mac[nicKey] + ip, ok := nic2Ip[nicKey] + if ok { + vs.HostIps[hostName] = append(vs.HostIps[hostName], ip) + } + } + case *types.DistributedVirtualSwitchHostMemberBacking: + } + vs.Hosts = append(vs.Hosts, hostDev) + } + ret.Insert(vsId, vs) + } + // vs + for i := range hss { + hs := hss[i] + if hs.Config.Network == nil { + continue + } + for i := range hs.Config.Network.Vswitch { + ivs := hs.Config.Network.Vswitch[i] + vsId := fmt.Sprintf("%s/%s", hs.Self.Value, ivs.Name) + vsName := fmt.Sprintf("%s/%s", hs.Name, ivs.Name) + vs := SVirtualSwitchSpec{ + Name: vsName, + Id: vsId, + Distributed: false, + HostIps: make(map[string][]netutils.IPV4Addr), + Vlans: make(map[int32][]netutils.IPV4Addr), + } + hostDev := SSimpleHostDev{ + Id: hs.Self.Value, + Name: hs.Name, + } + for _, nic := range getNicDevice(&ivs) { + nicKey := fmt.Sprintf("%s-%s", hs.Self.Value, nic) + log.Infof("nic: %s, nicKey: %s", nic, nicKey) + hostDev.Mac = nic2Mac[nicKey] + ip, ok := nic2Ip[nicKey] + if !ok { + continue + } + vs.HostIps[hs.Name] = append(vs.HostIps[hs.Name], ip) + } + vs.Hosts = append(vs.Hosts, hostDev) + ret.Insert(vsId, vs) + } + } + return ret, nil +} + +func (cli *SESXiClient) getVPGMap(mohost *mo.HostSystem) sVPGMap { + sm := newVPGMap() + if mohost.Config.Network == nil { + return sm + } + for i := range mohost.Config.Network.Portgroup { + ipg := mohost.Config.Network.Portgroup[i] + key := vpgMapKey(mohost.Reference().Value, ipg.Spec.Name) + vlan := ipg.Spec.VlanId + vsId := fmt.Sprintf("%s/%s", mohost.Reference().Value, ipg.Spec.VswitchName) + sm.Insert(key, SIPProc{ + VlanId: vlan, + VSId: vsId, + }) + } + return sm +} + +func (cli *SESXiClient) getDVPGMap() (sVPGMap, error) { + sm := newVPGMap() + dvpgs, err := cli.scanAllDvPortgroups() + if err != nil { + return sm, err + } + for i := range dvpgs { + modvpg := dvpgs[i].getMODVPortgroup() + key := vpgMapKey("", modvpg.Key) + vlanid := dvpgs[i].GetVlanId() + vsId := modvpg.Config.DistributedVirtualSwitch.Value + sm.Insert(key, SIPProc{ + VlanId: vlanid, + VSId: vsId, + }) + } + return sm, nil +} + +func newVPGMap() sVPGMap { + return sVPGMap{m: &sync.Map{}} +} + +type sVPGMap struct { + m *sync.Map +} + +func (vm *sVPGMap) Insert(key string, proc SIPProc) { + vm.m.Store(key, proc) +} + +func (vm *sVPGMap) Get(key string) (SIPProc, bool) { + v, ok := vm.m.Load(key) + if !ok { + return SIPProc{}, ok + } + r := v.(SIPProc) + return r, ok +} + +type SIPProc struct { + VSId string + VlanId int32 + IsHost bool +} + +type SIPPool struct { + p map[netutils.IPV4Addr]SIPProc +} + +func NewIPPool(size ...int) SIPPool { + if len(size) > 0 { + return SIPPool{p: make(map[netutils.IPV4Addr]SIPProc, size[0])} + } + return SIPPool{p: make(map[netutils.IPV4Addr]SIPProc)} +} + +func (p *SIPPool) Has(ip netutils.IPV4Addr) bool { + _, ok := p.p[ip] + return ok +} + +func (p *SIPPool) Get(ip netutils.IPV4Addr) (SIPProc, bool) { + r, ok := p.p[ip] + return r, ok +} + +func (p *SIPPool) Insert(ip netutils.IPV4Addr, proc SIPProc) { + p.p[ip] = proc +} + +func (p *SIPPool) Merge(op *SIPPool) { + for ip, proc := range op.p { + if p.Has(ip) { + continue + } + p.Insert(ip, proc) + } +} + +func (p *SIPPool) Len() int { + return len(p.p) +} diff --git a/pkg/multicloud/esxi/network.go b/pkg/multicloud/esxi/network.go index d5664b5e55..409f1180e8 100644 --- a/pkg/multicloud/esxi/network.go +++ b/pkg/multicloud/esxi/network.go @@ -15,15 +15,11 @@ package esxi import ( - "fmt" - "sync" - "github.com/vmware/govmomi/object" "github.com/vmware/govmomi/vim25/mo" "github.com/vmware/govmomi/vim25/types" "yunion.io/x/pkg/errors" - "yunion.io/x/pkg/util/netutils" ) type IVMNetwork interface { @@ -265,163 +261,3 @@ func (net *SDistributedVirtualPortgroup) AddHostToDVS(host *SHost) (err error) { } return err } - -type SVirtualSwitch struct { - Name string - Id string - Distributed bool - Hosts []string - Vlans map[int32]SVirtualLan -} - -type SVirtualLan struct { - Vlan int32 - Ips []netutils.IPV4Addr -} - -func (h *SHost) getVirtualSwitchs() []SVirtualSwitch { - hs := h.getHostSystem() - if hs.Config.Network == nil { - return nil - } - vss := make([]SVirtualSwitch, 0, len(hs.Config.Network.Vswitch)) - vsMap := make(map[string]*SVirtualSwitch) - for i := range hs.Config.Network.Vswitch { - ivs := hs.Config.Network.Vswitch[i] - vsId := fmt.Sprintf("%s-%s", h.GetId(), ivs.Name) - vsName := ivs.Name - vs := SVirtualSwitch{ - Name: vsName, - Id: vsId, - Distributed: false, - } - vss = append(vss, vs) - vsMap[ivs.Name] = &vss[len(vss)-1] - } - for i := range hs.Config.Network.Portgroup { - ipg := hs.Config.Network.Portgroup[i] - vlan, vsName := ipg.Spec.VlanId, ipg.Spec.VswitchName - ivs := vsMap[vsName] - ivs.Vlans[vlan] = SVirtualLan{ - Vlan: vlan, - } - } - return vss -} - -func (cli *SESXiClient) getVPGMap(mohost *mo.HostSystem) sVPGMap { - sm := newVPGMap() - if mohost.Config.Network == nil { - return sm - } - for i := range mohost.Config.Network.Portgroup { - ipg := mohost.Config.Network.Portgroup[i] - key := fmt.Sprintf("%s-%s", mohost.Reference().Value, ipg.Spec.Name) - vlan := ipg.Spec.VlanId - sm.Insert(key, sVPGProc{ - vlanId: vlan, - }) - // TODO: fill VSId - } - return sm -} - -func (cli *SESXiClient) getDVPGMap() (sVPGMap, error) { - sm := newVPGMap() - dvpgs, err := cli.scanAllDvPortgroups() - if err != nil { - return sm, err - } - for i := range dvpgs { - key := dvpgs[i].getMODVPortgroup().Key - vlanid := dvpgs[i].GetVlanId() - sm.Insert(key, sVPGProc{ - vlanId: vlanid, - }) - // TODO: fill VSId - } - return sm, nil -} - -func (cli *SESXiClient) getVirtualSwitchs() []SVirtualSwitch { - // TODO - return nil -} - -func newVPGMap() sVPGMap { - return sVPGMap{m: &sync.Map{}} -} - -type sVPGMap struct { - m *sync.Map -} - -func (vm *sVPGMap) Insert(key string, proc sVPGProc) { - vm.m.Store(key, proc) -} - -func (vm *sVPGMap) Get(key string) (sVPGProc, bool) { - v, ok := vm.m.Load(key) - if !ok { - return sVPGProc{}, ok - } - r := v.(sVPGProc) - return r, ok -} - -type sVPGProc struct { - vlanId int32 - dvId string -} - -type SIPProc struct { - VSId string - VlanId int32 -} - -type SIPPool struct { - p map[netutils.IPV4Addr]SIPProc -} - -func NewIPPool(length ...int) SIPPool { - if len(length) > 0 { - return SIPPool{p: make(map[netutils.IPV4Addr]SIPProc, length[0])} - } - return SIPPool{p: make(map[netutils.IPV4Addr]SIPProc)} -} - -func (p *SIPPool) Has(ip netutils.IPV4Addr) bool { - _, ok := p.p[ip] - return ok -} - -func (p *SIPPool) FillVsId(vsId string) { - for k, v := range p.p { - p.p[k] = SIPProc{ - VlanId: v.VlanId, - VSId: vsId, - } - } -} - -func (p *SIPPool) Get(ip netutils.IPV4Addr) (SIPProc, bool) { - r, ok := p.p[ip] - return r, ok -} - -func (p *SIPPool) Insert(ip netutils.IPV4Addr, proc SIPProc) { - p.p[ip] = proc -} - -func (p *SIPPool) Merge(op *SIPPool) { - for ip, proc := range op.p { - if p.Has(ip) { - continue - } - p.Insert(ip, proc) - } -} - -func (p *SIPPool) Len() int { - return len(p.p) -}