Skip to content

Commit

Permalink
Add VM migration ordering interleaved by AZ
Browse files Browse the repository at this point in the history
This fixes issue #5
  • Loading branch information
sneal committed May 22, 2023
1 parent 6263829 commit f5f6f45
Show file tree
Hide file tree
Showing 2 changed files with 149 additions and 22 deletions.
45 changes: 43 additions & 2 deletions pkg/migrate/vm_source.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@ package migrate

import (
"context"
"fmt"
"github.com/vmware-tanzu/vmotion-migration-tool-for-bosh-deployments/pkg/bosh"
"github.com/vmware-tanzu/vmotion-migration-tool-for-bosh-deployments/pkg/config"
"sort"
)

//counterfeiter:generate . BoshClient
Expand Down Expand Up @@ -59,14 +61,53 @@ func (s *VMSource) VMsToMigrate(ctx context.Context) ([]VM, error) {

var vms []VM
for _, bvm := range boshVMs {
clusters := s.srcAZsToClusters[bvm.AZ]
if len(clusters) == 0 {
return nil, fmt.Errorf("found BOSH VM '%s' with AZ '%s' but no source clusters in the config for that AZ",
bvm.Name, bvm.AZ)
}
vms = append(vms, VM{
Name: bvm.Name,
AZ: bvm.AZ,
Clusters: s.srcAZsToClusters[bvm.AZ],
Clusters: clusters,
})
}
vms = append(vms, s.additionalVMs...)
return vms, nil
return s.interleaveVMsByAZ(vms), nil
}

func (s *VMSource) interleaveVMsByAZ(vms []VM) []VM {
// sort all VMs into buckets by AZ
vmsByAZ := make(map[string][]VM)
for _, v := range vms {
if vmsByAZ[v.AZ] == nil {
vmsByAZ[v.AZ] = make([]VM, 0)
}
vmsByAZ[v.AZ] = append(vmsByAZ[v.AZ], v)
}

// create a stable list of all unique AZs
var azs []string
for az := range vmsByAZ {
azs = append(azs, az)
}
sort.Strings(azs)

// create a new list of VMs interleaved by AZ
// pull one VM from each AZ, then start it all over again until we run out of VMs
more := true
var sortedVMs []VM
for i := 0; more; i++ {
more = false
for _, az := range azs {
l := vmsByAZ[az]
if i < len(l) {
more = true
sortedVMs = append(sortedVMs, l[i])
}
}
}
return sortedVMs
}

