1
0
mirror of https://github.com/gluster/glusterd2.git synced 2026-02-05 12:45:38 +01:00

volume ops: Separate internal structs from structs sent to client

Signed-off-by: Prashanth Pai <ppai@redhat.com>
This commit is contained in:
Prashanth Pai
2017-11-14 20:58:06 +05:30
parent 6d35770632
commit b4a164ca52
24 changed files with 219 additions and 155 deletions

View File

@@ -18,16 +18,6 @@ func (b *Brickinfo) String() string {
return b.NodeID.String() + ":" + b.Path
}
// Brickstatus represents real-time status of the brick and contains dynamic
// information about the brick
type Brickstatus struct {
BInfo Brickinfo
Online bool
Pid int
Port int
// TODO: Add other fields like filesystem type, statvfs output etc.
}
// StringMap returns a map[string]string representation of the Brickinfo
func (b *Brickinfo) StringMap() map[string]string {
m := make(map[string]string)

View File

@@ -259,7 +259,7 @@ var volumeResetCmd = &cobra.Command{
}
func volumeInfoHandler2(cmd *cobra.Command, isInfo bool) error {
var vols []api.Volinfo
var vols api.VolumeListResp
var err error
validateNArgs(cmd, 0, 1)
volname := ""
@@ -276,7 +276,7 @@ func volumeInfoHandler2(cmd *cobra.Command, isInfo bool) error {
fmt.Println("Volume Name: ", vol.Name)
fmt.Println("Type: ", vol.Type)
fmt.Println("Volume ID: ", vol.ID)
fmt.Println("Status: ", vol.Status)
fmt.Println("State: ", vol.State)
fmt.Println("Transport-type: ", vol.Transport)
fmt.Println("Number of Bricks: ", len(vol.Bricks))
fmt.Println("Bricks:")

View File

@@ -4,21 +4,15 @@ package versioncommands
import (
"net/http"
"github.com/gluster/glusterd2/pkg/api"
restutils "github.com/gluster/glusterd2/servers/rest/utils"
"github.com/gluster/glusterd2/version"
)
// VersionResponse represents the structure of the response object for
// /version end point. TODO: Move this 'pkg/api'
type VersionResponse struct {
GlusterdVersion string
APIVersion int
}
func getVersionHandler(w http.ResponseWriter, r *http.Request) {
v := VersionResponse{
resp := api.VersionResp{
GlusterdVersion: version.GlusterdVersion,
APIVersion: version.APIVersion,
}
restutils.SendHTTPResponse(w, http.StatusOK, v)
restutils.SendHTTPResponse(w, http.StatusOK, resp)
}

View File

@@ -94,7 +94,7 @@ func notifyVolfileChange(c transaction.TxnCtx) error {
return err
}
if volinfo.Status != volume.VolStarted {
if volinfo.State != volume.VolStarted {
return nil
}

38
commands/volumes/utils.go Normal file
View File

@@ -0,0 +1,38 @@
package volumecommands
import (
"github.com/gluster/glusterd2/brick"
"github.com/gluster/glusterd2/pkg/api"
"github.com/gluster/glusterd2/volume"
)
func createBrickInfo(b *brick.Brickinfo) api.BrickInfo {
return api.BrickInfo{
ID: b.ID,
Path: b.Path,
VolumeID: b.VolumeID,
VolumeName: b.VolumeName,
NodeID: b.NodeID,
Hostname: b.Hostname,
}
}
func createVolumeInfoResp(v *volume.Volinfo) *api.VolumeInfo {
blist := make([]api.BrickInfo, len(v.Bricks))
for i, b := range v.Bricks {
blist[i] = createBrickInfo(&b)
}
return &api.VolumeInfo{
ID: v.ID,
Name: v.Name,
Type: api.VolType(v.Type),
Transport: v.Transport,
DistCount: v.DistCount,
ReplicaCount: v.ReplicaCount,
State: api.VolState(v.State),
Options: v.Options,
Bricks: blist,
}
}

View File

@@ -84,7 +84,7 @@ func createVolinfo(req *api.VolCreateReq) (*volume.Volinfo, error) {
Password: uuid.NewRandom().String(),
}
v.Status = volume.VolStopped
v.State = volume.VolStopped
return v, nil
}
@@ -243,5 +243,11 @@ func volumeCreateHandler(w http.ResponseWriter, r *http.Request) {
}
c.Logger().WithField("volname", vol.Name).Info("new volume created")
restutils.SendHTTPResponse(w, http.StatusCreated, vol)
resp := createVolumeCreateResp(vol)
restutils.SendHTTPResponse(w, http.StatusCreated, resp)
}
func createVolumeCreateResp(v *volume.Volinfo) *api.VolumeCreateResp {
return (*api.VolumeCreateResp)(createVolumeInfoResp(v))
}

View File

@@ -80,7 +80,7 @@ func volumeDeleteHandler(w http.ResponseWriter, r *http.Request) {
return
}
if vol.Status == volume.VolStarted {
if vol.State == volume.VolStarted {
restutils.SendHTTPError(w, http.StatusForbidden, "volume is not stopped")
return
}

View File

@@ -57,7 +57,7 @@ func startBricksOnExpand(c transaction.TxnCtx) error {
}
}
if volinfo.Status != volume.VolStarted {
if volinfo.State != volume.VolStarted {
return nil
}
@@ -296,5 +296,10 @@ func volumeExpandHandler(w http.ResponseWriter, r *http.Request) {
return
}
restutils.SendHTTPResponse(w, http.StatusOK, newvolinfo)
resp := createVolumeExpandResp(newvolinfo)
restutils.SendHTTPResponse(w, http.StatusOK, resp)
}
func createVolumeExpandResp(v *volume.Volinfo) *api.VolumeExpandResp {
return (*api.VolumeExpandResp)(createVolumeInfoResp(v))
}

View File

@@ -4,6 +4,7 @@ import (
"net/http"
"github.com/gluster/glusterd2/errors"
"github.com/gluster/glusterd2/pkg/api"
restutils "github.com/gluster/glusterd2/servers/rest/utils"
"github.com/gluster/glusterd2/volume"
@@ -11,13 +12,16 @@ import (
)
func volumeInfoHandler(w http.ResponseWriter, r *http.Request) {
p := mux.Vars(r)
volname := p["volname"]
vol, e := volume.GetVolume(volname)
if e != nil {
v, err := volume.GetVolume(mux.Vars(r)["volname"])
if err != nil {
restutils.SendHTTPError(w, http.StatusNotFound, errors.ErrVolNotFound.Error())
} else {
restutils.SendHTTPResponse(w, http.StatusOK, vol)
}
resp := createVolumeGetResp(v)
restutils.SendHTTPResponse(w, http.StatusOK, resp)
}
func createVolumeGetResp(v *volume.Volinfo) *api.VolumeGetResp {
return (*api.VolumeGetResp)(createVolumeInfoResp(v))
}

View File

@@ -3,16 +3,28 @@ package volumecommands
import (
"net/http"
"github.com/gluster/glusterd2/pkg/api"
restutils "github.com/gluster/glusterd2/servers/rest/utils"
"github.com/gluster/glusterd2/volume"
)
func volumeListHandler(w http.ResponseWriter, r *http.Request) {
volumes, e := volume.GetVolumes()
if e != nil {
restutils.SendHTTPError(w, http.StatusNotFound, e.Error())
} else {
restutils.SendHTTPResponse(w, http.StatusOK, volumes)
volumes, err := volume.GetVolumes()
if err != nil {
restutils.SendHTTPError(w, http.StatusNotFound, err.Error())
}
resp := createVolumeListResp(volumes)
restutils.SendHTTPResponse(w, http.StatusOK, resp)
}
func createVolumeListResp(volumes []*volume.Volinfo) *api.VolumeListResp {
var resp api.VolumeListResp
for _, v := range volumes {
resp = append(resp, *(createVolumeGetResp(v)))
}
return &resp
}

View File

@@ -98,7 +98,7 @@ func volumeStartHandler(w http.ResponseWriter, r *http.Request) {
restutils.SendHTTPError(w, http.StatusNotFound, errors.ErrVolNotFound.Error())
return
}
if vol.Status == volume.VolStarted {
if vol.State == volume.VolStarted {
restutils.SendHTTPError(w, http.StatusBadRequest, errors.ErrVolAlreadyStarted.Error())
return
}
@@ -133,7 +133,7 @@ func volumeStartHandler(w http.ResponseWriter, r *http.Request) {
return
}
vol.Status = volume.VolStarted
vol.State = volume.VolStarted
e = volume.AddOrUpdateVolumeFunc(vol)
if e != nil {

View File

@@ -1,13 +1,13 @@
package volumecommands
import (
goerrors "errors"
"net/http"
"github.com/gluster/glusterd2/brick"
"github.com/gluster/glusterd2/daemon"
"github.com/gluster/glusterd2/errors"
"github.com/gluster/glusterd2/gdctx"
"github.com/gluster/glusterd2/pkg/api"
"github.com/gluster/glusterd2/pmap"
restutils "github.com/gluster/glusterd2/servers/rest/utils"
"github.com/gluster/glusterd2/transaction"
@@ -22,6 +22,14 @@ const (
brickStatusTxnKey string = "brickstatuses"
)
type brickstatus struct {
Info brick.Brickinfo
Online bool
Pid int
Port int
// TODO: Add other fields like filesystem type, statvfs output etc.
}
func checkStatus(ctx transaction.TxnCtx) error {
var volname string
@@ -42,7 +50,7 @@ func checkStatus(ctx transaction.TxnCtx) error {
return err
}
var brickStatuses []*brick.Brickstatus
var brickStatuses []*brickstatus
for _, binfo := range vol.Bricks {
// Skip bricks that aren't on this node.
@@ -69,8 +77,8 @@ func checkStatus(ctx transaction.TxnCtx) error {
}
}
brickStatus := &brick.Brickstatus{
BInfo: binfo,
brickStatus := &brickstatus{
Info: binfo,
Online: online,
Pid: pid,
Port: port,
@@ -89,38 +97,17 @@ func registerVolStatusStepFuncs() {
transaction.RegisterStepFunc(checkStatus, "vol-status.Check")
}
func aggregateVolumeStatus(ctx transaction.TxnCtx, nodes []uuid.UUID) (*volume.VolStatus, error) {
var brickStatuses []brick.Brickstatus
// Loop over each node on which txn was run.
// Fetch brick statuses stored by each node in transaction context.
for _, node := range nodes {
var tmp []brick.Brickstatus
err := ctx.GetNodeResult(node, brickStatusTxnKey, &tmp)
if err != nil {
return nil, goerrors.New("aggregateVolumeStatus: Could not fetch results from transaction context.")
}
brickStatuses = append(brickStatuses, tmp...)
}
v := &volume.VolStatus{Brickstatuses: brickStatuses}
return v, nil
}
func volumeStatusHandler(w http.ResponseWriter, r *http.Request) {
p := mux.Vars(r)
volname := p["volname"]
reqID, logger := restutils.GetReqIDandLogger(r)
// Ensure that the volume exists.
vol, err := volume.GetVolume(volname)
if err != nil {
restutils.SendHTTPError(w, http.StatusNotFound, errors.ErrVolNotFound.Error())
return
}
// A very simple free-form transaction to query each node for brick
// status. Fetching volume status does not modify state/data on the
// remote node. So there's no need for locks.
txn := transaction.NewTxn(reqID)
defer txn.Cleanup()
txn.Nodes = vol.Nodes()
@@ -131,13 +118,8 @@ func volumeStatusHandler(w http.ResponseWriter, r *http.Request) {
},
}
// The remote nodes get args it needs from the transaction context.
txn.Ctx.Set("volname", volname)
// As all key-value pairs stored in transaction context ends up in etcd
// store, using either the old txn context reference or the one
// returned by txn.Do() is one and the same. The transaction context is
// a way for the nodes store the results of the step runs.
rtxn, err := txn.Do()
if err != nil {
logger.WithFields(log.Fields{
@@ -148,10 +130,7 @@ func volumeStatusHandler(w http.ResponseWriter, r *http.Request) {
return
}
// Example of how an aggregate function will make sense from results of
// run of a step on multiple nodes. The transaction context will have
// results from each node, seggregated by the node's UUID.
result, err := aggregateVolumeStatus(rtxn, txn.Nodes)
result, err := createVolumeStatusResp(rtxn, txn.Nodes)
if err != nil {
errMsg := "Failed to aggregate brick status results from multiple nodes."
logger.WithField("error", err.Error()).Error("volumeStatusHandler:" + errMsg)
@@ -159,6 +138,32 @@ func volumeStatusHandler(w http.ResponseWriter, r *http.Request) {
return
}
// Send aggregated result back to the client.
restutils.SendHTTPResponse(w, http.StatusOK, result)
}
func createVolumeStatusResp(ctx transaction.TxnCtx, nodes []uuid.UUID) (*api.VolumeStatusResp, error) {
// Loop over each node on which txn was run.
// Fetch brick statuses stored by each node in transaction context.
var brickStatuses []brickstatus
for _, node := range nodes {
var tmp []brickstatus
err := ctx.GetNodeResult(node, brickStatusTxnKey, &tmp)
if err != nil {
return nil, err
}
brickStatuses = append(brickStatuses, tmp...)
}
var resp api.VolumeStatusResp
for _, b := range brickStatuses {
resp.Bricks = append(resp.Bricks, api.BrickStatus{
Info: createBrickInfo(&b.Info),
Online: b.Online,
Pid: b.Pid,
Port: b.Port,
})
}
return &resp, nil
}

View File

@@ -91,7 +91,7 @@ func volumeStopHandler(w http.ResponseWriter, r *http.Request) {
restutils.SendHTTPError(w, http.StatusNotFound, errors.ErrVolNotFound.Error())
return
}
if vol.Status == volume.VolStopped {
if vol.State == volume.VolStopped {
restutils.SendHTTPError(w, http.StatusBadRequest, errors.ErrVolAlreadyStopped.Error())
return
}
@@ -126,7 +126,7 @@ func volumeStopHandler(w http.ResponseWriter, r *http.Request) {
return
}
vol.Status = volume.VolStopped
vol.State = volume.VolStopped
e = volume.AddOrUpdateVolumeFunc(vol)
if e != nil {

View File

@@ -46,7 +46,7 @@ func TestRestart(t *testing.T) {
r.Nil(gd.Stop())
}
func getVols(gd *gdProcess, r *require.Assertions) []api.Volinfo {
func getVols(gd *gdProcess, r *require.Assertions) api.VolumeListResp {
client := initRestclient(gd.ClientAddress)
volname := ""
vols, err := client.Volumes(volname)

View File

@@ -7,9 +7,9 @@ import (
// Peer reperesents a GlusterD
type Peer struct {
ID uuid.UUID `json:"id"`
Name string `json:"name"`
Addresses []string `json:"addresses"`
ID uuid.UUID
Name string
Addresses []string
}
// ETCDConfig represents the structure which holds the ETCD env variables &

View File

@@ -1,44 +0,0 @@
package api
import (
"github.com/pborman/uuid"
)
// VolState is the current status of a volume
type VolState uint16
// VolType is the status of the volume
type VolType uint16
// VolAuth represents username and password used by trusted/internal clients
type VolAuth struct {
Username string
Password string
}
// Brickinfo is the static information about the brick
type Brickinfo struct {
NodeID uuid.UUID
Path string
VolumeName string
VolumeID uuid.UUID
}
// Volinfo repesents a volume
type Volinfo struct {
ID uuid.UUID
Name string
Type VolType
Transport string
DistCount int
ReplicaCount int
Options map[string]string
Status VolState
Checksum uint64
Version uint64
Bricks []Brickinfo
Auth VolAuth // TODO: should not be returned to client
}
// VolList respresents volumes list
type VolList map[string]string

7
pkg/api/version.go Normal file
View File

@@ -0,0 +1,7 @@
package api
// VersionResp is the response for request sent to /version endpoint.
type VersionResp struct {
GlusterdVersion string `json:"glusterd-version"`
APIVersion int `json:"api-version"`
}

View File

@@ -19,6 +19,4 @@ type VolOptionReq struct {
type VolExpandReq struct {
ReplicaCount int `json:"replica,omitempty"`
Bricks []string `json:"bricks"`
// TODO: Add other fields like disperse count when we support
// that volume type
}

61
pkg/api/volume_resp.go Normal file
View File

@@ -0,0 +1,61 @@
package api
import "github.com/pborman/uuid"
// BrickInfo contains the static information about the brick.
// Clients should NOT use this struct directly.
type BrickInfo struct {
ID uuid.UUID `json:"id"`
Path string `json:"path"`
VolumeID uuid.UUID `json:"volume-id"`
VolumeName string `json:"volume-name"`
NodeID uuid.UUID `json:"node-id"`
Hostname string `json:"host"`
}
// BrickStatus contains the runtime information about the brick.
// Clients should NOT use this struct directly.
type BrickStatus struct {
Info BrickInfo `json:"info"`
Online bool `json:"online"`
Pid int `json:"pid"`
Port int `json:"port"`
}
// VolState is the current state of the volume.
type VolState uint16
// VolType is the type of volume.
type VolType uint16
// VolumeInfo contains static information about the volume.
// Clients should NOT use this struct directly.
type VolumeInfo struct {
ID uuid.UUID `json:"id"`
Name string `json:"name"`
Type VolType `json:"type"`
Transport string `json:"transport"`
DistCount int `json:"distribute-count"`
ReplicaCount int `json:"replica-count"`
Options map[string]string `json:"options"`
State VolState `json:"state"`
Bricks []BrickInfo `json:"bricks"`
}
// VolumeStatusResp response contains the statuses of all bricks of the volume.
type VolumeStatusResp struct {
Bricks []BrickStatus `json:"bricks"`
// TODO: Add clients connected, capacity, free size etc.
}
// VolumeCreateResp is the response sent for a volume create request.
type VolumeCreateResp VolumeInfo
// VolumeGetResp is the response sent for a volume get request.
type VolumeGetResp VolumeInfo
// VolumeExpandResp is the response sent for a volume expand request.
type VolumeExpandResp VolumeInfo
// VolumeListResp is the response sent for a volume list request.
type VolumeListResp []VolumeGetResp

View File

@@ -5,34 +5,33 @@ import (
"net/http"
"github.com/gluster/glusterd2/pkg/api"
"github.com/gluster/glusterd2/volume"
)
// VolumeCreate creates Gluster Volume
func (c *Client) VolumeCreate(req api.VolCreateReq) (api.Volinfo, error) {
var vol api.Volinfo
func (c *Client) VolumeCreate(req api.VolCreateReq) (api.VolumeCreateResp, error) {
var vol api.VolumeCreateResp
err := c.post("/v1/volumes", req, http.StatusCreated, &vol)
return vol, err
}
// Volumes returns list of all volumes
func (c *Client) Volumes(volname string) ([]api.Volinfo, error) {
var vols []api.Volinfo
func (c *Client) Volumes(volname string) (api.VolumeListResp, error) {
if volname == "" {
var vols api.VolumeListResp
url := fmt.Sprintf("/v1/volumes")
err := c.get(url, nil, http.StatusOK, &vols)
return vols, err
}
var vol api.Volinfo
var vol api.VolumeGetResp
url := fmt.Sprintf("/v1/volumes/%s", volname)
err := c.get(url, nil, http.StatusOK, &vol)
return []api.Volinfo{vol}, err
return []api.VolumeGetResp{vol}, err
}
// VolumeStatus returns the status of a Gluster volume
func (c *Client) VolumeStatus(volname string) (volume.VolStatus, error) {
func (c *Client) VolumeStatus(volname string) (api.VolumeStatusResp, error) {
url := fmt.Sprintf("/v1/volumes/%s/status", volname)
var volStatus volume.VolStatus
var volStatus api.VolumeStatusResp
err := c.get(url, nil, http.StatusOK, &volStatus)
return volStatus, err
}
@@ -63,8 +62,8 @@ func (c *Client) VolumeSet(volname string, req api.VolOptionReq) error {
}
// VolumeExpand expands a Gluster Volume
func (c *Client) VolumeExpand(volname string, req api.VolExpandReq) (api.Volinfo, error) {
var vol api.Volinfo
func (c *Client) VolumeExpand(volname string, req api.VolExpandReq) (api.VolumeExpandResp, error) {
var vol api.VolumeExpandResp
url := fmt.Sprintf("/v1/volumes/%s/expand", volname)
err := c.post(url, req, http.StatusOK, &vol)
return vol, err

View File

@@ -197,7 +197,7 @@ func georepStartHandler(w http.ResponseWriter, r *http.Request) {
return
}
if vol.Status != volume.VolStarted {
if vol.State != volume.VolStarted {
restutils.SendHTTPError(w, http.StatusInternalServerError, "master volume not started")
return
}

View File

@@ -95,13 +95,13 @@ func GetVolumesList() (map[string]uuid.UUID, error) {
//GetVolumes retrives the json objects from the store and converts them into
//respective volinfo objects
func GetVolumes() ([]Volinfo, error) {
func GetVolumes() ([]*Volinfo, error) {
resp, e := store.Store.Get(context.TODO(), volumePrefix, clientv3.WithPrefix())
if e != nil {
return nil, e
}
volumes := make([]Volinfo, len(resp.Kvs))
volumes := make([]*Volinfo, len(resp.Kvs))
for i, kv := range resp.Kvs {
var vol Volinfo
@@ -114,7 +114,7 @@ func GetVolumes() ([]Volinfo, error) {
continue
}
volumes[i] = vol
volumes[i] = &vol
}
return volumes, nil

View File

@@ -65,7 +65,7 @@ type Volinfo struct {
DistCount int
ReplicaCount int
Options map[string]string
Status VolState
State VolState
Checksum uint64
Version uint64
Bricks []brick.Brickinfo
@@ -79,13 +79,6 @@ type VolAuth struct {
Password string
}
// VolStatus represents collective status of the bricks that make up the volume
type VolStatus struct {
Brickstatuses []brick.Brickstatus
// TODO: Add further fields like memory usage, brick filesystem, fd consumed,
// clients connected etc.
}
// StringMap returns a map[string]string representation of Volinfo
func (v *Volinfo) StringMap() map[string]string {
m := make(map[string]string)

View File

@@ -9,14 +9,10 @@ import (
log "github.com/sirupsen/logrus"
)
var (
getVolumesFunc = GetVolumes
)
// isBrickPathAvailable validates whether the brick is consumed by other
// volume
func isBrickPathAvailable(nodeID uuid.UUID, brickPath string) error {
volumes, e := getVolumesFunc()
volumes, e := GetVolumes()
if e != nil || volumes == nil {
// In case cluster doesn't have any volumes configured yet,
// treat this as success