1// Copyright 2016-2018 VMware, Inc. All Rights Reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//    http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package vsphere
16
17import (
18	"context"
19	"fmt"
20	"sync"
21	"testing"
22
23	"github.com/stretchr/testify/assert"
24
25	"github.com/vmware/govmomi/object"
26	"github.com/vmware/vic/lib/portlayer/storage/volume"
27	"github.com/vmware/vic/lib/portlayer/storage/volume/cache"
28	"github.com/vmware/vic/pkg/trace"
29	"github.com/vmware/vic/pkg/vsphere/datastore"
30	"github.com/vmware/vic/pkg/vsphere/datastore/test"
31	"github.com/vmware/vic/pkg/vsphere/tasks"
32)
33
34func TestVolumeCreateListAndRestart(t *testing.T) {
35	client := test.Session(context.TODO(), t)
36	if client == nil {
37		return
38	}
39
40	op := trace.NewOperation(context.Background(), "test")
41
42	// Root our datastore
43	testStorePath := datastore.TestName("voltest")
44	ds, err := datastore.NewHelper(op, client, client.Datastore, testStorePath)
45	if !assert.NoError(t, err) || !assert.NotNil(t, ds) {
46		return
47	}
48
49	// Create the backing store on vsphere
50	DetachAll = false
51	vsVolumeStore, err := NewVolumeStore(op, "testStoreName", client, ds)
52	if !assert.NoError(t, err) || !assert.NotNil(t, vsVolumeStore) {
53		return
54	}
55
56	// Clean up the mess
57	defer func() {
58		fm := object.NewFileManager(client.Vim25())
59		tasks.WaitForResult(context.TODO(), func(ctx context.Context) (tasks.Task, error) {
60			return fm.DeleteDatastoreFile(ctx, client.Datastore.Path(testStorePath), client.Datacenter)
61		})
62	}()
63
64	// Create the cache
65	firstCache := cache.NewVolumeLookupCache(op)
66	if !assert.NotNil(t, firstCache) {
67		return
68	}
69
70	// add the vs to the cache and assert the url matches
71	storeURL, err := firstCache.AddStore(op, "testStoreName", vsVolumeStore)
72	if !assert.NoError(t, err) || !assert.Equal(t, vsVolumeStore.SelfLink, storeURL) {
73		return
74	}
75
76	// test we can list it
77	m, err := firstCache.VolumeStoresList(op)
78	if !assert.NoError(t, err) || !assert.Len(t, m, 1) || !assert.Equal(t, m[0], "testStoreName") {
79		return
80	}
81
82	// Create the volumes (in parallel)
83	numVols := 5
84	wg := &sync.WaitGroup{}
85	wg.Add(numVols)
86	volumes := make(map[string]*volume.Volume)
87	for i := 0; i < numVols; i++ {
88		go func(idx int) {
89			defer wg.Done()
90			ID := fmt.Sprintf("testvolume-%d", idx)
91
92			// add some metadata if i is even
93			var info map[string][]byte
94
95			if idx%2 == 0 {
96				info = make(map[string][]byte)
97				info[ID] = []byte(ID)
98			}
99
100			outVol, err := firstCache.VolumeCreate(op, ID, storeURL, 10240, info)
101			if !assert.NoError(t, err) || !assert.NotNil(t, outVol) {
102				return
103			}
104
105			volumes[ID] = outVol
106		}(i)
107	}
108
109	wg.Wait()
110
111	// list using the datastore (skipping the cache)
112	outVols, err := vsVolumeStore.VolumesList(op)
113	if !assert.NoError(t, err) || !assert.NotNil(t, outVols) || !assert.Equal(t, numVols, len(outVols)) {
114		return
115	}
116
117	for _, outVol := range outVols {
118		if !assert.Equal(t, volumes[outVol.ID], outVol) {
119			return
120		}
121	}
122
123	// Test restart
124
125	// Create a new vs and cache to the same datastore (simulating restart) and compare
126	secondVStore, err := NewVolumeStore(op, "testStoreName", client, ds)
127	if !assert.NoError(t, err) || !assert.NotNil(t, vsVolumeStore) {
128		return
129	}
130
131	secondCache := cache.NewVolumeLookupCache(op)
132	if !assert.NotNil(t, secondCache) {
133		return
134	}
135
136	_, err = secondCache.AddStore(op, "testStore", secondVStore)
137	if !assert.NoError(t, err) {
138		return
139	}
140
141	secondOutVols, err := secondCache.VolumesList(op)
142	if !assert.NoError(t, err) || !assert.NotNil(t, secondOutVols) || !assert.Equal(t, numVols, len(secondOutVols)) {
143		return
144	}
145
146	for _, outVol := range secondOutVols {
147		// XXX we could compare the Volumes, but the paths are different the
148		// second time around on vsan since the vsan UUID is not included.
149		if !assert.NotEmpty(t, volumes[outVol.ID].Device.DiskPath()) {
150			return
151		}
152	}
153}
154