func configToAdditionalVMs(c config.Config, srcAZToClusters map[string][]string) []VM {
Expand Down
126 changes: 106 additions & 20 deletions pkg/migrate/vm_source_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,21 @@ func baseSourceConfig() config.Config {
},
},
},
{
Name: "az3",
VCenter: &config.VCenter{
Host: "vcenter1.example.com",
Username: "admin1",
Password: "secret1",
Datacenter: "DC1",
},
Clusters: []config.ComputeCluster{
{
Name: "Cluster4",
ResourcePool: "RP1",
},
},
},
},
Target: []config.ComputeAZ{
{
Expand Down Expand Up @@ -90,7 +105,22 @@ func baseSourceConfig() config.Config {
Clusters: []config.ComputeCluster{
{
Name: "Cluster5",
ResourcePool: "RP5",
ResourcePool: "RP1",
},
},
},
{
Name: "az2",
VCenter: &config.VCenter{
Host: "vcenter2.example.com",
Username: "admin2",
Password: "secret2",
Datacenter: "DC2",
},
Clusters: []config.ComputeCluster{
{
Name: "Cluster6",
ResourcePool: "RP1",
},
},
},
Expand All @@ -104,7 +134,7 @@ func baseSourceConfig() config.Config {
}
}

func TestConfigToBoshClient(t *testing.T) {
func TestNewVMSourceFromConfigBoshClient(t *testing.T) {
c := baseSourceConfig()
src := migrate.NewVMSourceFromConfig(c)
require.IsType(t, migrate.NullBoshClient{}, src.BoshClient)
Expand All @@ -121,7 +151,7 @@ func TestConfigToBoshClient(t *testing.T) {
require.Equal(t, "secret", b.ClientSecret)
}

func TestConfigSourceAZToMultipleClusters(t *testing.T) {
func TestVMsToMigrateWithMultipleClusters(t *testing.T) {
c := baseSourceConfig()
src := migrate.NewVMSourceFromConfig(c)
vms, err := src.VMsToMigrate(context.Background())
Expand All @@ -134,7 +164,7 @@ func TestConfigSourceAZToMultipleClusters(t *testing.T) {
require.Equal(t, "Cluster2", vms[0].Clusters[1])
}

func TestConfigSourceFromBosh(t *testing.T) {
func TestVMsToMigrateInterleavesVMsByAZ(t *testing.T) {
c := baseSourceConfig()
c.AdditionalVMs = nil

Expand All @@ -144,43 +174,99 @@ func TestConfigSourceFromBosh(t *testing.T) {
b := &migratefakes.FakeBoshClient{}
b.VMsAndStemcellsReturns([]bosh.VM{
{
Name: "bosh-vm1",
Name: "vm1az1",
AZ: "az1",
},
{
Name: "vm2az1",
AZ: "az1",
},
{
Name: "bosh-vm2",
Name: "vm3az1",
AZ: "az1",
},
{
Name: "bosh-vm3",
Name: "vm4az1",
AZ: "az1",
},
{
Name: "vm1az2",
AZ: "az2",
},
{
Name: "vm2az2",
AZ: "az2",
},
{
Name: "vm3az2",
AZ: "az2",
},
{
Name: "vm1az3",
AZ: "az3",
},
{
Name: "vm2az3",
AZ: "az3",
},
{
Name: "vm3az3",
AZ: "az3",
},
{
Name: "vm4az3",
AZ: "az3",
},
}, nil)
src.BoshClient = b

vms, err := src.VMsToMigrate(context.Background())
require.NoError(t, err)
require.Len(t, vms, 3)
require.Len(t, vms, 11)

vm := vms[0]
require.Equal(t, "az1", vm.AZ)
require.Equal(t, "bosh-vm1", vm.Name)
require.Len(t, vms[0].Clusters, 2)
require.Equal(t, "Cluster1", vm.Clusters[0])
require.Equal(t, "Cluster2", vm.Clusters[1])
require.Equal(t, "vm1az1", vm.Name)

vm = vms[1]
require.Equal(t, "az1", vm.AZ)
require.Equal(t, "bosh-vm2", vm.Name)
require.Len(t, vm.Clusters, 2)
require.Equal(t, "Cluster1", vm.Clusters[0])
require.Equal(t, "Cluster2", vm.Clusters[1])
require.Equal(t, "az2", vm.AZ)
require.Equal(t, "vm1az2", vm.Name)

vm = vms[2]
require.Equal(t, "az3", vm.AZ)
require.Equal(t, "vm1az3", vm.Name)

vm = vms[3]
require.Equal(t, "az1", vm.AZ)
require.Equal(t, "vm2az1", vm.Name)

vm = vms[4]
require.Equal(t, "az2", vm.AZ)
require.Equal(t, "vm2az2", vm.Name)

vm = vms[5]
require.Equal(t, "az3", vm.AZ)
require.Equal(t, "vm2az3", vm.Name)

vm = vms[6]
require.Equal(t, "az1", vm.AZ)
require.Equal(t, "vm3az1", vm.Name)

vm = vms[7]
require.Equal(t, "az2", vm.AZ)
require.Equal(t, "bosh-vm3", vm.Name)
require.Len(t, vm.Clusters, 1)
require.Equal(t, "Cluster3", vm.Clusters[0])
require.Equal(t, "vm3az2", vm.Name)

vm = vms[8]
require.Equal(t, "az3", vm.AZ)
require.Equal(t, "vm3az3", vm.Name)

vm = vms[9]
require.Equal(t, "az1", vm.AZ)
require.Equal(t, "vm4az1", vm.Name)

vm = vms[10]
require.Equal(t, "az3", vm.AZ)
require.Equal(t, "vm4az3", vm.Name)
}

func TestConfigSourceBoshError(t *testing.T) {
Expand Down

0 comments on commit f5f6f45

Please sign in to comment.