mirror of
https://github.com/oracle/zfssa-csi-driver.git
synced 2025-06-30 07:22:01 +00:00
289 lines
6.6 KiB
Go
289 lines
6.6 KiB
Go
/*
|
|
* Copyright (c) 2021, 2024, Oracle and/or its affiliates.
|
|
* Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/
|
|
*/
|
|
|
|
package utils
|
|
|
|
import (
|
|
"fmt"
|
|
"github.com/golang/protobuf/ptypes/timestamp"
|
|
"google.golang.org/grpc/codes"
|
|
"google.golang.org/grpc/status"
|
|
"regexp"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
const (
|
|
VolumeMinComponents = 2
|
|
VolumeIdLen = 6
|
|
VolumeHandleLen = 8
|
|
SnapshotIdLen = 7
|
|
VolumeHrefLen = 10
|
|
SnapshotHrefLen = 12
|
|
)
|
|
|
|
const (
|
|
BlockVolume string = "lun"
|
|
MountVolume = "mnt"
|
|
)
|
|
|
|
const (
|
|
ResourceNamePattern string = `^[a-zA-Z0-9_\-\.\:]+$`
|
|
ResourceNameLength int = 64
|
|
)
|
|
|
|
// Volume ID
|
|
// ---------
|
|
// This structure is what identifies a volume (lun or filesystem).
|
|
type VolumeId struct {
|
|
Type string
|
|
Zfssa string
|
|
Pool string
|
|
Project string
|
|
Name string
|
|
}
|
|
|
|
func NewVolumeId(vType, zfssaName, pool, project, name string) *VolumeId {
|
|
return &VolumeId{
|
|
Type: vType,
|
|
Zfssa: zfssaName,
|
|
Pool: pool,
|
|
Project: project,
|
|
Name: name,
|
|
}
|
|
}
|
|
|
|
func VolumeIdStringFromHref(zfssa, hRef string) (string, error) {
|
|
result := strings.Split(hRef, "/")
|
|
if len(result) < VolumeHrefLen {
|
|
return "", status.Errorf(codes.NotFound,
|
|
"Volume ID (%s) contains insufficient components (%d)", hRef, VolumeHrefLen)
|
|
}
|
|
|
|
var vType string
|
|
switch result[8] {
|
|
case "filesystems":
|
|
vType = MountVolume
|
|
case "luns":
|
|
vType = BlockVolume
|
|
default:
|
|
return "", status.Errorf(codes.NotFound, "Invalid snapshot href (%s)", hRef)
|
|
}
|
|
|
|
return fmt.Sprintf("/%v/%v/%v/%v/%v",
|
|
vType,
|
|
zfssa,
|
|
result[5],
|
|
result[7],
|
|
result[9]), nil
|
|
}
|
|
|
|
func VolumeIdFromString(volumeId string) (*VolumeId, error) {
|
|
result := strings.Split(volumeId, "/")
|
|
|
|
if len(result) < VolumeMinComponents {
|
|
return nil, status.Errorf(codes.InvalidArgument,
|
|
"Volume ID/Handle (%s) contains insufficient components to continue handling (%d)",
|
|
volumeId, VolumeMinComponents)
|
|
}
|
|
|
|
var pool, project, share string
|
|
switch result[1] {
|
|
case "nfs", "iscsi":
|
|
if len(result) < VolumeHandleLen {
|
|
return nil, status.Errorf(codes.NotFound,
|
|
"Volume ID/Handle (%s) contains insufficient components (%d)", volumeId, VolumeHandleLen)
|
|
}
|
|
pool = result[4]
|
|
project = result[6]
|
|
share = result[7]
|
|
default:
|
|
if len(result) < VolumeIdLen {
|
|
return nil, status.Errorf(codes.NotFound,
|
|
"Volume ID (%s) contains insufficient components (%d)", volumeId, VolumeIdLen)
|
|
}
|
|
pool = result[3]
|
|
project = result[4]
|
|
share = result[5]
|
|
}
|
|
|
|
if !IsResourceNameValid(pool) {
|
|
return nil, status.Errorf(codes.InvalidArgument, "pool name is invalid (%s)", pool)
|
|
}
|
|
|
|
if !IsResourceNameValid(project) {
|
|
return nil, status.Errorf(codes.InvalidArgument, "project name is invalid (%s)", project)
|
|
}
|
|
|
|
if !IsResourceNameValid(share) {
|
|
return nil, status.Errorf(codes.InvalidArgument, "share name is invalid (%s)", share)
|
|
}
|
|
|
|
return &VolumeId{
|
|
Type: result[1],
|
|
Zfssa: result[2],
|
|
Pool: pool,
|
|
Project: project,
|
|
Name: share,
|
|
}, nil
|
|
}
|
|
|
|
func (zvi *VolumeId) String() string {
|
|
return fmt.Sprintf("/%v/%v/%v/%v/%v", zvi.Type, zvi.Zfssa, zvi.Pool, zvi.Project, zvi.Name)
|
|
}
|
|
|
|
func (zvi *VolumeId) IsBlock() bool {
|
|
switch zvi.Type {
|
|
case BlockVolume:
|
|
return true
|
|
case MountVolume:
|
|
return false
|
|
}
|
|
return false
|
|
}
|
|
|
|
// Snapshot ID
|
|
// -----------
|
|
// This structure is what identifies a volume (lun or filesystem).
|
|
type SnapshotId struct {
|
|
VolumeId *VolumeId
|
|
Name string
|
|
}
|
|
|
|
func NewSnapshotId(volumeId *VolumeId, snapshotName string) *SnapshotId {
|
|
return &SnapshotId{
|
|
VolumeId: volumeId,
|
|
Name: snapshotName,
|
|
}
|
|
}
|
|
|
|
func SnapshotIdFromString(snapshotId string) (*SnapshotId, error) {
|
|
|
|
result := strings.Split(snapshotId, "/")
|
|
if len(result) < SnapshotIdLen {
|
|
return nil, status.Errorf(codes.NotFound,
|
|
"Volume ID (%s) contains insufficient components (%d)", snapshotId, SnapshotIdLen)
|
|
}
|
|
|
|
return &SnapshotId{
|
|
VolumeId: &VolumeId{
|
|
Type: result[1],
|
|
Zfssa: result[2],
|
|
Pool: result[3],
|
|
Project: result[4],
|
|
Name: result[5]},
|
|
Name: result[6]}, nil
|
|
}
|
|
|
|
func SnapshotIdFromHref(zfssa, hRef string) (*SnapshotId, error) {
|
|
result := strings.Split(hRef, "/")
|
|
if len(result) < SnapshotHrefLen {
|
|
return nil, status.Errorf(codes.NotFound,
|
|
"Snapshot ID (%s) contains insufficient components (%d)", hRef, SnapshotHrefLen)
|
|
}
|
|
|
|
if result[10] != "snapshots" {
|
|
return nil, status.Errorf(codes.NotFound, "Invalid snapshot href (%s)", hRef)
|
|
}
|
|
|
|
var vType string
|
|
switch result[8] {
|
|
case "filesystems":
|
|
vType = MountVolume
|
|
case "luns":
|
|
vType = BlockVolume
|
|
default:
|
|
return nil, status.Errorf(codes.NotFound, "Invalid snapshot href (%s)", hRef)
|
|
}
|
|
|
|
return &SnapshotId{
|
|
VolumeId: &VolumeId{
|
|
Type: vType,
|
|
Zfssa: zfssa,
|
|
Pool: result[5],
|
|
Project: result[7],
|
|
Name: result[9]},
|
|
Name: result[11]}, nil
|
|
}
|
|
|
|
func SnapshotIdStringFromHref(zfssa, hRef string) (string, error) {
|
|
result := strings.Split(hRef, "/")
|
|
if len(result) < SnapshotHrefLen {
|
|
return "", status.Errorf(codes.NotFound,
|
|
"Snapshot ID (%s) contains insufficient components (%d)", hRef, SnapshotHrefLen)
|
|
}
|
|
|
|
if result[10] != "snapshots" {
|
|
return "", status.Errorf(codes.NotFound, "Invalid snapshot href (%s)", hRef)
|
|
}
|
|
|
|
var vType string
|
|
switch result[8] {
|
|
case "filesystems":
|
|
vType = MountVolume
|
|
case "luns":
|
|
vType = BlockVolume
|
|
default:
|
|
return "", status.Errorf(codes.NotFound, "Invalid snapshot href (%s)", hRef)
|
|
}
|
|
|
|
return fmt.Sprintf("/%v/%v/%v/%v/%v/%v",
|
|
vType,
|
|
zfssa,
|
|
result[5],
|
|
result[7],
|
|
result[9],
|
|
result[11]), nil
|
|
}
|
|
|
|
func (zsi *SnapshotId) String() string {
|
|
return fmt.Sprintf("/%v/%v/%v/%v/%v/%v",
|
|
zsi.VolumeId.Type,
|
|
zsi.VolumeId.Zfssa,
|
|
zsi.VolumeId.Pool,
|
|
zsi.VolumeId.Project,
|
|
zsi.VolumeId.Name,
|
|
zsi.Name)
|
|
}
|
|
|
|
func (zsi *SnapshotId) GetVolumeId() *VolumeId {
|
|
return zsi.VolumeId
|
|
}
|
|
|
|
func DateToUnix(date string) (*timestamp.Timestamp, error) {
|
|
year, err := strconv.Atoi(date[0:4])
|
|
if err == nil {
|
|
month, err := strconv.Atoi(date[5:7])
|
|
if err == nil {
|
|
day, err := strconv.Atoi(date[8:10])
|
|
if err == nil {
|
|
hour, err := strconv.Atoi(date[11:13])
|
|
if err == nil {
|
|
min, err := strconv.Atoi(date[14:16])
|
|
if err == nil {
|
|
sec, err := strconv.Atoi(date[17:19])
|
|
if err == nil {
|
|
seconds := time.Date(year, time.Month(month), day, hour, min, sec,
|
|
0, time.Local).Unix()
|
|
return ×tamp.Timestamp{Seconds: seconds, Nanos: 0}, nil
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return nil, err
|
|
}
|
|
|
|
func IsResourceNameValid(name string) bool {
|
|
if len(name) > ResourceNameLength {
|
|
return false
|
|
}
|
|
|
|
var validResourceName = regexp.MustCompile(ResourceNamePattern).MatchString
|
|
return validResourceName(name)
|
|
}
|