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:
		@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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]
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user