Skip to content

Commit

Permalink
storage-operator,salt: Add support for LVM volumes
Browse files Browse the repository at this point in the history
Add a new volume type `LVMLV` that represent an LVM LogicalVolume that
will be created on a specified LVM VolumeGroup with a specified Size
then this LVM LogicalVolume is used as a classical rawBlockDevice volume

Fixes: #1997
  • Loading branch information
TeddyAndrieux committed Mar 22, 2021
1 parent 8c0ef3e commit dd77090
Show file tree
Hide file tree
Showing 3 changed files with 111 additions and 15 deletions.
91 changes: 77 additions & 14 deletions salt/_modules/metalk8s_volumes.py
Original file line number Diff line number Diff line change
Expand Up @@ -368,7 +368,7 @@ def __init__(self, volume):
# Detect which kind of device we have: a real disk, only a partition or
# an LVM volume.
self._partition = self._get_partition(_device_name(self.path))
if self._get_lvm_path() is not None:
if _get_lvm_path(self.path) is not None:
self._kind = DeviceType.LVM
elif self._partition is not None:
self._kind = DeviceType.PARTITION
Expand All @@ -378,7 +378,7 @@ def __init__(self, volume):
@property
def persistent_path(self):
if self._kind == DeviceType.LVM:
return self._get_lvm_path()
return _get_lvm_path(self.path)
return "/dev/disk/by-partuuid/{}".format(self.uuid)

@property
Expand Down Expand Up @@ -425,18 +425,6 @@ def prepare(self, force=False):
else:
prepare_block(self.path, name, self.uuid)

def _get_lvm_path(self):
"""Return the persistent path for a LVM volume.
If the backing storage device is not an LVM volume, return None.
"""
name = _device_name(self.path)
for symlink in glob.glob("/dev/disk/by-id/dm-uuid-LVM-*"):
realpath = os.path.realpath(symlink)
if os.path.basename(realpath) == name:
return symlink
return None

@staticmethod
def _get_partition(device_name):
part_re = r"(?:(?:h|s|v|xv)d[a-z]|nvme\d+n\d+p)(?P<partition>\d+)$"
Expand All @@ -450,6 +438,63 @@ class DeviceType:
LVM = 3


class LVMLV(RawBlockDevice):
@property
def size(self):
return _quantity_to_bytes(self.get("spec.LVMLV.size"))

@property
def vg_name(self):
return self.get("spec.LVMLV.vgName")

@property
def lv_name(self):
return self.get("metadata.name")

@property
def path(self):
return "/dev/{}/{}".format(self.vg_name, self.lv_name)

@property
def exists(self):
lv_info = __salt__["lvm.lvdisplay"](lvname=self.path, quiet=True)

return lv_info and lv_info.get(self.path)

def create(self):
try:
__salt__["lvm.lvcreate"](
lvname=self.lv_name, vgname=self.vg_name, size="{}b".format(self.size)
)
except Exception as exc:
raise CommandExecutionError(
"cannot create LVM LV {} in VG {}".format(self.lv_name, self.vg_name)
) from exc

def clean_up(self):
# We do not remove the LV, too dangerous
log.info(
"Volume get clean up but the backing LV '%s' does not get removed",
self.path,
)
return


class LVMLVBlock(LVMLV):
@property
def persistent_path(self):
return _get_lvm_path(self.path)

@property
def is_prepared(self):
# Nothing to prepare on LVM
return True

def prepare(self, force=False):
# Nothing to prepare on LVM
return


# }}}
# Helpers {{{

Expand All @@ -470,6 +515,11 @@ def _get_volume(name):
return SparseLoopDevice(volume)
else:
return SparseLoopDeviceBlock(volume)
elif "LVMLV" in volume["spec"]:
if mode == "Filesystem":
return LVMLV(volume)
else:
return LVMLVBlock(volume)
else:
raise ValueError("unsupported Volume type for Volume {}".format(name))

Expand Down Expand Up @@ -539,6 +589,19 @@ def _quantity_to_bytes(quantity):
return size * UNIT_FACTOR[unit]


def _get_lvm_path(path):
"""Return the persistent path for a LVM volume.
If the backing storage device is not an LVM volume, return None.
"""
name = _device_name(path)
for symlink in glob.glob("/dev/disk/by-id/dm-uuid-LVM-*"):
realpath = os.path.realpath(symlink)
if os.path.basename(realpath) == name:
return symlink
return None


@contextlib.contextmanager
def _open_fd(*args, **kwargs):
fd = os.open(*args, **kwargs)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,22 @@ spec:
required:
- devicePath
type: object
LVMLV:
properties:
vgName:
description: Name of the LVM VolumeGroup to create the LVM LogicalVolume to back the PersistentVolume.
type: string
size:
anyOf:
- type: integer
- type: string
description: Size of the created LVM LogicalVolume backing the PersistentVolume
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
x-kubernetes-int-or-string: true
required:
- vgName
- size
type: object
sparseLoopDevice:
properties:
size:
Expand Down
19 changes: 18 additions & 1 deletion storage-operator/pkg/apis/storage/v1alpha1/volume_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,18 @@ type RawBlockDeviceVolumeSource struct {
DevicePath string `json:"devicePath"`
}

type LVMLVSource struct {
// Name of the LVM VolumeGroup on the node to create the LVM LogicalVolume to back
// the PersistentVolume
VgName string `json:"vgName"`
// Size of the created LVM LogicalVolume backing the PersistentVolume
Size resource.Quantity `json:"size"`
}

type VolumeSource struct {
SparseLoopDevice *SparseLoopDeviceVolumeSource `json:"sparseLoopDevice,omitempty"`
RawBlockDevice *RawBlockDeviceVolumeSource `json:"rawBlockDevice,omitempty"`
LVMLV *LVMLVSource `json:"LVMLV,omitempty"`
}

// VolumeSpec defines the desired state of Volume
Expand Down Expand Up @@ -263,7 +272,8 @@ func (self *Volume) SetTerminatingStatus(job string) {
func (self *Volume) IsValid() error {
// Check if a type is specified.
if self.Spec.SparseLoopDevice == nil &&
self.Spec.RawBlockDevice == nil {
self.Spec.RawBlockDevice == nil &&
self.Spec.LVMLV == nil {
return errors.New("volume type not found in Volume Spec")
}
// Check if the size is strictly positive.
Expand All @@ -274,6 +284,13 @@ func (self *Volume) IsValid() error {
self.Spec.SparseLoopDevice.Size.String(),
)
}
} else if self.Spec.LVMLV != nil {
if self.Spec.LVMLV.Size.Sign() <= 0 {
return fmt.Errorf(
"invalid LVMLV size (should be greater than 0): %s",
self.Spec.LVMLV.Size.String(),
)
}
}

// Default to Filesystem when mode is not specified.
Expand Down

0 comments on commit dd77090

Please sign in to comment.