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

Plugin architecture for brick Provisioners #1256

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open

Plugin architecture for brick Provisioners #1256

wants to merge 1 commit into from

Conversation

aravindavk
Copy link
Member

@aravindavk aravindavk commented Sep 29, 2018

Code is refactored to create Provisioner interface and lvm
provisioner is now a plugin

To create a new Provisioner, implement the following interface
and add it to glusterd2/provisioners/provisioner.go

type Provisioner interface {
	// Register will be called when user registers a device or directory.
	// PV and VG will be created in case of lvm plugin
	Register(devpath string) error
	// AvailableSize returns available size of the device
	AvailableSize(devpath string) (uint64, uint64, error)
	// Unregister will be called when device/directory needs to be removed
	Unregister(devpath string) error
	// CreateBrick creates the brick volume
	CreateBrick(devpath, brickid string, size uint64, bufferFactor float64) error
	// CreateBrickFs will create the brick filesystem
	CreateBrickFS(devpath, brickid, fstype string) error
	// CreateBrickDir will create the brick directory
	CreateBrickDir(brickPath string) error
	// MountBrick will mount the brick
	MountBrick(devpath, brickid, brickPath string) error
	// UnmountBrick will unmount the brick
	UnmountBrick(devpath, brickid string) error
	// RemoveBrick will remove the brick
	RemoveBrick(devpath, brickid string) error
}

Signed-off-by: Aravinda VK [email protected]

@ghost ghost assigned aravindavk Sep 29, 2018
@ghost ghost added the in progress label Sep 29, 2018
@aravindavk aravindavk requested a review from amarts September 29, 2018 13:29
Copy link
Member

@amarts amarts left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is very very critical! With this, technically, we are opening up GD2 (and IVP too) many other projects, who wants to use something other than xfs/lvm combination!

Would be great if this can make it in soon!

"github.com/gluster/glusterd2/pkg/lvmutils"
)

var mountOpts = "rw,inode64,noatime,nouuid"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We also need 'discard' as a mount option? So that actual backend gets free'd up ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this option for general purpose or only for loopmount bricks?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On LVM it returns space to the TP, and on loop, it'll return space to the outer file system... both are desirable.

Path string `json:"path"`
Name string `json:"name,omitempty"`
Size uint64 `json:"size,omitempty"`
VgID string `json:"vg-id,omitempty"`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we need this any more?

Code is refactored to create Provisioner interface and lvm
provisioner is now a plugin

To create a new Provisioner, implement the following interface
and add it to `glusterd2/provisioners/provisioner.go`

```
type Provisioner interface {
	// Register will be called when user registers a device or directory.
	// PV and VG will be created in case of lvm plugin
	Register(devpath string) error
	// AvailableSize returns available size of the device
	AvailableSize(devpath string) (uint64, uint64, error)
	// Unregister will be called when device/directory needs to be removed
	Unregister(devpath string) error
	// CreateBrick creates the brick volume
	CreateBrick(devpath, brickid string, size uint64, bufferFactor float64) error
	// CreateBrickFs will create the brick filesystem
	CreateBrickFS(devpath, brickid, fstype string) error
	// CreateBrickDir will create the brick directory
	CreateBrickDir(brickPath string) error
	// MountBrick will mount the brick
	MountBrick(devpath, brickid, brickPath string) error
	// UnmountBrick will unmount the brick
	UnmountBrick(devpath, brickid string) error
	// RemoveBrick will remove the brick
	RemoveBrick(devpath, brickid string) error
}
```

Signed-off-by: Aravinda VK <[email protected]>
@aravindavk
Copy link
Member Author

  • Snapshot related code is not refactored in this PR, I will add Snapshot related changes in separate PR.
  • This PR also includes device API refactor

Please review

@@ -21,74 +20,76 @@ func txnPrepareBricks(c transaction.TxnCtx) error {
return err
}

var provisioner provisioners.Provisioner
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you make a seperate function to avoid code duplication
like

func validateProvisioner(req *api.VolCreateReq)(error,provisioners.Provisioner){
        var provisioner provisioners.Provisioner
	var err error
	if req.Provisioner == "" {
		provisioner = provisioners.GetDefault()
	} else {
		provisioner, err = provisioners.Get(req.Provisioner)
		if err != nil {
			c.Logger().WithError(err).WithField("name", req.Provisioner).Error("invalid provisioner")
			return err,nil
		}
                }
return nil,provisioner
}

if err != nil {
c.Logger().WithError(err).WithField(
"path", b.Path).Error("failed to create brick directory in mount")
return err
}

// Update current Vg free size
err = deviceutils.UpdateDeviceFreeSize(gdctx.MyUUID.String(), b.VgName)
availableSize, extentSize, err := provisioner.AvailableSize(b.Device)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same for AvailableSize and UpdateDeviceFreeSize
I think we can create a separate function which does the functionality for getting the Available size and updating free size

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok

@@ -0,0 +1,10 @@
package deviceutils
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

