Skip to content

Commit 40c83bf

Browse files
author
Oshank Kumar
committed
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: gluster#1319 Signed-off-by: Oshank Kumar <[email protected]>
1 parent 5dd1254 commit 40c83bf

File tree

17 files changed

+1361
-71
lines changed

17 files changed

+1361
-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: 26 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -129,9 +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-
135132
logger := gdctx.GetReqLogger(ctx)
136133
volname := mux.Vars(r)["volname"]
137134
var req api.VolumeStartReq
@@ -142,24 +139,40 @@ func volumeStartHandler(w http.ResponseWriter, r *http.Request) {
142139
return
143140
}
144141

145-
txn, err := transactionv2.NewTxnWithLocks(ctx, volname)
142+
volinfo, status, err := StartVolume(ctx, volname, req)
146143
if err != nil {
147-
status, err := restutils.ErrToStatusCode(err)
148144
restutils.SendHTTPError(ctx, w, status, err)
149145
return
150146
}
147+
148+
logger.WithField("volume-name", volinfo.Name).Info("volume started")
149+
events.Broadcast(volume.NewEvent(volume.EventVolumeStarted, volinfo))
150+
151+
resp := createVolumeStartResp(volinfo)
152+
restutils.SendHTTPResponse(ctx, w, http.StatusOK, resp)
153+
}
154+
155+
// StartVolume starts a volume
156+
func StartVolume(ctx context.Context, volname string, req api.VolumeStartReq) (volInfo *volume.Volinfo, status int, err error) {
157+
logger := gdctx.GetReqLogger(ctx)
158+
ctx, span := trace.StartSpan(ctx, "/volumeStartHandler")
159+
defer span.End()
160+
161+
txn, err := transactionv2.NewTxnWithLocks(ctx, volname)
162+
if err != nil {
163+
status, err := restutils.ErrToStatusCode(err)
164+
return nil, status, err
165+
}
151166
defer txn.Done()
152167

153168
volinfo, err := volume.GetVolume(volname)
154169
if err != nil {
155170
status, err := restutils.ErrToStatusCode(err)
156-
restutils.SendHTTPError(ctx, w, status, err)
157-
return
171+
return nil, status, err
158172
}
159173

160174
if volinfo.State == volume.VolStarted && !req.ForceStartBricks {
161-
restutils.SendHTTPError(ctx, w, http.StatusBadRequest, errors.ErrVolAlreadyStarted)
162-
return
175+
return nil, http.StatusBadRequest, errors.ErrVolAlreadyStarted
163176
}
164177

165178
txn.Steps = []*transaction.Step{
@@ -182,15 +195,13 @@ func volumeStartHandler(w http.ResponseWriter, r *http.Request) {
182195
}
183196

184197
if err := txn.Ctx.Set("oldvolinfo", volinfo); err != nil {
185-
restutils.SendHTTPError(ctx, w, http.StatusInternalServerError, err)
186-
return
198+
return nil, http.StatusInternalServerError, err
187199
}
188200

189201
volinfo.State = volume.VolStarted
190202

191203
if err := txn.Ctx.Set("volinfo", volinfo); err != nil {
192-
restutils.SendHTTPError(ctx, w, http.StatusInternalServerError, err)
193-
return
204+
return nil, http.StatusInternalServerError, err
194205
}
195206

196207
span.AddAttributes(
@@ -201,15 +212,10 @@ func volumeStartHandler(w http.ResponseWriter, r *http.Request) {
201212
if err := txn.Do(); err != nil {
202213
logger.WithError(err).WithField(
203214
"volume", volname).Error("transaction to start volume failed")
204-
restutils.SendHTTPError(ctx, w, http.StatusInternalServerError, err)
205-
return
215+
return nil, http.StatusInternalServerError, err
206216
}
207217

208-
logger.WithField("volume-name", volinfo.Name).Info("volume started")
209-
events.Broadcast(volume.NewEvent(volume.EventVolumeStarted, volinfo))
210-
211-
resp := createVolumeStartResp(volinfo)
212-
restutils.SendHTTPResponse(ctx, w, http.StatusOK, resp)
218+
return volinfo, http.StatusOK, nil
213219
}
214220

215221
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)