Implement `STAGE_UNSTAGE_VOLUME` capability

Summary: Before this, we directly mounted the the rawfile on the mountpoint. In this revision the `STAGE_UNSTAGE_VOLUME` capability is implemented, meaning that the volume is first mounted to a staging path, and then `bind`-mounted to the actual path. This way we can free up loopback devices when they are not needed.

Test Plan:
- Create a pvc, and use it inside a pod
- Run `losetup -l` on the node running the pod, and assert the creation of a loop device
- Delete the pod, but not the pvc
- Run `losetup -l` on the same node, and assert the removal of the loop device

Reviewers: h.marvi, bghadiri

Differential Revision: https://phab.hamravesh.ir/D806
This commit is contained in:
Mehran Kholdi 2020-05-29 20:23:26 +04:30
parent f557aef8ec
commit b2c4b77911
4 changed files with 46 additions and 8 deletions

View File

@ -2,5 +2,5 @@ apiVersion: v2
name: rawfile-csi
description: RawFile Driver Container Storage Interface
type: application
version: 0.1.2
version: 0.1.3
appVersion: 0.0.1

View File

@ -47,7 +47,7 @@ spec:
type: DirectoryOrCreate
- name: mountpoint-dir
hostPath:
path: /var/lib/kubelet/pods
path: /var/lib/kubelet
type: DirectoryOrCreate
- name: data-dir
hostPath:
@ -76,7 +76,7 @@ spec:
- name: socket-dir
mountPath: /csi
- name: mountpoint-dir
mountPath: /var/lib/kubelet/pods
mountPath: /var/lib/kubelet
mountPropagation: "Bidirectional"
- name: data-dir
mountPath: /data

View File

@ -1,10 +1,12 @@
from pathlib import Path
import grpc
from google.protobuf.wrappers_pb2 import BoolValue
import rawfile_util
from csi import csi_pb2, csi_pb2_grpc
from orchestrator.k8s import volume_to_node, run_on_node
from rawfile_util import attach_loop
from rawfile_util import attach_loop, detach_loops
from remote import init_rawfile, scrub
from util import log_grpc_request, run
@ -43,14 +45,16 @@ class RawFileNodeServicer(csi_pb2_grpc.NodeServicer):
@log_grpc_request
def NodeGetCapabilities(self, request, context):
return csi_pb2.NodeGetCapabilitiesResponse(capabilities=[])
Cap = csi_pb2.NodeServiceCapability
return csi_pb2.NodeGetCapabilitiesResponse(
capabilities=[Cap(rpc=Cap.RPC(type=Cap.RPC.STAGE_UNSTAGE_VOLUME))]
)
@log_grpc_request
def NodePublishVolume(self, request, context):
mount_path = request.target_path
img_file = rawfile_util.img_file(request.volume_id)
loop_file = attach_loop(img_file)
run(f"mount {loop_file} {mount_path}")
staging_path = request.staging_target_path
run(f"mount --bind {staging_path}/mount {mount_path}")
return csi_pb2.NodePublishVolumeResponse()
@log_grpc_request
@ -68,6 +72,34 @@ class RawFileNodeServicer(csi_pb2_grpc.NodeServicer):
),
)
@log_grpc_request
def NodeStageVolume(self, request, context):
img_file = rawfile_util.img_file(request.volume_id)
loop_file = attach_loop(img_file)
staging_path = request.staging_target_path
device_path = Path(f"{staging_path}/device")
if not device_path.exists():
device_path.symlink_to(loop_file)
mount_path = Path(f"{staging_path}/mount")
if not mount_path.exists():
mount_path.mkdir()
run(f"mount {device_path} {mount_path}")
return csi_pb2.NodeStageVolumeResponse()
@log_grpc_request
def NodeUnstageVolume(self, request, context):
img_file = rawfile_util.img_file(request.volume_id)
staging_path = request.staging_target_path
mount_path = Path(f"{staging_path}/mount")
if mount_path.exists():
run(f"umount {mount_path}")
mount_path.rmdir()
device_path = Path(f"{staging_path}/device")
if device_path.exists():
device_path.unlink()
detach_loops(img_file)
return csi_pb2.NodeUnstageVolumeResponse()
class RawFileControllerServicer(csi_pb2_grpc.ControllerServicer):
@log_grpc_request

View File

@ -57,6 +57,12 @@ def attach_loop(file) -> str:
run(f"losetup --direct-io=on -f {file}")
def detach_loops(file) -> None:
devs = attached_loops(file)
for dev in devs:
run(f"losetup -d {dev}")
def list_all_volumes():
metas = glob.glob(f"{DATA_DIR}/*/disk.meta")
return [basename(dirname(meta)) for meta in metas]