Skip to content

Commit f5f6f45

Browse files
committed
Add VM migration ordering interleaved by AZ
This fixes issue #5
1 parent 6263829 commit f5f6f45

File tree

2 files changed

+149
-22
lines changed

2 files changed

+149
-22
lines changed

pkg/migrate/vm_source.go

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@ package migrate
77

88
import (
99
"context"
10+
"fmt"
1011
"github.com/vmware-tanzu/vmotion-migration-tool-for-bosh-deployments/pkg/bosh"
1112
"github.com/vmware-tanzu/vmotion-migration-tool-for-bosh-deployments/pkg/config"
13+
"sort"
1214
)
1315

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

6062
var vms []VM
6163
for _, bvm := range boshVMs {
64+
clusters := s.srcAZsToClusters[bvm.AZ]
65+
if len(clusters) == 0 {
66+
return nil, fmt.Errorf("found BOSH VM '%s' with AZ '%s' but no source clusters in the config for that AZ",
67+
bvm.Name, bvm.AZ)
68+
}
6269
vms = append(vms, VM{
6370
Name: bvm.Name,
6471
AZ: bvm.AZ,
65-
Clusters: s.srcAZsToClusters[bvm.AZ],
72+
Clusters: clusters,
6673
})
6774
}
6875
vms = append(vms, s.additionalVMs...)
69-
return vms, nil
76+
return s.interleaveVMsByAZ(vms), nil
77+
}
78+
79+
func (s *VMSource) interleaveVMsByAZ(vms []VM) []VM {
80+
// sort all VMs into buckets by AZ
81+
vmsByAZ := make(map[string][]VM)
82+
for _, v := range vms {
83+
if vmsByAZ[v.AZ] == nil {
84+
vmsByAZ[v.AZ] = make([]VM, 0)
85+
}
86+
vmsByAZ[v.AZ] = append(vmsByAZ[v.AZ], v)
87+
}
88+
89+
// create a stable list of all unique AZs
90+
var azs []string
91+
for az := range vmsByAZ {
92+
azs = append(azs, az)
93+
}
94+
sort.Strings(azs)
95+
96+
// create a new list of VMs interleaved by AZ
97+
// pull one VM from each AZ, then start it all over again until we run out of VMs
98+
more := true
99+
var sortedVMs []VM
100+
for i := 0; more; i++ {
101+
more = false
102+
for _, az := range azs {
103+
l := vmsByAZ[az]
104+
if i < len(l) {
105+
more = true
106+
sortedVMs = append(sortedVMs, l[i])
107+
}
108+
}
109+
}
110+
return sortedVMs
70111
}
71112

