diff --git a/glusterd2/commands/peers/addpeer.go b/glusterd2/commands/peers/addpeer.go index 706aaed03..381dfef81 100644 --- a/glusterd2/commands/peers/addpeer.go +++ b/glusterd2/commands/peers/addpeer.go @@ -3,6 +3,7 @@ package peercommands import ( "fmt" "net/http" + "strings" "github.com/gluster/glusterd2/glusterd2/events" "github.com/gluster/glusterd2/glusterd2/gdctx" @@ -25,6 +26,13 @@ func addPeerHandler(w http.ResponseWriter, r *http.Request) { return } + for key := range req.Metadata { + if strings.HasPrefix(key, "_") { + restutils.SendHTTPError(ctx, w, http.StatusBadRequest, errors.ErrRestrictedKeyFound) + return + } + } + if len(req.Addresses) < 1 { restutils.SendHTTPError(ctx, w, http.StatusBadRequest, errors.ErrNoHostnamesPresent) return @@ -84,7 +92,13 @@ func addPeerHandler(w http.ResponseWriter, r *http.Request) { return } - newpeer.MetaData = req.MetaData + if req.Zone != "" { + newpeer.Metadata["_zone"] = req.Zone + } + for key, value := range req.Metadata { + newpeer.Metadata[key] = value + } + err = peer.AddOrUpdatePeer(newpeer) if err != nil { restutils.SendHTTPError(ctx, w, http.StatusInternalServerError, "Fail to add metadata to peer") @@ -104,6 +118,6 @@ func createPeerAddResp(p *peer.Peer) *api.PeerAddResp { Name: p.Name, PeerAddresses: p.PeerAddresses, ClientAddresses: p.ClientAddresses, - MetaData: p.MetaData, + Metadata: p.Metadata, } } diff --git a/glusterd2/commands/peers/editpeer.go b/glusterd2/commands/peers/editpeer.go index 8d9d57433..e02c87163 100644 --- a/glusterd2/commands/peers/editpeer.go +++ b/glusterd2/commands/peers/editpeer.go @@ -32,7 +32,7 @@ func editPeer(w http.ResponseWriter, r *http.Request) { return } - for key := range req.MetaData { + for key := range req.Metadata { if strings.HasPrefix(key, "_") { logger.WithField("metadata-key", key).Error("Key names starting with '_' are restricted in metadata field") restutils.SendHTTPError(ctx, w, http.StatusInternalServerError, "Key names starting with '_' are restricted in metadata field") @@ -58,7 +58,7 @@ func editPeer(w http.ResponseWriter, r *http.Request) { } err = txn.Ctx.Set("peerid", peerID) if err != nil { - logger.WithError(err).WithField("key", peerID).Error("Failed to set key in transaction context") + logger.WithError(err).WithField("key", "peerid").WithField("value", peerID).Error("Failed to set key in transaction context") restutils.SendHTTPError(ctx, w, http.StatusInternalServerError, err) return } @@ -102,13 +102,13 @@ func txnPeerEdit(c transaction.TxnCtx) error { c.Logger().WithError(err).WithField("peerid", peerID).Error("Peer ID not found in store") return err } - for k, v := range req.MetaData { - if peerInfo.MetaData != nil { - peerInfo.MetaData[k] = v - } else { - peerInfo.MetaData = make(map[string]string) - peerInfo.MetaData[k] = v - } + + for k, v := range req.Metadata { + peerInfo.Metadata[k] = v + } + + if req.Zone != "" { + peerInfo.Metadata["_zone"] = req.Zone } err = peer.AddOrUpdatePeer(peerInfo) if err != nil { @@ -141,6 +141,6 @@ func createPeerEditResp(p *peer.Peer) *api.PeerEditResp { Name: p.Name, PeerAddresses: p.PeerAddresses, ClientAddresses: p.ClientAddresses, - MetaData: p.MetaData, + Metadata: p.Metadata, } } diff --git a/glusterd2/commands/peers/getpeer.go b/glusterd2/commands/peers/getpeer.go index 8a2ebdee6..0d4a93c7b 100644 --- a/glusterd2/commands/peers/getpeer.go +++ b/glusterd2/commands/peers/getpeer.go @@ -38,6 +38,6 @@ func createPeerGetResp(p *peer.Peer) *api.PeerGetResp { PeerAddresses: p.PeerAddresses, ClientAddresses: p.ClientAddresses, Online: store.Store.IsNodeAlive(p.ID), - MetaData: p.MetaData, + Metadata: p.Metadata, } } diff --git a/glusterd2/commands/peers/getpeers.go b/glusterd2/commands/peers/getpeers.go index 3eba4301d..19ffa5163 100644 --- a/glusterd2/commands/peers/getpeers.go +++ b/glusterd2/commands/peers/getpeers.go @@ -32,7 +32,7 @@ func createPeerListResp(peers []*peer.Peer) *api.PeerListResp { PeerAddresses: p.PeerAddresses, ClientAddresses: p.ClientAddresses, Online: store.Store.IsNodeAlive(p.ID), - MetaData: p.MetaData, + Metadata: p.Metadata, }) } diff --git a/glusterd2/peer/peer.go b/glusterd2/peer/peer.go index 2cf1505ff..407707a30 100644 --- a/glusterd2/peer/peer.go +++ b/glusterd2/peer/peer.go @@ -11,7 +11,7 @@ type Peer struct { Name string PeerAddresses []string ClientAddresses []string - MetaData map[string]string + Metadata map[string]string } // ETCDConfig represents the structure which holds the ETCD env variables & diff --git a/glusterd2/peer/self.go b/glusterd2/peer/self.go index ea22778ae..a0c823774 100644 --- a/glusterd2/peer/self.go +++ b/glusterd2/peer/self.go @@ -5,6 +5,7 @@ import ( "net" "github.com/gluster/glusterd2/glusterd2/gdctx" + "github.com/gluster/glusterd2/pkg/errors" config "github.com/spf13/viper" ) @@ -39,18 +40,27 @@ func normalizeAddrs() ([]string, error) { // AddSelfDetails results in the peer adding its own details into etcd func AddSelfDetails() error { - var err error + var err error p := &Peer{ ID: gdctx.MyUUID, Name: gdctx.HostName, PeerAddresses: []string{config.GetString("peeraddress")}, } - p.ClientAddresses, err = normalizeAddrs() if err != nil { return err } + peerInfo, err := GetPeer(gdctx.MyUUID.String()) + if err == errors.ErrPeerNotFound { + p.Metadata = make(map[string]string) + p.Metadata["_zone"] = p.ID.String() + } else if err == nil && peerInfo != nil { + p.Metadata = peerInfo.Metadata + } else { + return err + } + return AddOrUpdatePeer(p) } diff --git a/pkg/api/peer_req_resp.go b/pkg/api/peer_req_resp.go index 005772a17..f1de51a27 100644 --- a/pkg/api/peer_req_resp.go +++ b/pkg/api/peer_req_resp.go @@ -9,24 +9,26 @@ type Peer struct { PeerAddresses []string `json:"peer-addresses"` ClientAddresses []string `json:"client-addresses"` Online bool `json:"online"` - MetaData map[string]string `json:"metadata"` + Metadata map[string]string `json:"metadata"` } // PeerAddReq represents an incoming request to add a peer to the cluster type PeerAddReq struct { Addresses []string `json:"addresses"` - MetaData map[string]string `json:"metadata"` + Zone string `json:"zone,omitempty"` + Metadata map[string]string `json:"metadata,omitempty"` } // PeerEditReq represents an incoming request to edit metadata of peer type PeerEditReq struct { - MetaData map[string]string `json:"metadata"` + Zone string `json:"zone"` + Metadata map[string]string `json:"metadata"` } // PeerAddResp is the success response sent to a PeerAddReq request type PeerAddResp Peer -// PeerEditResp is the success response sent to a MetaDataEditReq request +// PeerEditResp is the success response sent to a PeerEditReq request type PeerEditResp Peer // PeerGetResp is the response sent for a peer get request diff --git a/pkg/errors/error.go b/pkg/errors/error.go index 5a6578262..7eb700596 100644 --- a/pkg/errors/error.go +++ b/pkg/errors/error.go @@ -40,5 +40,6 @@ var ( ErrUnmarshallFailed = errors.New("failed to unmarshall from json") ErrClusterNotFound = errors.New("Cluster instance not found in store") ErrDuplicateBrickPath = errors.New("Duplicate brick entry") + ErrRestrictedKeyFound = errors.New("Key names starting with '_' are restricted in metadata field") ErrVolFileNotFound = errors.New("volume file not found") ) diff --git a/plugins/device/api/req.go b/plugins/device/api/req.go index 29fabc803..75e0458f9 100644 --- a/plugins/device/api/req.go +++ b/plugins/device/api/req.go @@ -6,12 +6,9 @@ const ( // DeviceDisabled represents disabled DeviceDisabled = "Disabled" - - // DeviceFailed represents failed - DeviceFailed = "Failed" ) // AddDeviceReq structure type AddDeviceReq struct { - Devices []string `json:"devices"` + Device string `json:"device"` } diff --git a/plugins/device/api/resp.go b/plugins/device/api/resp.go index ecdc932ea..fccda3874 100644 --- a/plugins/device/api/resp.go +++ b/plugins/device/api/resp.go @@ -4,7 +4,7 @@ import ( "github.com/gluster/glusterd2/pkg/api" ) -// Info represents structure in which devices are to be store in Peer MetaData +// Info represents structure in which devices are to be store in Peer Metadata type Info struct { Name string `json:"name"` State string `json:"state"` diff --git a/plugins/device/deviceutils/utils.go b/plugins/device/deviceutils/utils.go new file mode 100644 index 000000000..7fad76717 --- /dev/null +++ b/plugins/device/deviceutils/utils.go @@ -0,0 +1,29 @@ +package deviceutils + +import ( + "os/exec" +) + +//CreatePV is used to create physical volume. +func CreatePV(device string) error { + pvcreateCmd := exec.Command("pvcreate", "--metadatasize=128M", "--dataalignment=256K", device) + return pvcreateCmd.Run() +} + +//CreateVG is used to create volume group +func CreateVG(device string, vgName string) error { + vgcreateCmd := exec.Command("vgcreate", vgName, device) + return vgcreateCmd.Run() +} + +//RemoveVG is used to remove volume group. +func RemoveVG(vgName string) error { + vgremoveCmd := exec.Command("vgremove", vgName) + return vgremoveCmd.Run() +} + +//RemovePV is used to remove physical volume +func RemovePV(device string) error { + pvremoveCmd := exec.Command("pvremove", device) + return pvremoveCmd.Run() +} diff --git a/plugins/device/rest.go b/plugins/device/rest.go index 5e652e846..d07a2f524 100644 --- a/plugins/device/rest.go +++ b/plugins/device/rest.go @@ -1,6 +1,7 @@ package device import ( + "encoding/json" "net/http" "github.com/gluster/glusterd2/glusterd2/gdctx" @@ -18,6 +19,11 @@ func deviceAddHandler(w http.ResponseWriter, r *http.Request) { ctx := r.Context() logger := gdctx.GetReqLogger(ctx) + peerID := mux.Vars(r)["peerid"] + if uuid.Parse(peerID) == nil { + restutils.SendHTTPError(ctx, w, http.StatusBadRequest, "Invalid peer-id passed in url") + return + } req := new(deviceapi.AddDeviceReq) if err := restutils.UnmarshalRequest(r, req); err != nil { @@ -25,45 +31,65 @@ func deviceAddHandler(w http.ResponseWriter, r *http.Request) { restutils.SendHTTPError(ctx, w, http.StatusBadRequest, errors.ErrJSONParsingFailed) return } - peerID := mux.Vars(r)["peerid"] - if peerID == "" { - restutils.SendHTTPError(ctx, w, http.StatusBadRequest, "peerid not present in request") + + lock, unlock := transaction.CreateLockFuncs(peerID) + if err := lock(ctx); err != nil { + if err == transaction.ErrLockTimeout { + restutils.SendHTTPError(ctx, w, http.StatusConflict, err) + } else { + restutils.SendHTTPError(ctx, w, http.StatusInternalServerError, err) + } return } + defer unlock(ctx) + peerInfo, err := peer.GetPeer(peerID) if err != nil { logger.WithError(err).WithField("peerid", peerID).Error("Peer ID not found in store") if err == errors.ErrPeerNotFound { restutils.SendHTTPError(ctx, w, http.StatusNotFound, errors.ErrPeerNotFound) } else { - restutils.SendHTTPError(ctx, w, http.StatusInternalServerError, "Failed to get peer from store") + restutils.SendHTTPError(ctx, w, http.StatusInternalServerError, "Failed to get peer details from store") } return } + + var devices []deviceapi.Info + err = json.Unmarshal([]byte(peerInfo.Metadata["_devices"]), &devices) + if err != nil { + logger.WithError(err).WithField("peerid", peerID).Error(err) + restutils.SendHTTPError(ctx, w, http.StatusInternalServerError, err) + return + } + if checkIfDeviceExist(req.Device, devices) { + logger.WithError(err).WithField("device", req.Device).Error("Device already exists") + restutils.SendHTTPError(ctx, w, http.StatusBadRequest, "Device already exists") + return + } + txn := transaction.NewTxn(ctx) defer txn.Cleanup() - lock, unlock, err := transaction.CreateLockSteps(peerInfo.ID.String()) + txn.Nodes = []uuid.UUID{peerInfo.ID} txn.Steps = []*transaction.Step{ - lock, { DoFunc: "prepare-device", Nodes: txn.Nodes, }, - unlock, } - err = txn.Ctx.Set("peerid", peerID) + err = txn.Ctx.Set("peerid", &peerID) if err != nil { - logger.WithError(err).Error("Failed to set data for transaction") + logger.WithError(err).WithField("key", "peerid").WithField("value", peerID).Error("Failed to set key in transaction context") restutils.SendHTTPError(ctx, w, http.StatusInternalServerError, err) return } - err = txn.Ctx.Set("req", req) + err = txn.Ctx.Set("device", &req.Device) if err != nil { - logger.WithError(err).Error("Failed to set data for transaction") + logger.WithError(err).WithField("key", "device").Error("Failed to set key in transaction context") restutils.SendHTTPError(ctx, w, http.StatusInternalServerError, err) return } + err = txn.Do() if err != nil { logger.WithError(err).Error("Transaction to prepare device failed") @@ -72,7 +98,7 @@ func deviceAddHandler(w http.ResponseWriter, r *http.Request) { } peerInfo, err = peer.GetPeer(peerID) if err != nil { - logger.WithError(err).Error("Failed to get peer from store") + logger.WithError(err).WithField("peerid", peerID).Error("Failed to get peer from store") restutils.SendHTTPError(ctx, w, http.StatusInternalServerError, "Failed to get peer from store") return } diff --git a/plugins/device/store-utils.go b/plugins/device/store-utils.go index 9903165c3..4ee551c2e 100644 --- a/plugins/device/store-utils.go +++ b/plugins/device/store-utils.go @@ -13,9 +13,9 @@ func GetDevices(peerID string) ([]deviceapi.Info, error) { if err != nil { return nil, err } - if len(peerInfo.MetaData["devices"]) > 0 { + if len(peerInfo.Metadata["_devices"]) > 0 { var deviceInfo []deviceapi.Info - if err := json.Unmarshal([]byte(peerInfo.MetaData["devices"]), &deviceInfo); err != nil { + if err := json.Unmarshal([]byte(peerInfo.Metadata["_devices"]), &deviceInfo); err != nil { return nil, err } return deviceInfo, nil @@ -23,8 +23,16 @@ func GetDevices(peerID string) ([]deviceapi.Info, error) { return nil, nil } -// AddDevices adds device to specific peer -func AddDevices(devices []deviceapi.Info, peerID string) error { +func checkIfDeviceExist(reqDevice string, devices []deviceapi.Info) bool { + for _, key := range devices { + if reqDevice == key.Name { + return true + } + } + return false +} + +func addDevice(device deviceapi.Info, peerID string) error { deviceDetails, err := GetDevices(peerID) if err != nil { return err @@ -33,19 +41,19 @@ func AddDevices(devices []deviceapi.Info, peerID string) error { if err != nil { return err } + var devices []deviceapi.Info if deviceDetails != nil { - devices = append(devices, deviceDetails...) + devices = append(deviceDetails, device) } deviceJSON, err := json.Marshal(devices) if err != nil { return err } - peerInfo.MetaData["devices"] = string(deviceJSON) + peerInfo.Metadata["_devices"] = string(deviceJSON) err = peer.AddOrUpdatePeer(peerInfo) if err != nil { return err } return nil - } diff --git a/plugins/device/transaction.go b/plugins/device/transaction.go index 2967418b0..438882316 100644 --- a/plugins/device/transaction.go +++ b/plugins/device/transaction.go @@ -1,52 +1,53 @@ package device import ( - "os/exec" "strings" "github.com/gluster/glusterd2/glusterd2/transaction" deviceapi "github.com/gluster/glusterd2/plugins/device/api" - - "github.com/pborman/uuid" - log "github.com/sirupsen/logrus" + "github.com/gluster/glusterd2/plugins/device/deviceutils" ) func txnPrepareDevice(c transaction.TxnCtx) error { - var peerID uuid.UUID - var req deviceapi.AddDeviceReq - var deviceList []deviceapi.Info - if err := c.Get("peerid", peerID); err != nil { - c.Logger().WithError(err).Error("Failed transaction, cannot find peer-id") + var peerID string + if err := c.Get("peerid", &peerID); err != nil { + c.Logger().WithError(err).WithField("key", "peerid").Error("Failed to get key from transaction context") return err } - if err := c.Get("req", req); err != nil { - c.Logger().WithError(err).Error("Failed transaction, cannot find device-details") + + var device string + if err := c.Get("device", &device); err != nil { + c.Logger().WithError(err).WithField("key", "device").Error("Failed to get key from transaction context") return err } - for _, name := range req.Devices { - tempDevice := deviceapi.Info{ - Name: name, - } - deviceList = append(deviceList, tempDevice) + + var deviceInfo deviceapi.Info + + err := deviceutils.CreatePV(device) + if err != nil { + c.Logger().WithError(err).WithField("device", device).Error("Failed to create physical volume") + return err } - for index, element := range deviceList { - pvcreateCmd := exec.Command("pvcreate", "--metadatasize=128M", "--dataalignment=256K", element.Name) - if err := pvcreateCmd.Run(); err != nil { - c.Logger().WithError(err).WithField("device", element.Name).Error("pvcreate failed for device") - deviceList[index].State = deviceapi.DeviceFailed - continue - } - vgcreateCmd := exec.Command("vgcreate", strings.Replace("vg"+element.Name, "/", "-", -1), element.Name) - if err := vgcreateCmd.Run(); err != nil { - c.Logger().WithError(err).WithField("device", element.Name).Error("vgcreate failed for device") - deviceList[index].State = deviceapi.DeviceFailed - continue + vgName := strings.Replace("vg"+device, "/", "-", -1) + err = deviceutils.CreateVG(device, vgName) + if err != nil { + c.Logger().WithError(err).WithField("device", device).Error("Failed to create volume group") + errPV := deviceutils.RemovePV(device) + if errPV != nil { + c.Logger().WithError(err).WithField("device", device).Error("Failed to remove physical volume") } - deviceList[index].State = deviceapi.DeviceEnabled + return err } - err := AddDevices(deviceList, peerID.String()) + c.Logger().WithField("device", device).Info("Device setup successful, setting device status to 'Enabled'") + + deviceInfo = deviceapi.Info{ + Name: device, + State: deviceapi.DeviceEnabled, + } + + err = addDevice(deviceInfo, peerID) if err != nil { - log.WithError(err).Error("Couldn't add deviceinfo to store") + c.Logger().WithError(err).WithField("peerid", peerID).Error("Couldn't add deviceinfo to store") return err } return nil