Skip to content
This repository was archived by the owner on Mar 26, 2020. It is now read-only.

Commit 9b6e78f

Browse files
Oshank Kumararavindavk
authored andcommitted
gd2 plugin: added a plugin for block volume management
- added APIs for creation,deleting and listing block volumes. - added pluggable interface for block volume providers. Refer Design Doc: #1319 Signed-off-by: Oshank Kumar <[email protected]>
1 parent 0839909 commit 9b6e78f

File tree

17 files changed

+1360
-71
lines changed

17 files changed

+1360
-71
lines changed

Gopkg.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Gopkg.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@
4848

4949
[[constraint]]
5050
name = "github.com/sirupsen/logrus"
51-
version = "~1.0.3"
51+
version = "~1.2.0"
5252

5353
[[constraint]]
5454
name = "github.com/cockroachdb/cmux"

glusterd2/commands/volumes/volume-create.go

Lines changed: 45 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package volumecommands
22

33
import (
4+
"context"
45
"errors"
56
"net/http"
67
"path/filepath"
@@ -102,9 +103,6 @@ func registerVolCreateStepFuncs() {
102103
func volumeCreateHandler(w http.ResponseWriter, r *http.Request) {
103104

104105
ctx := r.Context()
105-
ctx, span := trace.StartSpan(ctx, "/volumeCreateHandler")
106-
defer span.End()
107-
108106
logger := gdctx.GetReqLogger(ctx)
109107
var err error
110108

@@ -114,45 +112,68 @@ func volumeCreateHandler(w http.ResponseWriter, r *http.Request) {
114112
return
115113
}
116114

117-
if err := validateVolCreateReq(&req); err != nil {
118-
restutils.SendHTTPError(ctx, w, http.StatusBadRequest, err)
115+
if status, err := CreateVolume(ctx, req); err != nil {
116+
restutils.SendHTTPError(ctx, w, status, err)
119117
return
120118
}
121119

122-
if containsReservedGroupProfile(req.Options) {
123-
restutils.SendHTTPError(ctx, w, http.StatusBadRequest, gderrors.ErrReservedGroupProfile)
120+
volinfo, err := volume.GetVolume(req.Name)
121+
if err != nil {
122+
// FIXME: If volume was created successfully in the txn above and
123+
// then the store goes down by the time we reach here, what do
124+
// we return to the client ?
125+
restutils.SendHTTPError(ctx, w, http.StatusInternalServerError, err)
124126
return
125127
}
126128

129+
logger.WithField("volume-name", volinfo.Name).Info("new volume created")
130+
events.Broadcast(volume.NewEvent(volume.EventVolumeCreated, volinfo))
131+
132+
resp := createVolumeCreateResp(volinfo)
133+
restutils.SetLocationHeader(r, w, volinfo.Name)
134+
restutils.SendHTTPResponse(ctx, w, http.StatusCreated, resp)
135+
}
136+
137+
func createVolumeCreateResp(v *volume.Volinfo) *api.VolumeCreateResp {
138+
return (*api.VolumeCreateResp)(volume.CreateVolumeInfoResp(v))
139+
}
140+
141+
// CreateVolume creates a volume
142+
func CreateVolume(ctx context.Context, req api.VolCreateReq) (status int, err error) {
143+
ctx, span := trace.StartSpan(ctx, "/volumeCreateHandler")
144+
defer span.End()
145+
146+
if err := validateVolCreateReq(&req); err != nil {
147+
return http.StatusBadRequest, err
148+
}
149+
150+
if containsReservedGroupProfile(req.Options) {
151+
return http.StatusBadRequest, gderrors.ErrReservedGroupProfile
152+
}
153+
127154
if req.Size > 0 {
128155
applyDefaults(&req)
129156

130157
if req.SnapshotReserveFactor < 1 {
131-
restutils.SendHTTPError(ctx, w, http.StatusBadRequest,
132-
errors.New("invalid snapshot reserve factor"))
133-
return
158+
return http.StatusBadRequest, errors.New("invalid snapshot reserve factor")
134159
}
135160

136161
if err := bricksplanner.PlanBricks(&req); err != nil {
137-
restutils.SendHTTPError(ctx, w, http.StatusInternalServerError, err)
138-
return
162+
return http.StatusInternalServerError, err
139163
}
140164
} else {
141165
if err := checkDupBrickEntryVolCreate(req); err != nil {
142-
restutils.SendHTTPError(ctx, w, http.StatusBadRequest, err)
143-
return
166+
return http.StatusBadRequest, err
144167
}
145168
}
146169

147170
req.Options, err = expandGroupOptions(req.Options)
148171
if err != nil {
149-
restutils.SendHTTPError(ctx, w, http.StatusInternalServerError, err)
150-
return
172+
return http.StatusInternalServerError, err
151173
}
152174

153175
if err := validateOptions(req.Options, req.VolOptionFlags); err != nil {
154-
restutils.SendHTTPError(ctx, w, http.StatusBadRequest, err)
155-
return
176+
return http.StatusBadRequest, err
156177
}
157178

158179
// Include default Volume Options profile
@@ -171,21 +192,17 @@ func volumeCreateHandler(w http.ResponseWriter, r *http.Request) {
171192

172193
nodes, err := req.Nodes()
173194
if err != nil {
174-
restutils.SendHTTPError(ctx, w, http.StatusBadRequest, err)
175-
return
195+
return http.StatusBadRequest, err
176196
}
177197

178198
txn, err := transactionv2.NewTxnWithLocks(ctx, req.Name)
179199
if err != nil {
180-
status, err := restutils.ErrToStatusCode(err)
181-
restutils.SendHTTPError(ctx, w, status, err)
182-
return
200+
return restutils.ErrToStatusCode(err)
183201
}
184202
defer txn.Done()
185203

186204
if volume.Exists(req.Name) {
187-
restutils.SendHTTPError(ctx, w, http.StatusBadRequest, gderrors.ErrVolExists)
188-
return
205+
return http.StatusBadRequest, gderrors.ErrVolExists
189206
}
190207

191208
txn.Steps = []*transaction.Step{
@@ -219,8 +236,7 @@ func volumeCreateHandler(w http.ResponseWriter, r *http.Request) {
219236
}
220237

221238
if err := txn.Ctx.Set("req", &req); err != nil {
222-
restutils.SendHTTPError(ctx, w, http.StatusInternalServerError, err)
223-
return
239+
return http.StatusInternalServerError, err
224240
}
225241

226242
// Add attributes to the span with info that can be viewed along with traces.
@@ -231,28 +247,8 @@ func volumeCreateHandler(w http.ResponseWriter, r *http.Request) {
231247
)
232248

233249
if err := txn.Do(); err != nil {
234-
status, err := restutils.ErrToStatusCode(err)
235-
restutils.SendHTTPError(ctx, w, status, err)
236-
return
250+
return restutils.ErrToStatusCode(err)
237251
}
238252

239-
volinfo, err := volume.GetVolume(req.Name)
240-
if err != nil {
241-
// FIXME: If volume was created successfully in the txn above and
242-
// then the store goes down by the time we reach here, what do
243-
// we return to the client ?
244-
restutils.SendHTTPError(ctx, w, http.StatusInternalServerError, err)
245-
return
246-
}
247-
248-
logger.WithField("volume-name", volinfo.Name).Info("new volume created")
249-
events.Broadcast(volume.NewEvent(volume.EventVolumeCreated, volinfo))
250-
251-
resp := createVolumeCreateResp(volinfo)
252-
restutils.SetLocationHeader(r, w, volinfo.Name)
253-
restutils.SendHTTPResponse(ctx, w, http.StatusCreated, resp)
254-
}
255-
256-
func createVolumeCreateResp(v *volume.Volinfo) *api.VolumeCreateResp {
257-
return (*api.VolumeCreateResp)(volume.CreateVolumeInfoResp(v))
253+
return http.StatusCreated, nil
258254
}

glusterd2/commands/volumes/volume-start.go

Lines changed: 25 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -129,10 +129,6 @@ func registerVolStartStepFuncs() {
129129
func volumeStartHandler(w http.ResponseWriter, r *http.Request) {
130130

131131
ctx := r.Context()
132-
ctx, span := trace.StartSpan(ctx, "/volumeStartHandler")
133-
defer span.End()
134-
135-
logger := gdctx.GetReqLogger(ctx)
136132
volname := mux.Vars(r)["volname"]
137133
var req api.VolumeStartReq
138134

@@ -142,24 +138,39 @@ func volumeStartHandler(w http.ResponseWriter, r *http.Request) {
142138
return
143139
}
144140

145-
txn, err := transactionv2.NewTxnWithLocks(ctx, volname)
141+
volinfo, status, err := StartVolume(ctx, volname, req)
146142
if err != nil {
147-
status, err := restutils.ErrToStatusCode(err)
148143
restutils.SendHTTPError(ctx, w, status, err)
149144
return
150145
}
146+
147+
events.Broadcast(volume.NewEvent(volume.EventVolumeStarted, volinfo))
148+
149+
resp := createVolumeStartResp(volinfo)
150+
restutils.SendHTTPResponse(ctx, w, http.StatusOK, resp)
151+
}
152+
153+
// StartVolume starts a volume
154+
func StartVolume(ctx context.Context, volname string, req api.VolumeStartReq) (volInfo *volume.Volinfo, status int, err error) {
155+
logger := gdctx.GetReqLogger(ctx)
156+
ctx, span := trace.StartSpan(ctx, "/volumeStartHandler")
157+
defer span.End()
158+
159+
txn, err := transactionv2.NewTxnWithLocks(ctx, volname)
160+
if err != nil {
161+
status, err := restutils.ErrToStatusCode(err)
162+
return nil, status, err
163+
}
151164
defer txn.Done()
152165

153166
volinfo, err := volume.GetVolume(volname)
154167
if err != nil {
155168
status, err := restutils.ErrToStatusCode(err)
156-
restutils.SendHTTPError(ctx, w, status, err)
157-
return
169+
return nil, status, err
158170
}
159171

160172
if volinfo.State == volume.VolStarted && !req.ForceStartBricks {
161-
restutils.SendHTTPError(ctx, w, http.StatusBadRequest, errors.ErrVolAlreadyStarted)
162-
return
173+
return nil, http.StatusBadRequest, errors.ErrVolAlreadyStarted
163174
}
164175

165176
txn.Steps = []*transaction.Step{
@@ -182,15 +193,13 @@ func volumeStartHandler(w http.ResponseWriter, r *http.Request) {
182193
}
183194

184195
if err := txn.Ctx.Set("oldvolinfo", volinfo); err != nil {
185-
restutils.SendHTTPError(ctx, w, http.StatusInternalServerError, err)
186-
return
196+
return nil, http.StatusInternalServerError, err
187197
}
188198

189199
volinfo.State = volume.VolStarted
190200

191201
if err := txn.Ctx.Set("volinfo", volinfo); err != nil {
192-
restutils.SendHTTPError(ctx, w, http.StatusInternalServerError, err)
193-
return
202+
return nil, http.StatusInternalServerError, err
194203
}
195204

196205
span.AddAttributes(
@@ -201,14 +210,10 @@ func volumeStartHandler(w http.ResponseWriter, r *http.Request) {
201210
if err := txn.Do(); err != nil {
202211
logger.WithError(err).WithField(
203212
"volume", volname).Error("transaction to start volume failed")
204-
restutils.SendHTTPError(ctx, w, http.StatusInternalServerError, err)
205-
return
213+
return nil, http.StatusInternalServerError, err
206214
}
207215

208-
events.Broadcast(volume.NewEvent(volume.EventVolumeStarted, volinfo))
209-
210-
resp := createVolumeStartResp(volinfo)
211-
restutils.SendHTTPResponse(ctx, w, http.StatusOK, resp)
216+
return volinfo, http.StatusOK, nil
212217
}
213218

214219
func createVolumeStartResp(v *volume.Volinfo) *api.VolumeStartResp {

glusterd2/plugin/plugins.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ package plugin
44

55
import (
66
"github.com/gluster/glusterd2/plugins/bitrot"
7+
"github.com/gluster/glusterd2/plugins/blockvolume"
78
"github.com/gluster/glusterd2/plugins/device"
89
"github.com/gluster/glusterd2/plugins/events"
910
"github.com/gluster/glusterd2/plugins/georeplication"
@@ -25,4 +26,5 @@ var PluginsList = []GlusterdPlugin{
2526
&glustershd.Plugin{},
2627
&device.Plugin{},
2728
&rebalance.Plugin{},
29+
&blockvolume.BlockVolume{},
2830
}

glusterd2/volume/filters.go

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package volume
2+
3+
const (
4+
// BlockHosted is plugin name for FilterBlockHostedVolumes
5+
BlockHosted = "block-hosted"
6+
)
7+
8+
// Filter will receive a slice of *Volinfo and filters out the undesired one and return slice of desired one only
9+
type Filter func([]*Volinfo) []*Volinfo
10+
11+
var filters = make(map[string]Filter)
12+
13+
// InstallFilter will register a custom Filter
14+
func InstallFilter(name string, f Filter) {
15+
filters[name] = f
16+
}
17+
18+
// ApplyFilters applies all registered filters passed in the args to a slice of *Volinfo
19+
func ApplyFilters(volumes []*Volinfo, names ...string) []*Volinfo {
20+
for _, name := range names {
21+
if filter, found := filters[name]; found {
22+
volumes = filter(volumes)
23+
}
24+
}
25+
return volumes
26+
}
27+
28+
// ApplyCustomFilters applies all custom filter to a slice of *Volinfo
29+
func ApplyCustomFilters(volumes []*Volinfo, filters ...Filter) []*Volinfo {
30+
for _, filter := range filters {
31+
volumes = filter(volumes)
32+
}
33+
34+
return volumes
35+
}
36+
37+
// FilterBlockHostedVolumes filters out volume which are suitable for hosting block volume
38+
func FilterBlockHostedVolumes(volumes []*Volinfo) []*Volinfo {
39+
var volInfos []*Volinfo
40+
for _, volume := range volumes {
41+
val, found := volume.Metadata["block-hosting"]
42+
if found && val == "yes" {
43+
volInfos = append(volInfos, volume)
44+
}
45+
}
46+
return volInfos
47+
}
48+
49+
func init() {
50+
InstallFilter(BlockHosted, FilterBlockHostedVolumes)
51+
}

0 commit comments

Comments
 (0)