mirror of
https://github.com/TECHNOFAB11/zfs-localpv.git
synced 2025-12-12 06:20:11 +01:00
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:
parent
a5e645b43d
commit
e40026c98a
48 changed files with 5148 additions and 7 deletions
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue