diff --git a/src/driver/index.js b/src/driver/index.js index cff19b6..c4f3316 100644 --- a/src/driver/index.js +++ b/src/driver/index.js @@ -298,6 +298,14 @@ class CsiBaseDriver { call.request.volume_context.provisioner_driver_instance_id ); + /* + let mount_options = await mount.getMountOptions(staging_target_path); + console.log(mount_options); + console.log(await mount.getMountOptionValue(mount_options, "stripe")); + console.log(await mount.getMountOptionPresent(mount_options, "stripee")); + throw new Error("foobar"); + */ + if (access_type == "mount") { fs_type = capability.mount.fs_type; mount_flags = capability.mount.mount_flags || []; @@ -307,6 +315,10 @@ class CsiBaseDriver { } mount_flags.push("defaults"); + // https://github.com/karelzak/util-linux/issues/1429 + //mount_flags.push("x-democratic-csi.managed"); + //mount_flags.push("x-democratic-csi.staged"); + if ( semver.satisfies(driver.ctx.csiVersion, ">=1.5.0") && driver.options.service.node.capabilities.rpc.includes( @@ -373,9 +385,27 @@ class CsiBaseDriver { // ensure unique entries only portals = [...new Set(portals)]; + // stores actual device paths after iscsi login let iscsiDevices = []; + // stores configuration of targets/iqn/luns to connect to + let iscsiConnections = []; for (let portal of portals) { + iscsiConnections.push({ + portal, + iqn: volume_context.iqn, + lun: volume_context.lun, + }); + } + + /** + * TODO: allow sending in iscsiConnection in a raw/manual format + * TODO: allow option to determine if send_targets should be invoked + * TODO: allow option to control whether nodedb entry should be created by driver + * TODO: allow option to control whether nodedb entry should be deleted by driver + */ + + for (let iscsiConnection of iscsiConnections) { // create DB entry // https://library.netapp.com/ecmdocs/ECMP1654943/html/GUID-8EC685B4-8CB6-40D8-A8D5-031A3899BCDC.html // put these options in place to force targets managed by csi to be explicitly attached (in the case of unclearn shutdown etc) @@ -391,24 +421,27 @@ class CsiBaseDriver { } } await iscsi.iscsiadm.createNodeDBEntry( - volume_context.iqn, - portal, + iscsiConnection.iqn, + iscsiConnection.portal, nodeDB ); // login - await iscsi.iscsiadm.login(volume_context.iqn, portal); + await iscsi.iscsiadm.login( + iscsiConnection.iqn, + iscsiConnection.portal + ); // get associated session let session = await iscsi.iscsiadm.getSession( - volume_context.iqn, - portal + iscsiConnection.iqn, + iscsiConnection.portal ); // rescan in scenarios when login previously occurred but volumes never appeared await iscsi.iscsiadm.rescanSession(session); // find device name - device = `/dev/disk/by-path/ip-${portal}-iscsi-${volume_context.iqn}-lun-${volume_context.lun}`; + device = `/dev/disk/by-path/ip-${iscsiConnection.portal}-iscsi-${iscsiConnection.iqn}-lun-${iscsiConnection.lun}`; let deviceByPath = device; // can take some time for device to show up, loop for some period @@ -439,7 +472,7 @@ class CsiBaseDriver { iscsiDevices.push(device); driver.ctx.logger.info( - `successfully logged into portal ${portal} and created device ${deviceByPath} with realpath ${device}` + `successfully logged into portal ${iscsiConnection.portal} and created device ${deviceByPath} with realpath ${device}` ); } } @@ -461,7 +494,7 @@ class CsiBaseDriver { ); } - if (iscsiDevices.length != portals.length) { + if (iscsiDevices.length != iscsiConnections.length) { driver.ctx.logger.warn( `failed to attach all iscsi devices/targets/portals` ); @@ -484,7 +517,8 @@ class CsiBaseDriver { iscsiDevices.includes(value) ); - const useMultipath = portals.length > 1 || commonDevices.length > 0; + const useMultipath = + iscsiConnections.length > 1 || commonDevices.length > 0; // discover multipath device to use if (useMultipath) { @@ -679,6 +713,8 @@ class CsiBaseDriver { //result = await mount.pathIsMounted(block_path); //result = await mount.pathIsMounted(staging_target_path) + // TODO: use the x-* mount options to detect if we should delete target + try { result = await mount.pathIsMounted(block_path); } catch (err) { @@ -900,7 +936,13 @@ class CsiBaseDriver { } bind_mount_flags.push("defaults"); + + // https://github.com/karelzak/util-linux/issues/1429 + //bind_mount_flags.push("x-democratic-csi.managed"); + //bind_mount_flags.push("x-democratic-csi.published"); + if (readonly) bind_mount_flags.push("ro"); + // , "x-democratic-csi.ro" switch (node_attach_driver) { case "nfs": diff --git a/src/utils/filesystem.js b/src/utils/filesystem.js index 4937863..724f25e 100644 --- a/src/utils/filesystem.js +++ b/src/utils/filesystem.js @@ -326,9 +326,14 @@ class Filesystem { try { result = await filesystem.exec("blkid", args); } catch (err) { + if (err.code == 2 && err.stderr.includes("No such device or address")) { + throw err; + } + if (err.code == 2) { return false; } + throw err; } diff --git a/src/utils/mount.js b/src/utils/mount.js index 5ad935e..187c1d4 100644 --- a/src/utils/mount.js +++ b/src/utils/mount.js @@ -145,7 +145,7 @@ class Mount { * * @param {*} path */ - async getMountDetails(path, extraOutputProperties = []) { + async getMountDetails(path, extraOutputProperties = [], extraArgs = []) { const mount = this; let args = []; const common_options = JSON.parse(JSON.stringify(FINDMNT_COMMON_OPTIONS)); @@ -156,6 +156,7 @@ class Mount { args = args.concat(["--mountpoint", path]); args = args.concat(common_options); + args = args.concat(extraArgs); let result; try { @@ -167,6 +168,94 @@ class Mount { } } + /** + * parse a mount options string into an array + * + * @param {*} options + * @returns + */ + async parseMountOptions(options) { + if (!options) { + return []; + } + + if (Array.isArray(options)) { + return options; + } + + options = options.split(","); + return options; + } + + /** + * Given the set of mount options and sought after option, return true if the option is present + * + * @param {*} options + * @param {*} option + * @returns + */ + async getMountOptionPresent(options, option) { + const mount = this; + + if (!Array.isArray(options)) { + options = await mount.parseMountOptions(options); + } + + for (let i of options) { + let parts = i.split("=", 2); + if (parts[0] == option) { + return true; + } + } + + return false; + } + + /** + * Get the value of the given mount option + * + * if the mount option is present by has no value null is returned + * if the mount option is NOT present undefined is returned + * is the mount option has a value that value is returned + * + * @param {*} options + * @param {*} option + * @returns + */ + async getMountOptionValue(options, option) { + const mount = this; + + if (!Array.isArray(options)) { + options = await mount.parseMountOptions(options); + } + + for (let i of options) { + let parts = i.split("=", 2); + if (parts[0] == option) { + if (typeof parts[1] === "undefined") { + return null; + } else { + return parts[1]; + } + } + } + + return undefined; + } + + /** + * Get mount optsion for a given path + * + * @param {*} path + * @returns Array + */ + async getMountOptions(path) { + const mount = this; + let details = await mount.getMountDetails(path, [], ["-m"]); + + return await mount.parseMountOptions(details.options); + } + /** * Get the device (source) at the given mount point *