instead of having a separate file, can we make use of pkg/error/error.go?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not added in pkg/error since this is in plugins right now

}

// UpdateDeviceFreeSize updates the actual available size of VG
func UpdateDeviceFreeSize(peerid, device string, size uint64, extentSize uint64) error {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

size uint64, extentSize uint64 can be made as size , extentSize uint64

if errPV != nil {
c.Logger().WithError(err).WithField("device", device).Error("Failed to remove physical volume")

var provisioner provisioners.Provisioner
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this also can be avoided if we have this code in separate function

@Madhu-1
Copy link
Member

Madhu-1 commented Oct 9, 2018

@ansiwen as you are integrating heketi with glusterd2, can you check this PR once as this changes device structure.

@prashanthpai prashanthpai added the GCS/0.2 GCS 0.2 release label Oct 11, 2018
@@ -16,6 +15,16 @@ const (
chunkSize = "1280k"
)

// MbToKb converts Value from Mb to Kb
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A better place for these two functions would be pkg/utils as they are used outside of lvmutils as well.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I left it since this will go away once we convert everything to bytes.

"github.com/gluster/glusterd2/pkg/utils"
)

// MakeXfs creates XFS filesystem
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Can be better named MakeXFS ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ack


// Mount mounts the brick LV
func Mount(dev, mountdir, options string) error {
return utils.ExecuteCommandRun("mount",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If https://golang.org/pkg/syscall/#Mount supports mount options, you can use that.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ack

return
}

if err != deviceutils.ErrDeviceNotFound {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can return 404 on ErrDeviceNotFound

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok

return
}

// FIXME: Change this to http.StatusCreated when we are able to set
// location header with a unique URL that points to created device.
restutils.SendHTTPResponse(ctx, w, http.StatusOK, peerInfo)
restutils.SendHTTPResponse(ctx, w, http.StatusOK, deviceInfo)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please check if the above FIXME can now be handled. Feel free to address in a later PR or commit.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure

Name string `json:"name,omitempty"`
Size uint64 `json:"size,omitempty"`
Mountdir string `json:"mount-dir,omitempty"`
Device string `json:"device,omitempty"`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the difference between device and device-path ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I need to think about different naming for this. Device is main device where VG is created in case of lvm. DevicePath is path of LV where brick needs to be mounted. Is RootDevice or ParentDevice sounds good instead of just Device?

@@ -46,6 +47,28 @@ func MountLocalBricks() error {
continue
}

if v.Provisioner != "" {
provisioner, err := provisioners.Get(v.Provisioner)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will and should not happen, right ?
May be return error instead of continuing ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently Snapshot Provisioned volumes are not included in this Provisioner interface. Mounting Snapshot provisioned volume or restored volume support needs to be handled.

continue
}

err = provisioner.MountBrick(b.Device, b.Name, b.Path)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see a mount command just below. What case does that handle ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that is to temporarily support mounting snapshot provisioned bricks.

// Unregister will be called when device/directory needs to be removed
Unregister(devpath string) error
// CreateBrick creates the brick volume
CreateBrick(devpath, brickid string, size uint64, bufferFactor float64) error
Copy link
Contributor

@prashanthpai prashanthpai Oct 11, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't dived too deep but I think there's an opportunity to merge some of these brick related methods, especially the ones that are always called in order.

For example, can the following for methods be just one instead, say named as CreateBrick ? .

CreateBrick
CreateBrickFS
MountBrick
CreateBrickDir

Do we need that much granularity ? Having less methods will make the provisioner interface more flexible in what it can choose to do internally when creating a brick

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

makes sense, I will merge the functions into one. MountBrick is also required as separate since bricks needs to be mounted on glusterd start/node reboot.

Register
AvailableSize
Unregister
CreateBrick
MountBrick
UnmountBrick
RemoveBrick

CreateBrick also mounts the brick. but exported MountBrick can be used separately when required.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not completely related to this changes. Is it possible to delay the mounting until the brick is actually started? Since the bricks are provisioned by glusterd, we have complete ownership of it. The problem with keeping the mount as soon as the volume creation is that, some application like an antivirus scan or a find, etc could be accessing the mount point when we try to unmount.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah possible to move bricks mount to volume start. With this change, we should only mount the bricks if volume.State=="Started" during glusterd2 start/restart

@@ -33,6 +33,7 @@ type MountInfo struct {
// Brickinfo is the static information about the brick
type Brickinfo struct {
ID uuid.UUID
Name string
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is stored in this variable ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

<volname>_s<subvol-number>_b<brick-number> similar to subvolume name

@prashanthpai
Copy link
Contributor

@rafikc30 Can you please review the LVM related changes ?
@rishubhjain Can you please review device related changes ?

@rafikc30
Copy link
Member

@prashanthpai I will review by eod

// Unregister will be called when device/directory needs to be removed
Unregister(devpath string) error
// CreateBrick creates the brick volume
CreateBrick(devpath, brickid string, size uint64, bufferFactor float64) error
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not completely related to this changes. Is it possible to delay the mounting until the brick is actually started? Since the bricks are provisioned by glusterd, we have complete ownership of it. The problem with keeping the mount as soon as the volume creation is that, some application like an antivirus scan or a find, etc could be accessing the mount point when we try to unmount.

// GetPoolMetadataSize calculates the thin pool metadata size based on the given thin pool size
func GetPoolMetadataSize(poolsize uint64) uint64 {
// GetTpMetadataSize calculates the thin pool metadata size based on the given thin pool size
func GetTpMetadataSize(poolsize uint64) uint64 {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should have a minimum value for metadata size, for example, if a user creates with a pool size of 100MB, then metadata will be 0.5MB. We had a recent issue in a user setup where metadata became full because of small size

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will look into that. But this is calculated based on given pool size.

Refer https://access.redhat.com/documentation/en-us/red_hat_gluster_storage/3.3/html-single/administration_guide/#Brick_Configuration

Minimum metadata size required is 0.5% and Max upto 16GB

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please provide more details about the issue. This function is moved from plugins/deviceutils to pkg/lvmutils

if err != nil {
return err
}
return fsutils.Mount(brickdev, mountdir, mountOpts)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think mount can be variable. Different provisioners of the same type may need to mount with different mount options. For example, in the case of a snapshot, we have to mount using the same options that the parent bricks are used. In this case, the default value won't help

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Snapshot mount should be different interface function in my opinion. Bricks of Cloned volume will use this function but not snapshot bricks.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any particular reason why snapshot should have separate mount interface. Snapshot mount has nothing special comared to the normal mount. IMHO, we can take brickinfo.MountInfo as an argument, because it is a strcture contains xclusive information about mount. And that is common to any provisoner. Something like glusterd2/volume/fs_utils.go:MountDirectory

// Provisioner represents lvm provisioner plugin
type Provisioner struct{}

func getVgName(devpath string) string {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we store the values like vgname, lvname, device name etc. As of now it return constant values, which is not the case with snapshot.

Or is it possible to move the Mountinfo variable in the brickinfo to provisioner, and return this values based on the values stored

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am trying to keep lvm specific things outside the device info. Non LVM use case will not have Vg. For example, loopmount devices provisioner


// UnmountBrick unmounts the brick
func (p Provisioner) UnmountBrick(brickPath string) error {
mountdir := path.Dir(brickPath)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This hold true only for smartvol, to extend to the other provisioners, we need to take the mount root from brickpath

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some provisioners may not have a directory after mount or it may not be mount. what other possible things after mount root? multiple directory levels?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yea, we can have multiple levels. Totally depends on how users provisioned it. So we use this function volume.GetBrickMountRoot to find that

@atinmu
Copy link
Contributor

atinmu commented Oct 25, 2018

@rafikc30 have you managed to find time to review this code?

if err != nil {
return err
}
return fsutils.Mount(brickdev, mountdir, mountOpts)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any particular reason why snapshot should have separate mount interface. Snapshot mount has nothing special comared to the normal mount. IMHO, we can take brickinfo.MountInfo as an argument, because it is a strcture contains xclusive information about mount. And that is common to any provisoner. Something like glusterd2/volume/fs_utils.go:MountDirectory


// UnmountBrick unmounts the brick
func (p Provisioner) UnmountBrick(brickPath string) error {
mountdir := path.Dir(brickPath)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yea, we can have multiple levels. Totally depends on how users provisioned it. So we use this function volume.GetBrickMountRoot to find that

@aravindavk
Copy link
Member Author

Removing GCS tracking label from pull request

@aravindavk aravindavk removed the GCS/0.2 GCS 0.2 release label Oct 26, 2018
@@ -33,6 +33,7 @@ type MountInfo struct {
// Brickinfo is the static information about the brick
type Brickinfo struct {
ID uuid.UUID
Name string
Hostname string
PeerID uuid.UUID
Path string
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this path represents the path where the brick is mounted?

Type string `json:"type"`
PeerID string `json:"peerid"`
Path string `json:"path"`
Name string `json:"name,omitempty"`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess we do not store the name of brick in the BrickInfo. Do we really require this?

ExtentSize uint64 `json:"extent-size"`
Used bool `json:"used"`
PeerID string `json:"peer-id"`
Device string `json:"device"`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@aravindavk I would like to know why Vgname was removed, it would be useful to know the VG of device via the device struct itself.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

VgName is plugin specific, doesn't make sense if it is non LVM plugin

}

func deviceListHandler(w http.ResponseWriter, r *http.Request) {

ctx := r.Context()
logger := gdctx.GetReqLogger(ctx)
peerID := mux.Vars(r)["peerid"]
if uuid.Parse(peerID) == nil {
if peerID != "" && uuid.Parse(peerID) == nil {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the peerID is empty then uuid.Parse() will return nil, so IMO we don't have to put an additional check.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok

@aravindavk
Copy link
Member Author

Updates: #1418

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants