diff --git a/changelogs/unreleased/292-pawanpraka1 b/changelogs/unreleased/292-pawanpraka1 new file mode 100644 index 0000000..46a2562 --- /dev/null +++ b/changelogs/unreleased/292-pawanpraka1 @@ -0,0 +1 @@ +adding support to restore in an encrypted pool diff --git a/deploy/yamls/zfsrestore-crd.yaml b/deploy/yamls/zfsrestore-crd.yaml index c1af836..955cc4b 100644 --- a/deploy/yamls/zfsrestore-crd.yaml +++ b/deploy/yamls/zfsrestore-crd.yaml @@ -76,6 +76,151 @@ spec: - InProgress - Invalid type: string + volSpec: + description: VolumeInfo defines ZFS volume parameters for all modes in which + ZFS volumes can be created like - ZFS volume with filesystem, ZFS Volume + exposed as zfs or ZFS volume exposed as raw block device. Some of the + parameters can be only set during creation time (as specified in the details + of the parameter), and a few are editable. In case of Cloned volumes, + the parameters are assigned the same values as the source volume. + properties: + capacity: + description: Capacity of the volume + minLength: 1 + type: string + compression: + description: 'Compression specifies the block-level compression algorithm + to be applied to the ZFS Volume. The value "on" indicates ZFS to use + the default compression algorithm. The default compression algorithm + used by ZFS will be either lzjb or, if the lz4_compress feature is + enabled, lz4. Compression property can be edited after the volume + has been created. The change will only be applied to the newly-written + data. For instance, if the Volume was created with "off" and the next + day the compression was modified to "on", the data written prior to + setting "on" will not be compressed. Default Value: off.' + pattern: ^(on|off|lzjb|gzip|gzip-[1-9]|zle|lz4)$ + type: string + dedup: + description: 'Deduplication is the process for removing redundant data + at the block level, reducing the total amount of data stored. If a + file system has the dedup property enabled, duplicate data blocks + are removed synchronously. The result is that only unique data is + stored and common components are shared among files. Deduplication + can consume significant processing power (CPU) and memory as well + as generate additional disk IO. Before creating a pool with deduplication + enabled, ensure that you have planned your hardware requirements appropriately + and implemented appropriate recovery practices, such as regular backups. + As an alternative to deduplication consider using compression=lz4, + as a less resource-intensive alternative. should be enabled on the + zvol. Dedup property can be edited after the volume has been created. + Default Value: off.' + enum: + - "on" + - "off" + type: string + encryption: + description: 'Enabling the encryption feature allows for the creation + of encrypted filesystems and volumes. ZFS will encrypt file and zvol + data, file attributes, ACLs, permission bits, directory listings, + FUID mappings, and userused / groupused data. ZFS will not encrypt + metadata related to the pool structure, including dataset and snapshot + names, dataset hierarchy, properties, file size, file holes, and deduplication + tables (though the deduplicated data itself is encrypted). Default + Value: off.' + pattern: ^(on|off|aes-128-[c,g]cm|aes-192-[c,g]cm|aes-256-[c,g]cm)$ + type: string + fsType: + description: 'FsType specifies filesystem type for the zfs volume/dataset. + If FsType is provided as "zfs", then the driver will create a ZFS + dataset, formatting is not required as underlying filesystem is ZFS + anyway. If FsType is ext2, ext3, ext4 or xfs, then the driver will + create a ZVOL and format the volume accordingly. FsType can not be + modified once volume has been provisioned. Default Value: ext4.' + type: string + keyformat: + description: KeyFormat specifies format of the encryption key The supported + KeyFormats are passphrase, raw, hex. + enum: + - passphrase + - raw + - hex + type: string + keylocation: + description: KeyLocation is the location of key for the encryption + type: string + ownerNodeID: + description: OwnerNodeID is the Node ID where the ZPOOL is running which + is where the volume has been provisioned. OwnerNodeID can not be edited + after the volume has been provisioned. + minLength: 1 + type: string + poolName: + description: poolName specifies the name of the pool where the volume + has been created. PoolName can not be edited after the volume has + been provisioned. + minLength: 1 + type: string + recordsize: + description: 'Specifies a suggested block size for files in the file + system. The size specified must be a power of two greater than or + equal to 512 and less than or equal to 128 Kbytes. RecordSize property + can be edited after the volume has been created. Changing the file + system''s recordsize affects only files created afterward; existing + files are unaffected. Default Value: 128k.' + minLength: 1 + type: string + shared: + description: Shared specifies whether the volume can be shared among + multiple pods. If it is not set to "yes", then the ZFS-LocalPV Driver + will not allow the volumes to be mounted by more than one pods. + enum: + - "yes" + - "no" + type: string + snapname: + description: SnapName specifies the name of the snapshot where the volume + has been cloned from. Snapname can not be edited after the volume + has been provisioned. + type: string + thinProvision: + description: 'ThinProvision describes whether space reservation for + the source volume is required or not. The value "yes" indicates that + volume should be thin provisioned and "no" means thick provisioning + of the volume. If thinProvision is set to "yes" then volume can be + provisioned even if the ZPOOL does not have the enough capacity. If + thinProvision is set to "no" then volume can be provisioned only if + the ZPOOL has enough capacity and capacity required by volume can + be reserved. ThinProvision can not be modified once volume has been + provisioned. Default Value: no.' + enum: + - "yes" + - "no" + type: string + volblocksize: + description: 'VolBlockSize specifies the block size for the zvol. The + volsize can only be set to a multiple of volblocksize, and cannot + be zero. VolBlockSize can not be edited after the volume has been + provisioned. Default Value: 8k.' + minLength: 1 + type: string + volumeType: + description: volumeType determines whether the volume is of type "DATASET" + or "ZVOL". If fstype provided in the storageclass is "zfs", a volume + of type dataset will be created. If "ext4", "ext3", "ext2" or "xfs" + is mentioned as fstype in the storageclass, then a volume of type + zvol will be created, which will be further formatted as the fstype + provided in the storageclass. VolumeType can not be modified once + volume has been provisioned. + enum: + - ZVOL + - DATASET + type: string + required: + - capacity + - ownerNodeID + - poolName + - volumeType + type: object required: - spec - status diff --git a/deploy/zfs-operator.yaml b/deploy/zfs-operator.yaml index 969f2fc..1916559 100644 --- a/deploy/zfs-operator.yaml +++ b/deploy/zfs-operator.yaml @@ -1022,6 +1022,151 @@ spec: - InProgress - Invalid type: string + volSpec: + description: VolumeInfo defines ZFS volume parameters for all modes in which + ZFS volumes can be created like - ZFS volume with filesystem, ZFS Volume + exposed as zfs or ZFS volume exposed as raw block device. Some of the + parameters can be only set during creation time (as specified in the details + of the parameter), and a few are editable. In case of Cloned volumes, + the parameters are assigned the same values as the source volume. + properties: + capacity: + description: Capacity of the volume + minLength: 1 + type: string + compression: + description: 'Compression specifies the block-level compression algorithm + to be applied to the ZFS Volume. The value "on" indicates ZFS to use + the default compression algorithm. The default compression algorithm + used by ZFS will be either lzjb or, if the lz4_compress feature is + enabled, lz4. Compression property can be edited after the volume + has been created. The change will only be applied to the newly-written + data. For instance, if the Volume was created with "off" and the next + day the compression was modified to "on", the data written prior to + setting "on" will not be compressed. Default Value: off.' + pattern: ^(on|off|lzjb|gzip|gzip-[1-9]|zle|lz4)$ + type: string + dedup: + description: 'Deduplication is the process for removing redundant data + at the block level, reducing the total amount of data stored. If a + file system has the dedup property enabled, duplicate data blocks + are removed synchronously. The result is that only unique data is + stored and common components are shared among files. Deduplication + can consume significant processing power (CPU) and memory as well + as generate additional disk IO. Before creating a pool with deduplication + enabled, ensure that you have planned your hardware requirements appropriately + and implemented appropriate recovery practices, such as regular backups. + As an alternative to deduplication consider using compression=lz4, + as a less resource-intensive alternative. should be enabled on the + zvol. Dedup property can be edited after the volume has been created. + Default Value: off.' + enum: + - "on" + - "off" + type: string + encryption: + description: 'Enabling the encryption feature allows for the creation + of encrypted filesystems and volumes. ZFS will encrypt file and zvol + data, file attributes, ACLs, permission bits, directory listings, + FUID mappings, and userused / groupused data. ZFS will not encrypt + metadata related to the pool structure, including dataset and snapshot + names, dataset hierarchy, properties, file size, file holes, and deduplication + tables (though the deduplicated data itself is encrypted). Default + Value: off.' + pattern: ^(on|off|aes-128-[c,g]cm|aes-192-[c,g]cm|aes-256-[c,g]cm)$ + type: string + fsType: + description: 'FsType specifies filesystem type for the zfs volume/dataset. + If FsType is provided as "zfs", then the driver will create a ZFS + dataset, formatting is not required as underlying filesystem is ZFS + anyway. If FsType is ext2, ext3, ext4 or xfs, then the driver will + create a ZVOL and format the volume accordingly. FsType can not be + modified once volume has been provisioned. Default Value: ext4.' + type: string + keyformat: + description: KeyFormat specifies format of the encryption key The supported + KeyFormats are passphrase, raw, hex. + enum: + - passphrase + - raw + - hex + type: string + keylocation: + description: KeyLocation is the location of key for the encryption + type: string + ownerNodeID: + description: OwnerNodeID is the Node ID where the ZPOOL is running which + is where the volume has been provisioned. OwnerNodeID can not be edited + after the volume has been provisioned. + minLength: 1 + type: string + poolName: + description: poolName specifies the name of the pool where the volume + has been created. PoolName can not be edited after the volume has + been provisioned. + minLength: 1 + type: string + recordsize: + description: 'Specifies a suggested block size for files in the file + system. The size specified must be a power of two greater than or + equal to 512 and less than or equal to 128 Kbytes. RecordSize property + can be edited after the volume has been created. Changing the file + system''s recordsize affects only files created afterward; existing + files are unaffected. Default Value: 128k.' + minLength: 1 + type: string + shared: + description: Shared specifies whether the volume can be shared among + multiple pods. If it is not set to "yes", then the ZFS-LocalPV Driver + will not allow the volumes to be mounted by more than one pods. + enum: + - "yes" + - "no" + type: string + snapname: + description: SnapName specifies the name of the snapshot where the volume + has been cloned from. Snapname can not be edited after the volume + has been provisioned. + type: string + thinProvision: + description: 'ThinProvision describes whether space reservation for + the source volume is required or not. The value "yes" indicates that + volume should be thin provisioned and "no" means thick provisioning + of the volume. If thinProvision is set to "yes" then volume can be + provisioned even if the ZPOOL does not have the enough capacity. If + thinProvision is set to "no" then volume can be provisioned only if + the ZPOOL has enough capacity and capacity required by volume can + be reserved. ThinProvision can not be modified once volume has been + provisioned. Default Value: no.' + enum: + - "yes" + - "no" + type: string + volblocksize: + description: 'VolBlockSize specifies the block size for the zvol. The + volsize can only be set to a multiple of volblocksize, and cannot + be zero. VolBlockSize can not be edited after the volume has been + provisioned. Default Value: 8k.' + minLength: 1 + type: string + volumeType: + description: volumeType determines whether the volume is of type "DATASET" + or "ZVOL". If fstype provided in the storageclass is "zfs", a volume + of type dataset will be created. If "ext4", "ext3", "ext2" or "xfs" + is mentioned as fstype in the storageclass, then a volume of type + zvol will be created, which will be further formatted as the fstype + provided in the storageclass. VolumeType can not be modified once + volume has been provisioned. + enum: + - ZVOL + - DATASET + type: string + required: + - capacity + - ownerNodeID + - poolName + - volumeType + type: object required: - spec - status diff --git a/pkg/apis/openebs.io/zfs/v1/zfsrestore.go b/pkg/apis/openebs.io/zfs/v1/zfsrestore.go index e832261..8a3891c 100644 --- a/pkg/apis/openebs.io/zfs/v1/zfsrestore.go +++ b/pkg/apis/openebs.io/zfs/v1/zfsrestore.go @@ -29,6 +29,7 @@ type ZFSRestore struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` // set name to restore name + volume name + something like csp tag Spec ZFSRestoreSpec `json:"spec"` + VolSpec VolumeInfo `json:"volSpec,omitempty"` // +kubebuilder:validation:Required // +kubebuilder:validation:Enum=Init;Done;Failed;Pending;InProgress;Invalid Status ZFSRestoreStatus `json:"status"` diff --git a/pkg/apis/openebs.io/zfs/v1/zz_generated.deepcopy.go b/pkg/apis/openebs.io/zfs/v1/zz_generated.deepcopy.go index 54737fd..e16e707 100644 --- a/pkg/apis/openebs.io/zfs/v1/zz_generated.deepcopy.go +++ b/pkg/apis/openebs.io/zfs/v1/zz_generated.deepcopy.go @@ -154,6 +154,7 @@ func (in *ZFSRestore) DeepCopyInto(out *ZFSRestore) { out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) out.Spec = in.Spec + out.VolSpec = in.VolSpec return } diff --git a/pkg/builder/restorebuilder/build.go b/pkg/builder/restorebuilder/build.go index c7e28bb..c6c3b5d 100644 --- a/pkg/builder/restorebuilder/build.go +++ b/pkg/builder/restorebuilder/build.go @@ -99,6 +99,12 @@ func (b *Builder) WithVolume(name string) *Builder { return b } +// WithVolSpec copies volume spec to ZFSRestore Object +func (b *Builder) WithVolSpec(vspec apis.VolumeInfo) *Builder { + b.rstr.Object.VolSpec = vspec + return b +} + // WithNode sets the node id for ZFSRestore func (b *Builder) WithNode(node string) *Builder { if node == "" { diff --git a/pkg/zfs/zfs_util.go b/pkg/zfs/zfs_util.go index 625d106..3fccad0 100644 --- a/pkg/zfs/zfs_util.go +++ b/pkg/zfs/zfs_util.go @@ -21,6 +21,8 @@ import ( "path/filepath" "fmt" + "os" + "time" "strings" @@ -329,11 +331,12 @@ func buildVolumeBackupArgs(bkp *apis.ZFSBackup, vol *apis.ZFSVolume) ([]string, } // builldVolumeRestoreArgs returns volume recv command for receiving the zfs volume -func buildVolumeRestoreArgs(rstr *apis.ZFSRestore, vol *apis.ZFSVolume) ([]string, error) { +func buildVolumeRestoreArgs(rstr *apis.ZFSRestore) ([]string, error) { var ZFSVolArg []string + var ZFSRecvParam string restoreSrc := rstr.Spec.RestoreSrc - volume := vol.Spec.PoolName + "/" + vol.Name + volume := rstr.VolSpec.PoolName + "/" + rstr.Spec.VolumeName rstrAddr := strings.Split(restoreSrc, ":") if len(rstrAddr) != 2 { @@ -342,7 +345,36 @@ func buildVolumeRestoreArgs(rstr *apis.ZFSRestore, vol *apis.ZFSVolume) ([]strin source := "nc -w 3 " + rstrAddr[0] + " " + rstrAddr[1] + " | " - cmd := source + ZFSVolCmd + " " + ZFSRecvArg + " -F " + volume + if rstr.VolSpec.VolumeType == VolTypeDataset { + if len(rstr.VolSpec.Capacity) != 0 { + ZFSRecvParam += " -o quota=" + rstr.VolSpec.Capacity + } + if len(rstr.VolSpec.RecordSize) != 0 { + ZFSRecvParam += " -o recordsize=" + rstr.VolSpec.RecordSize + } + if rstr.VolSpec.ThinProvision == "no" { + ZFSRecvParam += " -o reservation=" + rstr.VolSpec.Capacity + } + ZFSRecvParam += " -o mountpoint=legacy" + } + + if len(rstr.VolSpec.Dedup) != 0 { + ZFSRecvParam += " -o dedup=" + rstr.VolSpec.Dedup + } + if len(rstr.VolSpec.Compression) != 0 { + ZFSRecvParam += " -o compression=" + rstr.VolSpec.Compression + } + if len(rstr.VolSpec.Encryption) != 0 { + ZFSRecvParam += " -o encryption=" + rstr.VolSpec.Encryption + } + if len(rstr.VolSpec.KeyLocation) != 0 { + ZFSRecvParam += " -o keylocation=" + rstr.VolSpec.KeyLocation + } + if len(rstr.VolSpec.KeyFormat) != 0 { + ZFSRecvParam += " -o keyformat=" + rstr.VolSpec.KeyFormat + } + + cmd := source + ZFSVolCmd + " " + ZFSRecvArg + ZFSRecvParam + " -F " + volume ZFSVolArg = append(ZFSVolArg, "-c", cmd) @@ -794,18 +826,43 @@ func DestoryBackup(bkp *apis.ZFSBackup) error { return err } +// getDevice waits for the device to be created and returns the devpath +func getDevice(volume string) (string, error) { + device := ZFSDevPath + volume + // device should be created within 5 seconds + timeout := time.After(5 * time.Second) + for { + select { + case <-timeout: + return "", fmt.Errorf("zfs: not able to get the device: %s", device) + default: + if _, err := os.Stat(device); err == nil { + return device, nil + } + } + time.Sleep(1 * time.Second) + } +} + // 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, err := buildVolumeRestoreArgs(rstr, vol) + if len(rstr.VolSpec.PoolName) == 0 { + // for backward compatibility, older version of + // velero will not add spec in the ZFSRestore Object + // query it here and fill that information + vol, err := GetZFSVolume(rstr.Spec.VolumeName) + if err != nil { + return err + } + rstr.VolSpec = vol.Spec + } + args, err := buildVolumeRestoreArgs(rstr) if err != nil { return err } + volume := rstr.VolSpec.PoolName + "/" + rstr.Spec.VolumeName + cmd := exec.Command("bash", args...) out, err := cmd.CombinedOutput() @@ -820,12 +877,18 @@ func CreateRestore(rstr *apis.ZFSRestore) error { * need to generate a new uuid for zfs and btrfs volumes * so that we can mount it. */ - if vol.Spec.FsType == "xfs" { - device := ZFSDevPath + volume + if rstr.VolSpec.FsType == "xfs" { + device, err := getDevice(volume) + if err != nil { + return err + } return xfs.GenerateUUID(device) } - if vol.Spec.FsType == "btrfs" { - device := ZFSDevPath + volume + if rstr.VolSpec.FsType == "btrfs" { + device, err := getDevice(volume) + if err != nil { + return err + } return btrfs.GenerateUUID(device) }