func (v *Volume) Parse(u *url.URL) error { // Check the path isn't malformed. if !filepath.IsAbs(u.Path) { return errors.New("invalid uri path") } segments := strings.Split(filepath.Clean(u.Path), "/")[1:] if segments[0] != util.StorageURLPath { return errors.New("not a storage path") } if len(segments) < 3 { return errors.New("uri path mismatch") } store, err := util.VolumeStoreNameToURL(segments[2]) if err != nil { return err } id := segments[3] var SelfLink url.URL SelfLink = *u v.ID = id v.SelfLink = &SelfLink v.Store = store return nil }
//CreateVolume : Create a Volume func (h *StorageHandlersImpl) CreateVolume(params storage.CreateVolumeParams) middleware.Responder { defer trace.End(trace.Begin("storage_handlers.CreateVolume")) //TODO: FIXME: add more errorcodes as we identify error scenarios. storeURL, err := util.VolumeStoreNameToURL(params.VolumeRequest.Store) if err != nil { log.Errorf("storagehandler: VolumeStoreName error: %s", err) return storage.NewCreateVolumeInternalServerError().WithPayload(&models.Error{ Code: swag.Int64(http.StatusInternalServerError), Message: err.Error(), }) } byteMap := make(map[string][]byte) for key, value := range params.VolumeRequest.Metadata { byteMap[key] = []byte(value) } capacity := uint64(0) if params.VolumeRequest.Capacity < 0 { capacity = uint64(1024) //FIXME: this should look for a default cap and set or fail here. } else { capacity = uint64(params.VolumeRequest.Capacity) } op := trace.NewOperation(context.Background(), fmt.Sprintf("VolumeCreate(%s)", params.VolumeRequest.Name)) volume, err := h.volumeCache.VolumeCreate(op, params.VolumeRequest.Name, storeURL, capacity*1024, byteMap) if err != nil { log.Errorf("storagehandler: VolumeCreate error: %#v", err) if os.IsExist(err) { return storage.NewCreateVolumeConflict().WithPayload(&models.Error{ Code: swag.Int64(http.StatusConflict), Message: err.Error(), }) } if _, ok := err.(spl.VolumeStoreNotFoundError); ok { return storage.NewCreateVolumeNotFound().WithPayload(&models.Error{ Code: swag.Int64(http.StatusNotFound), Message: err.Error(), }) } return storage.NewCreateVolumeInternalServerError().WithPayload(&models.Error{ Code: swag.Int64(http.StatusInternalServerError), Message: err.Error(), }) } response := volumeToCreateResponse(volume, params.VolumeRequest) return storage.NewCreateVolumeCreated().WithPayload(&response) }
// AddStore adds a volumestore by uri. // // ds is the Datastore (+ path) volumes will be created under. // The resulting path will be parentDir/VIC/volumes. // storeName is the name used to refer to the datastore + path (ds above). // // returns the URL used to refer to the volume store func (v *VolumeStore) AddStore(ctx context.Context, ds *datastore.Helper, storeName string) (*url.URL, error) { u, err := util.VolumeStoreNameToURL(storeName) if err != nil { return nil, err } if _, ok := v.ds[*u]; ok { return nil, fmt.Errorf("volumestore (%s) already added", u.String()) } if _, err = ds.Mkdir(ctx, true, volumesDir); err != nil { return nil, err } v.ds[*u] = ds return u, nil }
// AddStore adds a volumestore by uri. // // ds is the Datastore (+ path) volumes will be created under. // The resulting path will be parentDir/VIC/volumes. // storeName is the name used to refer to the datastore + path (ds above). // // returns the URL used to refer to the volume store func (v *VolumeStore) AddStore(op trace.Operation, ds *datastore.Helper, storeName string) (*url.URL, error) { v.dsLock.Lock() defer v.dsLock.Unlock() u, err := util.VolumeStoreNameToURL(storeName) if err != nil { return nil, err } if _, ok := v.ds[*u]; ok { return nil, fmt.Errorf("volumestore (%s) already added", u.String()) } if _, err = ds.Mkdir(op, true, VolumesDir); err != nil && !os.IsExist(err) { return nil, err } v.ds[*u] = ds return u, nil }
// Create 2 store caches but use the same backing datastore. Create images // with the first cache, then get the image with the second. This simulates // restart since the second cache is empty and has to go to the backing store. func TestVolumeCacheRestart(t *testing.T) { mvs := NewMockVolumeStore() op := trace.NewOperation(context.Background(), "test") firstCache, err := NewVolumeLookupCache(op, mvs) if !assert.NoError(t, err) || !assert.NotNil(t, firstCache) { return } storeURL, err := util.VolumeStoreNameToURL("testStore") if !assert.NoError(t, err) || !assert.NotNil(t, storeURL) { return } // Create a set of volumes inVols := make(map[string]*Volume) for i := 1; i < 50; i++ { id := fmt.Sprintf("ID-%d", i) // Write to the datastore vol, err := firstCache.VolumeCreate(op, id, storeURL, 0, nil) if !assert.NoError(t, err) || !assert.NotNil(t, vol) { return } inVols[id] = vol } secondCache, err := NewVolumeLookupCache(op, mvs) if !assert.NoError(t, err) || !assert.NotNil(t, secondCache) { return } // get the vols from the second cache to ensure it goes to the ds for _, expectedVol := range inVols { vol, err := secondCache.VolumeGet(op, expectedVol.ID) if !assert.NoError(t, err) || !assert.Equal(t, expectedVol, vol) { return } } }
//CreateVolume : Create a Volume func (handler *StorageHandlersImpl) CreateVolume(params storage.CreateVolumeParams) middleware.Responder { defer trace.End(trace.Begin("storage_handlers.CreateVolume")) //TODO: FIXME: add more errorcodes as we identify error scenarios. storeURL, err := util.VolumeStoreNameToURL(params.VolumeRequest.Store) if err != nil { log.Errorf("storagehandler: VolumeStoreName error: %s", err) return storage.NewCreateVolumeInternalServerError().WithPayload(&models.Error{ Code: swag.Int64(http.StatusInternalServerError), Message: err.Error(), }) } byteMap := make(map[string][]byte) for key, value := range params.VolumeRequest.Metadata { byteMap[key] = []byte(value) } capacity := uint64(0) if params.VolumeRequest.Capacity < 0 { capacity = uint64(1024) //FIXME: this should look for a default cap and set or fail here. } else { capacity = uint64(params.VolumeRequest.Capacity) } volume, err := storageVolumeLayer.VolumeCreate(context.TODO(), params.VolumeRequest.Name, storeURL, capacity*1024, byteMap) if err != nil { log.Errorf("storagehandler: VolumeCreate error: %s", err) return storage.NewCreateVolumeInternalServerError().WithPayload(&models.Error{ Code: swag.Int64(http.StatusInternalServerError), Message: err.Error(), }) } response := volumeToCreateResponse(volume, params.VolumeRequest) return storage.NewCreateVolumeCreated().WithPayload(&response) }
func TestVolumeCreateGetListAndDelete(t *testing.T) { op := trace.NewOperation(context.Background(), "test") mvs := NewMockVolumeStore() v, err := NewVolumeLookupCache(op, mvs) if !assert.NoError(t, err) { return } storeURL, err := util.VolumeStoreNameToURL("testStore") if !assert.NoError(t, err) || !assert.NotNil(t, storeURL) { return } inVols := make(map[string]*Volume) inVolsM := &sync.Mutex{} wg := &sync.WaitGroup{} createFn := func(i int) { defer wg.Done() id := fmt.Sprintf("ID-%d", i) // Write to the datastore vol, err := v.VolumeCreate(op, id, storeURL, 0, nil) if !assert.NoError(t, err) || !assert.NotNil(t, vol) { return } inVolsM.Lock() inVols[id] = vol inVolsM.Unlock() } // Create a set of volumes numVolumes := 5 wg.Add(numVolumes) for i := 0; i < numVolumes; i++ { go createFn(i) } wg.Wait() getFn := func(inVol *Volume) { vol, err := v.VolumeGet(op, inVol.ID) if !assert.NoError(t, err) || !assert.NotNil(t, vol) { return } if !assert.Equal(t, inVol, vol) { return } wg.Done() } wg.Add(numVolumes) for _, inVol := range inVols { getFn(inVol) } wg.Wait() volumeList, err := v.VolumesList(op) if !assert.NoError(t, err) || !assert.Equal(t, numVolumes, len(volumeList)) { return } // Test that the list returned by VolumeList matches our inVols list. Then // delete each vol via the cache, then check the datastore to ensure it's // empty for _, outVol := range volumeList { if !assert.Equal(t, inVols[outVol.ID], outVol) { return } if err = v.VolumeDestroy(op, outVol.ID); !assert.NoError(t, err) { return } } // check the datastore is empty. if !assert.Empty(t, mvs.db) { return } }