72113
func configToAdditionalVMs(c config.Config, srcAZToClusters map[string][]string) []VM {

pkg/migrate/vm_source_test.go

Lines changed: 106 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,21 @@ func baseSourceConfig() config.Config {
6262
},
6363
},
6464
},
65+
{
66+
Name: "az3",
67+
VCenter: &config.VCenter{
68+
Host: "vcenter1.example.com",
69+
Username: "admin1",
70+
Password: "secret1",
71+
Datacenter: "DC1",
72+
},
73+
Clusters: []config.ComputeCluster{
74+
{
75+
Name: "Cluster4",
76+
ResourcePool: "RP1",
77+
},
78+
},
79+
},
6580
},
6681
Target: []config.ComputeAZ{
6782
{
@@ -90,7 +105,22 @@ func baseSourceConfig() config.Config {
90105
Clusters: []config.ComputeCluster{
91106
{
92107
Name: "Cluster5",
93-
ResourcePool: "RP5",
108+
ResourcePool: "RP1",
109+
},
110+
},
111+
},
112+
{
113+
Name: "az2",
114+
VCenter: &config.VCenter{
115+
Host: "vcenter2.example.com",
116+
Username: "admin2",
117+
Password: "secret2",
118+
Datacenter: "DC2",
119+
},
120+
Clusters: []config.ComputeCluster{
121+
{
122+
Name: "Cluster6",
123+
ResourcePool: "RP1",
94124
},
95125
},
96126
},
@@ -104,7 +134,7 @@ func baseSourceConfig() config.Config {
104134
}
105135
}
106136

107-
func TestConfigToBoshClient(t *testing.T) {
137+
func TestNewVMSourceFromConfigBoshClient(t *testing.T) {
108138
c := baseSourceConfig()
109139
src := migrate.NewVMSourceFromConfig(c)
110140
require.IsType(t, migrate.NullBoshClient{}, src.BoshClient)
@@ -121,7 +151,7 @@ func TestConfigToBoshClient(t *testing.T) {
121151
require.Equal(t, "secret", b.ClientSecret)
122152
}
123153

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

137-
func TestConfigSourceFromBosh(t *testing.T) {
167+
func TestVMsToMigrateInterleavesVMsByAZ(t *testing.T) {
138168
c := baseSourceConfig()
139169
c.AdditionalVMs = nil
140170

@@ -144,43 +174,99 @@ func TestConfigSourceFromBosh(t *testing.T) {
144174
b := &migratefakes.FakeBoshClient{}
145175
b.VMsAndStemcellsReturns([]bosh.VM{
146176
{
147-
Name: "bosh-vm1",
177+
Name: "vm1az1",
178+
AZ: "az1",
179+
},
180+
{
181+
Name: "vm2az1",
148182
AZ: "az1",
149183
},
150184
{
151-
Name: "bosh-vm2",
185+
Name: "vm3az1",
152186
AZ: "az1",
153187
},
154188
{
155-
Name: "bosh-vm3",
189+
Name: "vm4az1",
190+
AZ: "az1",
191+
},
192+
{
193+
Name: "vm1az2",
194+
AZ: "az2",
195+
},
196+
{
197+
Name: "vm2az2",
198+
AZ: "az2",
199+
},
200+
{
201+
Name: "vm3az2",
156202
AZ: "az2",
157203
},
204+
{
205+
Name: "vm1az3",
206+
AZ: "az3",
207+
},
208+
{
209+
Name: "vm2az3",
210+
AZ: "az3",
211+
},
212+
{
213+
Name: "vm3az3",
214+
AZ: "az3",
215+
},
216+
{
217+
Name: "vm4az3",
218+
AZ: "az3",
219+
},
158220
}, nil)
159221
src.BoshClient = b
160222

161223
vms, err := src.VMsToMigrate(context.Background())
162224
require.NoError(t, err)
163-
require.Len(t, vms, 3)
225+
require.Len(t, vms, 11)
164226

165227
vm := vms[0]
166228
require.Equal(t, "az1", vm.AZ)
167-
require.Equal(t, "bosh-vm1", vm.Name)
168-
require.Len(t, vms[0].Clusters, 2)
169-
require.Equal(t, "Cluster1", vm.Clusters[0])
170-
require.Equal(t, "Cluster2", vm.Clusters[1])
229+
require.Equal(t, "vm1az1", vm.Name)
171230

172231
vm = vms[1]
173-
require.Equal(t, "az1", vm.AZ)
174-
require.Equal(t, "bosh-vm2", vm.Name)
175-
require.Len(t, vm.Clusters, 2)
176-
require.Equal(t, "Cluster1", vm.Clusters[0])
177-
require.Equal(t, "Cluster2", vm.Clusters[1])
232+
require.Equal(t, "az2", vm.AZ)
233+
require.Equal(t, "vm1az2", vm.Name)
178234

179235
vm = vms[2]
236+
require.Equal(t, "az3", vm.AZ)
237+
require.Equal(t, "vm1az3", vm.Name)
238+
239+
vm = vms[3]
240+
require.Equal(t, "az1", vm.AZ)
241+
require.Equal(t, "vm2az1", vm.Name)
242+
243+
vm = vms[4]
244+
require.Equal(t, "az2", vm.AZ)
245+
require.Equal(t, "vm2az2", vm.Name)
246+
247+
vm = vms[5]
248+
require.Equal(t, "az3", vm.AZ)
249+
require.Equal(t, "vm2az3", vm.Name)
250+
251+
vm = vms[6]
252+
require.Equal(t, "az1", vm.AZ)
253+
require.Equal(t, "vm3az1", vm.Name)
254+
255+
vm = vms[7]
180256
require.Equal(t, "az2", vm.AZ)
181-
require.Equal(t, "bosh-vm3", vm.Name)
182-
require.Len(t, vm.Clusters, 1)
183-
require.Equal(t, "Cluster3", vm.Clusters[0])
257+
require.Equal(t, "vm3az2", vm.Name)
258+
259+
vm = vms[8]
260+
require.Equal(t, "az3", vm.AZ)
261+
require.Equal(t, "vm3az3", vm.Name)
262+
263+
vm = vms[9]
264+
require.Equal(t, "az1", vm.AZ)
265+
require.Equal(t, "vm4az1", vm.Name)
266+
267+
vm = vms[10]
268+
require.Equal(t, "az3", vm.AZ)
269+
require.Equal(t, "vm4az3", vm.Name)
184270
}
185271

186272
func TestConfigSourceBoshError(t *testing.T) {

0 commit comments

Comments
 (0)