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:
parent
f557aef8ec
commit
b2c4b77911
@ -2,5 +2,5 @@ apiVersion: v2
|
|||||||
name: rawfile-csi
|
name: rawfile-csi
|
||||||
description: RawFile Driver Container Storage Interface
|
description: RawFile Driver Container Storage Interface
|
||||||
type: application
|
type: application
|
||||||
version: 0.1.2
|
version: 0.1.3
|
||||||
appVersion: 0.0.1
|
appVersion: 0.0.1
|
||||||
|
@ -47,7 +47,7 @@ spec:
|
|||||||
type: DirectoryOrCreate
|
type: DirectoryOrCreate
|
||||||
- name: mountpoint-dir
|
- name: mountpoint-dir
|
||||||
hostPath:
|
hostPath:
|
||||||
path: /var/lib/kubelet/pods
|
path: /var/lib/kubelet
|
||||||
type: DirectoryOrCreate
|
type: DirectoryOrCreate
|
||||||
- name: data-dir
|
- name: data-dir
|
||||||
hostPath:
|
hostPath:
|
||||||
@ -76,7 +76,7 @@ spec:
|
|||||||
- name: socket-dir
|
- name: socket-dir
|
||||||
mountPath: /csi
|
mountPath: /csi
|
||||||
- name: mountpoint-dir
|
- name: mountpoint-dir
|
||||||
mountPath: /var/lib/kubelet/pods
|
mountPath: /var/lib/kubelet
|
||||||
mountPropagation: "Bidirectional"
|
mountPropagation: "Bidirectional"
|
||||||
- name: data-dir
|
- name: data-dir
|
||||||
mountPath: /data
|
mountPath: /data
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
|
from pathlib import Path
|
||||||
|
|
||||||
import grpc
|
import grpc
|
||||||
from google.protobuf.wrappers_pb2 import BoolValue
|
from google.protobuf.wrappers_pb2 import BoolValue
|
||||||
|
|
||||||
import rawfile_util
|
import rawfile_util
|
||||||
from csi import csi_pb2, csi_pb2_grpc
|
from csi import csi_pb2, csi_pb2_grpc
|
||||||
from orchestrator.k8s import volume_to_node, run_on_node
|
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 remote import init_rawfile, scrub
|
||||||
from util import log_grpc_request, run
|
from util import log_grpc_request, run
|
||||||
|
|
||||||
@ -43,14 +45,16 @@ class RawFileNodeServicer(csi_pb2_grpc.NodeServicer):
|
|||||||
|
|
||||||
@log_grpc_request
|
@log_grpc_request
|
||||||
def NodeGetCapabilities(self, request, context):
|
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
|
@log_grpc_request
|
||||||
def NodePublishVolume(self, request, context):
|
def NodePublishVolume(self, request, context):
|
||||||
mount_path = request.target_path
|
mount_path = request.target_path
|
||||||
img_file = rawfile_util.img_file(request.volume_id)
|
staging_path = request.staging_target_path
|
||||||
loop_file = attach_loop(img_file)
|
run(f"mount --bind {staging_path}/mount {mount_path}")
|
||||||
run(f"mount {loop_file} {mount_path}")
|
|
||||||
return csi_pb2.NodePublishVolumeResponse()
|
return csi_pb2.NodePublishVolumeResponse()
|
||||||
|
|
||||||
@log_grpc_request
|
@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):
|
class RawFileControllerServicer(csi_pb2_grpc.ControllerServicer):
|
||||||
@log_grpc_request
|
@log_grpc_request
|
||||||
|
@ -57,6 +57,12 @@ def attach_loop(file) -> str:
|
|||||||
run(f"losetup --direct-io=on -f {file}")
|
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():
|
def list_all_volumes():
|
||||||
metas = glob.glob(f"{DATA_DIR}/*/disk.meta")
|
metas = glob.glob(f"{DATA_DIR}/*/disk.meta")
|
||||||
return [basename(dirname(meta)) for meta in metas]
|
return [basename(dirname(meta)) for meta in metas]
|
||||||
|
Loading…
Reference in New Issue
Block a user