feat(zfspv): adding backup and restore support (#162)

This commit adds support for Backup and Restore controller, which will be watching for
the events. The velero plugin will create a Backup CR to create a backup
with the remote location information, the controller will send the data
to that remote location.

In the same way, the velero plugin will create a Restore CR to restore the
volume from the the remote location and the restore controller will restore
the data.

Steps to use velero plugin for ZFS-LocalPV are :

1. install velero

2. add openebs plugin

velero plugin add openebs/velero-plugin:latest

3. Create the volumesnapshot location :

for full backup :-

```yaml
apiVersion: velero.io/v1
kind: VolumeSnapshotLocation
metadata:
  name: default
  namespace: velero
spec:
  provider: openebs.io/zfspv-blockstore
  config:
    bucket: velero
    prefix: zfs
    namespace: openebs
    provider: aws
    region: minio
    s3ForcePathStyle: "true"
    s3Url: http://minio.velero.svc:9000
```

for incremental backup :-

```yaml
apiVersion: velero.io/v1
kind: VolumeSnapshotLocation
metadata:
  name: default
  namespace: velero
spec:
  provider: openebs.io/zfspv-blockstore
  config:
    bucket: velero
    prefix: zfs
    backup: incremental
    namespace: openebs
    provider: aws
    region: minio
    s3ForcePathStyle: "true"
    s3Url: http://minio.velero.svc:9000
```

4. Create backup

velero backup create my-backup --snapshot-volumes --include-namespaces=velero-ns --volume-snapshot-locations=aws-cloud-default --storage-location=default

5. Create Schedule

velero create schedule newschedule  --schedule="*/1 * * * *" --snapshot-volumes --include-namespaces=velero-ns --volume-snapshot-locations=aws-local-default --storage-location=default

6. Restore from backup

velero restore create --from-backup my-backup --restore-volumes=true --namespace-mappings velero-ns:ns1



Signed-off-by: Pawan <pawan@mayadata.io>
This commit is contained in:
Pawan Prakash Sharma 2020-09-08 13:44:39 +05:30 committed by GitHub
parent a5e645b43d
commit e40026c98a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
48 changed files with 5148 additions and 7 deletions

View file

@ -19,6 +19,8 @@ import (
"strconv"
apis "github.com/openebs/zfs-localpv/pkg/apis/openebs.io/zfs/v1"
"github.com/openebs/zfs-localpv/pkg/builder/bkpbuilder"
"github.com/openebs/zfs-localpv/pkg/builder/restorebuilder"
"github.com/openebs/zfs-localpv/pkg/builder/snapbuilder"
"github.com/openebs/zfs-localpv/pkg/builder/volbuilder"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@ -45,6 +47,8 @@ const (
ZFSTopologyKey string = "openebs.io/nodename"
// ZFSStatusPending shows object has not handled yet
ZFSStatusPending string = "Pending"
// ZFSStatusFailed shows object operation has failed
ZFSStatusFailed string = "Failed"
// ZFSStatusReady shows object has been processed
ZFSStatusReady string = "Ready"
)
@ -63,7 +67,7 @@ var (
func init() {
OpenEBSNamespace = os.Getenv(OpenEBSNamespaceKey)
if OpenEBSNamespace == "" {
if OpenEBSNamespace == "" && os.Getenv("OPENEBS_NODE_DRIVER") != "" {
klog.Fatalf("OPENEBS_NAMESPACE environment variable not set")
}
NodeID = os.Getenv("OPENEBS_NODE_ID")
@ -256,3 +260,44 @@ func RemoveSnapFinalizer(snap *apis.ZFSSnapshot) error {
_, err := snapbuilder.NewKubeclient().WithNamespace(OpenEBSNamespace).Update(snap)
return err
}
// RemoveBkpFinalizer removes finalizer from ZFSBackup CR
func RemoveBkpFinalizer(bkp *apis.ZFSBackup) error {
bkp.Finalizers = nil
_, err := bkpbuilder.NewKubeclient().WithNamespace(OpenEBSNamespace).Update(bkp)
return err
}
// UpdateBkpInfo updates the backup info with the status
func UpdateBkpInfo(bkp *apis.ZFSBackup, status apis.ZFSBackupStatus) error {
finalizers := []string{ZFSFinalizer}
newBkp, err := bkpbuilder.BuildFrom(bkp).WithFinalizer(finalizers).Build()
// set the status
newBkp.Status = status
if err != nil {
klog.Errorf("Update backup failed %s err: %s", bkp.Spec.VolumeName, err.Error())
return err
}
_, err = bkpbuilder.NewKubeclient().WithNamespace(OpenEBSNamespace).Update(newBkp)
return err
}
// UpdateRestoreInfo updates the rstr info with the status
func UpdateRestoreInfo(rstr *apis.ZFSRestore, status apis.ZFSRestoreStatus) error {
newRstr, err := restorebuilder.BuildFrom(rstr).Build()
// set the status
newRstr.Status = status
if err != nil {
klog.Errorf("Update snapshot failed %s err: %s", rstr.Spec.VolumeName, err.Error())
return err
}
_, err = restorebuilder.NewKubeclient().WithNamespace(OpenEBSNamespace).Update(newRstr)
return err
}

View file

@ -23,7 +23,9 @@ import (
"fmt"
apis "github.com/openebs/zfs-localpv/pkg/apis/openebs.io/zfs/v1"
k8serrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/klog"
"strings"
)
// zfs related constants
@ -42,6 +44,8 @@ const (
ZFSGetArg = "get"
ZFSListArg = "list"
ZFSSnapshotArg = "snapshot"
ZFSSendArg = "send"
ZFSRecvArg = "recv"
)
// constants to define volume type
@ -292,13 +296,56 @@ func buildVolumeResizeArgs(vol *apis.ZFSVolume) []string {
return ZFSVolArg
}
// builldVolumeBackupArgs returns volume send command for sending the zfs volume
func buildVolumeBackupArgs(bkp *apis.ZFSBackup, vol *apis.ZFSVolume) []string {
var ZFSVolArg []string
backupDest := bkp.Spec.BackupDest
bkpAddr := strings.Split(backupDest, ":")
curSnap := vol.Spec.PoolName + "/" + vol.Name + "@" + bkp.Spec.SnapName
remote := " | nc -w 3 " + bkpAddr[0] + " " + bkpAddr[1]
cmd := ZFSVolCmd + " "
if len(bkp.Spec.PrevSnapName) > 0 {
prevSnap := vol.Spec.PoolName + "/" + vol.Name + "@" + bkp.Spec.PrevSnapName
// do incremental send
cmd += ZFSSendArg + " -i " + prevSnap + " " + curSnap + " " + remote
} else {
cmd += ZFSSendArg + " " + curSnap + remote
}
ZFSVolArg = append(ZFSVolArg, "-c", cmd)
return ZFSVolArg
}
// builldVolumeRestoreArgs returns volume recv command for receiving the zfs volume
func buildVolumeRestoreArgs(rstr *apis.ZFSRestore, vol *apis.ZFSVolume) []string {
var ZFSVolArg []string
restoreSrc := rstr.Spec.RestoreSrc
volume := vol.Spec.PoolName + "/" + vol.Name
rstrAddr := strings.Split(restoreSrc, ":")
source := "nc -w 3 " + rstrAddr[0] + " " + rstrAddr[1] + " | "
cmd := source + ZFSVolCmd + " " + ZFSRecvArg + " -F " + volume
ZFSVolArg = append(ZFSVolArg, "-c", cmd)
return ZFSVolArg
}
// builldVolumeDestroyArgs returns volume destroy command along with attributes as a string array
func buildVolumeDestroyArgs(vol *apis.ZFSVolume) []string {
var ZFSVolArg []string
volume := vol.Spec.PoolName + "/" + vol.Name
ZFSVolArg = append(ZFSVolArg, ZFSDestroyArg, volume)
ZFSVolArg = append(ZFSVolArg, ZFSDestroyArg, "-r", volume)
return ZFSVolArg
}
@ -620,3 +667,101 @@ func ResizeZFSVolume(vol *apis.ZFSVolume, mountpath string) error {
err = handleVolResize(vol, mountpath)
return err
}
// CreateBackup creates the backup
func CreateBackup(bkp *apis.ZFSBackup) error {
vol, err := GetZFSVolume(bkp.Spec.VolumeName)
if err != nil {
return err
}
volume := vol.Spec.PoolName + "/" + vol.Name
/* create the snapshot for the backup */
snap := &apis.ZFSSnapshot{}
snap.Name = bkp.Spec.SnapName
snap.Spec.PoolName = vol.Spec.PoolName
snap.Labels = map[string]string{ZFSVolKey: vol.Name}
err = CreateSnapshot(snap)
if err != nil {
klog.Errorf(
"zfs: could not create snapshot for the backup vol %s snap %s err %v", volume, snap.Name, err,
)
return err
}
args := buildVolumeBackupArgs(bkp, vol)
cmd := exec.Command("bash", args...)
out, err := cmd.CombinedOutput()
if err != nil {
klog.Errorf(
"zfs: could not backup the volume %v cmd %v error: %s", volume, args, string(out),
)
}
return err
}
// DestoryBackup deletes the snapshot created
func DestoryBackup(bkp *apis.ZFSBackup) error {
vol, err := GetZFSVolume(bkp.Spec.VolumeName)
if err != nil {
if k8serrors.IsNotFound(err) {
// Volume has been deleted, return
return nil
}
return err
}
volume := vol.Spec.PoolName + "/" + vol.Name
/* create the snapshot for the backup */
snap := &apis.ZFSSnapshot{}
snap.Name = bkp.Spec.SnapName
snap.Spec.PoolName = vol.Spec.PoolName
snap.Labels = map[string]string{ZFSVolKey: vol.Name}
err = DestroySnapshot(snap)
if err != nil {
klog.Errorf(
"zfs: could not destroy snapshot for the backup vol %s snap %s err %v", volume, snap.Name, err,
)
}
return err
}
// CreateRestore creates the restore
func CreateRestore(rstr *apis.ZFSRestore) error {
vol, err := GetZFSVolume(rstr.Spec.VolumeName)
if err != nil {
return err
}
volume := vol.Spec.PoolName + "/" + vol.Name
args := buildVolumeRestoreArgs(rstr, vol)
cmd := exec.Command("bash", args...)
out, err := cmd.CombinedOutput()
if err != nil {
klog.Errorf(
"zfs: could not restore the volume %v cmd %v error: %s", volume, args, string(out),
)
return err
}
/*
* need to generate a new uuid for zfs and btrfs volumes
* so that we can mount it.
*/
if vol.Spec.FsType == "xfs" {
return xfsGenerateUUID(volume)
}
if vol.Spec.FsType == "btrfs" {
return btrfsGenerateUUID(volume)
}
return nil
}