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

Commit 3a80aac

Browse files
committed
Volume shrink api
1 parent 7436b73 commit 3a80aac

File tree

4 files changed

+221
-0
lines changed

4 files changed

+221
-0
lines changed

glusterd2/commands/volumes/commands.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,14 @@ func (c *Command) Routes() route.Routes {
3030
RequestType: utils.GetTypeString((*api.VolExpandReq)(nil)),
3131
ResponseType: utils.GetTypeString((*api.VolumeExpandResp)(nil)),
3232
HandlerFunc: volumeExpandHandler},
33+
route.Route{
34+
Name: "VolumeShrink",
35+
Method: "POST",
36+
Pattern: "/volumes/{volname}/shrink",
37+
Version: 1,
38+
RequestType: utils.GetTypeString((*api.VolShrinkReq)(nil)),
39+
ResponseType: utils.GetTypeString((*api.VolumeShrinkResp)(nil)),
40+
HandlerFunc: volumeShrinkHandler},
3341
// TODO: Implmement volume reset as
3442
// DELETE /volumes/{volname}/options
3543
route.Route{
@@ -160,6 +168,7 @@ func (c *Command) RegisterStepFuncs() {
160168
registerVolStopStepFuncs()
161169
registerBricksStatusStepFuncs()
162170
registerVolExpandStepFuncs()
171+
registerVolShrinkStepFuncs()
163172
registerVolOptionStepFuncs()
164173
registerVolStatedumpFuncs()
165174
}
Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
package volumecommands
2+
3+
import (
4+
"errors"
5+
"net/http"
6+
"path/filepath"
7+
"strings"
8+
9+
"github.com/gluster/glusterd2/glusterd2/daemon"
10+
"github.com/gluster/glusterd2/glusterd2/gdctx"
11+
restutils "github.com/gluster/glusterd2/glusterd2/servers/rest/utils"
12+
"github.com/gluster/glusterd2/glusterd2/transaction"
13+
"github.com/gluster/glusterd2/glusterd2/volume"
14+
"github.com/gluster/glusterd2/pkg/api"
15+
"github.com/gluster/glusterd2/plugins/rebalance"
16+
rebalanceapi "github.com/gluster/glusterd2/plugins/rebalance/api"
17+
18+
"github.com/gorilla/mux"
19+
"github.com/pborman/uuid"
20+
)
21+
22+
func registerVolShrinkStepFuncs() {
23+
transaction.RegisterStepFunc(storeVolume, "vol-shrink.UpdateVolinfo")
24+
transaction.RegisterStepFunc(notifyVolfileChange, "vol-shrink.NotifyClients")
25+
transaction.RegisterStepFunc(startRebalance, "vol-shrink.StartRebalance")
26+
}
27+
28+
func startRebalance(c transaction.TxnCtx) error {
29+
var rinfo rebalanceapi.RebalanceInfo
30+
err := c.Get("rinfo", &rinfo)
31+
if err != nil {
32+
return err
33+
}
34+
35+
rebalanceProcess, err := rebalance.NewRebalanceProcess(rinfo)
36+
if err != nil {
37+
return err
38+
}
39+
40+
err = daemon.Start(rebalanceProcess, true)
41+
42+
return err
43+
}
44+
45+
func volumeShrinkHandler(w http.ResponseWriter, r *http.Request) {
46+
ctx := r.Context()
47+
logger := gdctx.GetReqLogger(ctx)
48+
49+
volname := mux.Vars(r)["volname"]
50+
51+
volinfo, err := volume.GetVolume(volname)
52+
if err != nil {
53+
restutils.SendHTTPError(ctx, w, http.StatusNotFound, err.Error(), api.ErrCodeDefault)
54+
return
55+
}
56+
57+
var req api.VolShrinkReq
58+
if err := restutils.UnmarshalRequest(r, &req); err != nil {
59+
restutils.SendHTTPError(ctx, w, http.StatusUnprocessableEntity, err.Error(), api.ErrCodeDefault)
60+
return
61+
}
62+
63+
switch volinfo.Type {
64+
case volume.Distribute:
65+
case volume.Replicate:
66+
case volume.DistReplicate:
67+
if len(req.Bricks)%volinfo.Subvols[0].ReplicaCount != 0 {
68+
err := errors.New("wrong number of bricks to remove")
69+
restutils.SendHTTPError(ctx, w, http.StatusInternalServerError, err.Error(), api.ErrCodeDefault)
70+
return
71+
}
72+
default:
73+
err := errors.New("not implemented: " + volinfo.Type.String())
74+
restutils.SendHTTPError(ctx, w, http.StatusInternalServerError, err.Error(), api.ErrCodeDefault)
75+
return
76+
77+
}
78+
79+
nodes, err := []
80+
if err != nil {
81+
logger.WithError(err).Error("could not prepare node list")
82+
restutils.SendHTTPError(ctx, w, http.StatusInternalServerError, err.Error(), api.ErrCodeDefault)
83+
return
84+
}
85+
86+
txn := transaction.NewTxn(ctx)
87+
defer txn.Cleanup()
88+
89+
lock, unlock, err := transaction.CreateLockSteps(volname)
90+
if err != nil {
91+
restutils.SendHTTPError(ctx, w, http.StatusInternalServerError, err.Error(), api.ErrCodeDefault)
92+
return
93+
}
94+
95+
txn.Steps = []*transaction.Step{
96+
lock,
97+
{
98+
DoFunc: "vol-shrink.UpdateVolinfo",
99+
Nodes: []uuid.UUID{gdctx.MyUUID},
100+
},
101+
{
102+
DoFunc: "vol-shrink.NotifyClients",
103+
Nodes: nodes,
104+
},
105+
{
106+
DoFunc: "vol-shrink.StartRebalance",
107+
Nodes: nodes,
108+
},
109+
110+
unlock,
111+
}
112+
113+
decommissionedSubvols, err := findDecommissioned(req.Bricks, volinfo)
114+
if err != nil {
115+
restutils.SendHTTPError(ctx, w, http.StatusInternalServerError, err.Error(), api.ErrCodeDefault)
116+
return
117+
118+
}
119+
120+
// The following line is for testing purposes.
121+
// It seems that there is no other way to include this information in the rebalance volfile right now.
122+
volinfo.Options["distribute.decommissioned-bricks"] = strings.TrimSpace(decommissionedSubvols)
123+
124+
var rinfo rebalanceapi.RebalanceInfo
125+
var commit uint64
126+
rinfo.Volname = volname
127+
rinfo.RebalanceID = uuid.NewRandom()
128+
rinfo.Cmd = rebalanceapi.GfDefragCmdStartForce
129+
rinfo.Status = rebalanceapi.GfDefragStatusNotStarted
130+
rinfo.CommitHash = rebalance.SetCommitHash(commit)
131+
if err := txn.Ctx.Set("rinfo", rinfo); err != nil {
132+
restutils.SendHTTPError(ctx, w, http.StatusInternalServerError, err.Error(), api.ErrCodeDefault)
133+
return
134+
}
135+
136+
if err := txn.Ctx.Set("volinfo", volinfo); err != nil {
137+
restutils.SendHTTPError(ctx, w, http.StatusInternalServerError, err.Error(), api.ErrCodeDefault)
138+
return
139+
}
140+
141+
if err = txn.Do(); err != nil {
142+
logger.WithError(err).Error("remove bricks start transaction failed")
143+
if err == transaction.ErrLockTimeout {
144+
restutils.SendHTTPError(ctx, w, http.StatusConflict, err.Error(), api.ErrCodeDefault)
145+
} else {
146+
restutils.SendHTTPError(ctx, w, http.StatusInternalServerError, err.Error(), api.ErrCodeDefault)
147+
}
148+
return
149+
}
150+
151+
restutils.SendHTTPResponse(ctx, w, http.StatusOK, decommissionedSubvols)
152+
153+
}
154+
155+
func findDecommissioned(bricks []api.BrickReq, volinfo *volume.Volinfo) (string, error) {
156+
brickSet := make(map[string]bool)
157+
for _, brick := range bricks {
158+
u := uuid.Parse(brick.NodeID)
159+
if u == nil {
160+
return "", errors.New("Invalid nodeid")
161+
}
162+
path, err := filepath.Abs(brick.Path)
163+
if err != nil {
164+
return "", err
165+
}
166+
brickSet[brick.NodeID+":"+path] = true
167+
}
168+
169+
var subvolMap = make(map[string]int)
170+
for _, subvol := range volinfo.Subvols {
171+
for _, b := range subvol.Bricks {
172+
if brickSet[b.NodeID.String()+":"+b.Path] {
173+
if count, ok := subvolMap[subvol.Name]; !ok {
174+
subvolMap[subvol.Name] = 1
175+
} else {
176+
subvolMap[subvol.Name] = count + 1
177+
}
178+
}
179+
180+
}
181+
}
182+
183+
var base int
184+
switch volinfo.Type {
185+
case volume.Distribute:
186+
base = 1
187+
case volume.Replicate:
188+
base = len(bricks)
189+
case volume.DistReplicate:
190+
base = volinfo.Subvols[0].ReplicaCount
191+
default:
192+
return "", errors.New("not implemented: " + volinfo.Type.String())
193+
}
194+
195+
decommissioned := ""
196+
for subvol, count := range subvolMap {
197+
if count != base {
198+
return "", errors.New("Wrong number of bricks in the subvolume")
199+
}
200+
decommissioned = decommissioned + subvol + " "
201+
}
202+
203+
return decommissioned, nil
204+
}

pkg/api/volume_req.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,11 @@ type VolExpandReq struct {
6767
Flags map[string]bool `json:"flags,omitempty"`
6868
}
6969

70+
// VolShrinkReq represents a request to remove bricks from a volume
71+
type VolShrinkReq struct {
72+
Bricks []BrickReq `json:"bricks"`
73+
}
74+
7075
// VolumeOption represents an option that is part of a profile
7176
type VolumeOption struct {
7277
Name string `json:"name"`

pkg/api/volume_resp.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,9 @@ type VolumeGetResp VolumeInfo
9292
// VolumeExpandResp is the response sent for a volume expand request.
9393
type VolumeExpandResp VolumeInfo
9494

95+
// VolumeShrinkResp is the response sent for a volume expand request.
96+
type VolumeShrinkResp VolumeInfo
97+
9598
// VolumeStartResp is the response sent for a volume start request.
9699
type VolumeStartResp VolumeInfo
97100

0 commit comments

Comments
 (0)