more robust handling of stale nfs filesystems
Signed-off-by: Travis Glenn Hansen <travisghansen@yahoo.com>
This commit is contained in:
parent
1f87ea8d77
commit
a26488ca18
|
|
@ -450,7 +450,8 @@ class CsiBaseDriver {
|
||||||
// compare all device-mapper slaves with the newly created devices
|
// compare all device-mapper slaves with the newly created devices
|
||||||
// if any of the new devices are device-mapper slaves treat this as a
|
// if any of the new devices are device-mapper slaves treat this as a
|
||||||
// multipath scenario
|
// multipath scenario
|
||||||
let allDeviceMapperSlaves = await filesystem.getAllDeviceMapperSlaveDevices();
|
let allDeviceMapperSlaves =
|
||||||
|
await filesystem.getAllDeviceMapperSlaveDevices();
|
||||||
let commonDevices = allDeviceMapperSlaves.filter((value) =>
|
let commonDevices = allDeviceMapperSlaves.filter((value) =>
|
||||||
iscsiDevices.includes(value)
|
iscsiDevices.includes(value)
|
||||||
);
|
);
|
||||||
|
|
@ -581,6 +582,7 @@ class CsiBaseDriver {
|
||||||
* @param {*} call
|
* @param {*} call
|
||||||
*/
|
*/
|
||||||
async NodeUnstageVolume(call) {
|
async NodeUnstageVolume(call) {
|
||||||
|
const driver = this;
|
||||||
const mount = new Mount();
|
const mount = new Mount();
|
||||||
const filesystem = new Filesystem();
|
const filesystem = new Filesystem();
|
||||||
const iscsi = new ISCSI();
|
const iscsi = new ISCSI();
|
||||||
|
|
@ -606,7 +608,21 @@ class CsiBaseDriver {
|
||||||
//result = await mount.pathIsMounted(block_path);
|
//result = await mount.pathIsMounted(block_path);
|
||||||
//result = await mount.pathIsMounted(staging_target_path)
|
//result = await mount.pathIsMounted(staging_target_path)
|
||||||
|
|
||||||
result = await mount.pathIsMounted(block_path);
|
try {
|
||||||
|
result = await mount.pathIsMounted(block_path);
|
||||||
|
} catch (err) {
|
||||||
|
/**
|
||||||
|
* on stalled fs such as nfs, even findmnt will return immediately for the base mount point
|
||||||
|
* so in the case of timeout here (base mount point and then a file/folder beneath it) we almost certainly are not a block device
|
||||||
|
* AND the fs is probably stalled
|
||||||
|
*/
|
||||||
|
if (err.timeout) {
|
||||||
|
result = false;
|
||||||
|
} else {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (result) {
|
if (result) {
|
||||||
is_block = true;
|
is_block = true;
|
||||||
access_type = "block";
|
access_type = "block";
|
||||||
|
|
@ -626,7 +642,32 @@ class CsiBaseDriver {
|
||||||
|
|
||||||
result = await mount.pathIsMounted(normalized_staging_path);
|
result = await mount.pathIsMounted(normalized_staging_path);
|
||||||
if (result) {
|
if (result) {
|
||||||
result = await mount.umount(normalized_staging_path, umount_args);
|
try {
|
||||||
|
result = await mount.umount(normalized_staging_path, umount_args);
|
||||||
|
} catch (err) {
|
||||||
|
if (err.timeout) {
|
||||||
|
driver.ctx.logger.warn(
|
||||||
|
`hit timeout waiting to unmount path: ${normalized_staging_path}`
|
||||||
|
);
|
||||||
|
result = await mount.getMountDetails(normalized_staging_path);
|
||||||
|
switch (result.fstype) {
|
||||||
|
case "nfs":
|
||||||
|
driver.ctx.logger.warn(
|
||||||
|
`detected stale nfs filesystem, attempting to force unmount: ${normalized_staging_path}`
|
||||||
|
);
|
||||||
|
result = await mount.umount(
|
||||||
|
normalized_staging_path,
|
||||||
|
umount_args.concat(["--force", "--lazy"])
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw err;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_block) {
|
if (is_block) {
|
||||||
|
|
@ -666,14 +707,13 @@ class CsiBaseDriver {
|
||||||
session.attached_scsi_devices.host &&
|
session.attached_scsi_devices.host &&
|
||||||
session.attached_scsi_devices.host.devices
|
session.attached_scsi_devices.host.devices
|
||||||
) {
|
) {
|
||||||
is_attached_to_session = session.attached_scsi_devices.host.devices.some(
|
is_attached_to_session =
|
||||||
(device) => {
|
session.attached_scsi_devices.host.devices.some((device) => {
|
||||||
if (device.attached_scsi_disk == block_device_info_i.name) {
|
if (device.attached_scsi_disk == block_device_info_i.name) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
});
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_attached_to_session) {
|
if (is_attached_to_session) {
|
||||||
|
|
@ -864,6 +904,7 @@ class CsiBaseDriver {
|
||||||
}
|
}
|
||||||
|
|
||||||
async NodeUnpublishVolume(call) {
|
async NodeUnpublishVolume(call) {
|
||||||
|
const driver = this;
|
||||||
const mount = new Mount();
|
const mount = new Mount();
|
||||||
const filesystem = new Filesystem();
|
const filesystem = new Filesystem();
|
||||||
let result;
|
let result;
|
||||||
|
|
@ -874,7 +915,33 @@ class CsiBaseDriver {
|
||||||
|
|
||||||
result = await mount.pathIsMounted(target_path);
|
result = await mount.pathIsMounted(target_path);
|
||||||
if (result) {
|
if (result) {
|
||||||
result = await mount.umount(target_path, umount_args);
|
try {
|
||||||
|
result = await mount.umount(target_path, umount_args);
|
||||||
|
} catch (err) {
|
||||||
|
if (err.timeout) {
|
||||||
|
driver.ctx.logger.warn(
|
||||||
|
`hit timeout waiting to unmount path: ${target_path}`
|
||||||
|
);
|
||||||
|
// bind mounts do show the 'real' fs details
|
||||||
|
result = await mount.getMountDetails(target_path);
|
||||||
|
switch (result.fstype) {
|
||||||
|
case "nfs":
|
||||||
|
driver.ctx.logger.warn(
|
||||||
|
`detected stale nfs filesystem, attempting to force unmount: ${target_path}`
|
||||||
|
);
|
||||||
|
result = await mount.umount(
|
||||||
|
target_path,
|
||||||
|
umount_args.concat(["--force", "--lazy"])
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw err;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
result = await filesystem.pathExists(target_path);
|
result = await filesystem.pathExists(target_path);
|
||||||
|
|
@ -909,7 +976,7 @@ class CsiBaseDriver {
|
||||||
//VOLUME_CONDITION
|
//VOLUME_CONDITION
|
||||||
if (
|
if (
|
||||||
semver.satisfies(driver.ctx.csiVersion, ">=1.3.0") &&
|
semver.satisfies(driver.ctx.csiVersion, ">=1.3.0") &&
|
||||||
options.service.node.capabilities.rpc.includes("VOLUME_CONDITION")
|
driver.options.service.node.capabilities.rpc.includes("VOLUME_CONDITION")
|
||||||
) {
|
) {
|
||||||
// TODO: let drivers fill ths in
|
// TODO: let drivers fill ths in
|
||||||
let abnormal = false;
|
let abnormal = false;
|
||||||
|
|
@ -930,7 +997,11 @@ class CsiBaseDriver {
|
||||||
|
|
||||||
switch (access_type) {
|
switch (access_type) {
|
||||||
case "mount":
|
case "mount":
|
||||||
result = await mount.getMountDetails(device_path);
|
result = await mount.getMountDetails(device_path, [
|
||||||
|
"avail",
|
||||||
|
"size",
|
||||||
|
"used",
|
||||||
|
]);
|
||||||
|
|
||||||
res.usage = [
|
res.usage = [
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -614,10 +614,16 @@ class Filesystem {
|
||||||
});
|
});
|
||||||
|
|
||||||
child.on("close", function (code) {
|
child.on("close", function (code) {
|
||||||
const result = { code, stdout, stderr };
|
const result = { code, stdout, stderr, timeout: false };
|
||||||
if (timeout) {
|
if (timeout) {
|
||||||
clearTimeout(timeout);
|
clearTimeout(timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (code === null) {
|
||||||
|
result.timeout = true;
|
||||||
|
reject(result);
|
||||||
|
}
|
||||||
|
|
||||||
if (code) {
|
if (code) {
|
||||||
console.log(
|
console.log(
|
||||||
"failed to execute filesystem command: %s, response: %j",
|
"failed to execute filesystem command: %s, response: %j",
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,17 @@
|
||||||
const cp = require("child_process");
|
const cp = require("child_process");
|
||||||
const { Filesystem } = require("../utils/filesystem");
|
const { Filesystem } = require("../utils/filesystem");
|
||||||
|
|
||||||
|
// avoid using avail,size,used as it causes hangs when the fs is stale
|
||||||
FINDMNT_COMMON_OPTIONS = [
|
FINDMNT_COMMON_OPTIONS = [
|
||||||
"--output",
|
"--output",
|
||||||
"source,target,fstype,label,options,avail,size,used",
|
"source,target,fstype,label,options",
|
||||||
"-b",
|
"-b",
|
||||||
"-J",
|
"-J",
|
||||||
"--nofsroot", // prevents unwanted behavior with cifs volumes
|
"--nofsroot", // prevents unwanted behavior with cifs volumes
|
||||||
];
|
];
|
||||||
|
|
||||||
|
DEFAUT_TIMEOUT = 30000;
|
||||||
|
|
||||||
class Mount {
|
class Mount {
|
||||||
constructor(options = {}) {
|
constructor(options = {}) {
|
||||||
const mount = this;
|
const mount = this;
|
||||||
|
|
@ -142,9 +145,15 @@ class Mount {
|
||||||
*
|
*
|
||||||
* @param {*} path
|
* @param {*} path
|
||||||
*/
|
*/
|
||||||
async getMountDetails(path) {
|
async getMountDetails(path, extraOutputProperties = []) {
|
||||||
const mount = this;
|
const mount = this;
|
||||||
let args = [];
|
let args = [];
|
||||||
|
const common_options = FINDMNT_COMMON_OPTIONS;
|
||||||
|
if (extraOutputProperties.length > 0) {
|
||||||
|
common_options[1] =
|
||||||
|
common_options[1] + "," + extraOutputProperties.join(",");
|
||||||
|
}
|
||||||
|
|
||||||
args = args.concat(["--mountpoint", path]);
|
args = args.concat(["--mountpoint", path]);
|
||||||
args = args.concat(FINDMNT_COMMON_OPTIONS);
|
args = args.concat(FINDMNT_COMMON_OPTIONS);
|
||||||
let result;
|
let result;
|
||||||
|
|
@ -279,7 +288,11 @@ class Mount {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
exec(command, args, options) {
|
exec(command, args, options = {}) {
|
||||||
|
if (!options.hasOwnProperty("timeout")) {
|
||||||
|
options.timeout = DEFAUT_TIMEOUT;
|
||||||
|
}
|
||||||
|
|
||||||
const mount = this;
|
const mount = this;
|
||||||
args = args || [];
|
args = args || [];
|
||||||
|
|
||||||
|
|
@ -303,6 +316,10 @@ class Mount {
|
||||||
console.log("executing mount command: %s", cleansedLog);
|
console.log("executing mount command: %s", cleansedLog);
|
||||||
const child = mount.options.executor.spawn(command, args, options);
|
const child = mount.options.executor.spawn(command, args, options);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* timeout option natively supported since v16
|
||||||
|
* TODO: properly handle this based on nodejs version
|
||||||
|
*/
|
||||||
let didTimeout = false;
|
let didTimeout = false;
|
||||||
if (options && options.timeout) {
|
if (options && options.timeout) {
|
||||||
timeout = setTimeout(() => {
|
timeout = setTimeout(() => {
|
||||||
|
|
@ -321,10 +338,18 @@ class Mount {
|
||||||
});
|
});
|
||||||
|
|
||||||
child.on("close", function (code) {
|
child.on("close", function (code) {
|
||||||
const result = { code, stdout, stderr };
|
const result = { code, stdout, stderr, timeout: false };
|
||||||
|
|
||||||
if (timeout) {
|
if (timeout) {
|
||||||
clearTimeout(timeout);
|
clearTimeout(timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// timeout scenario
|
||||||
|
if (code === null) {
|
||||||
|
result.timeout = true;
|
||||||
|
reject(result);
|
||||||
|
}
|
||||||
|
|
||||||
if (code) {
|
if (code) {
|
||||||
reject(result);
|
reject(result);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue