Merge 8ef05936d8 into 55c36d62ff
This commit is contained in:
commit
e9fe7ea61f
|
|
@ -15,6 +15,15 @@ zfs:
|
||||||
# the example below is useful for TrueNAS 12
|
# the example below is useful for TrueNAS 12
|
||||||
#cli:
|
#cli:
|
||||||
# sudoEnabled: true
|
# sudoEnabled: true
|
||||||
|
#
|
||||||
|
# instead of setting sudoEnabled to true, sudo can be
|
||||||
|
# reduced only to specific (supported) zfs commands
|
||||||
|
# sudoEnabledCommands:
|
||||||
|
# - mount
|
||||||
|
# - unmount
|
||||||
|
# - share
|
||||||
|
# - unshare
|
||||||
|
#
|
||||||
# paths:
|
# paths:
|
||||||
# zfs: /usr/local/sbin/zfs
|
# zfs: /usr/local/sbin/zfs
|
||||||
# zpool: /usr/local/sbin/zpool
|
# zpool: /usr/local/sbin/zpool
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,7 @@ class ControllerZfsGenericDriver extends ControllerZfsBaseDriver {
|
||||||
}
|
}
|
||||||
|
|
||||||
options.sudo = _.get(this.options, "zfs.cli.sudoEnabled", false);
|
options.sudo = _.get(this.options, "zfs.cli.sudoEnabled", false);
|
||||||
|
options.sudoCommands = _.get(this.options, "zfs.cli.sudoEnabledCommands", []);
|
||||||
|
|
||||||
if (typeof this.setZetabyteCustomOptions === "function") {
|
if (typeof this.setZetabyteCustomOptions === "function") {
|
||||||
await this.setZetabyteCustomOptions(options);
|
await this.setZetabyteCustomOptions(options);
|
||||||
|
|
@ -92,6 +93,37 @@ class ControllerZfsGenericDriver extends ControllerZfsBaseDriver {
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async setShareProperty(zb, datasetName, propertyDict) {
|
||||||
|
try {
|
||||||
|
await zb.zfs.set(datasetName, propertyDict);
|
||||||
|
} catch (err) {
|
||||||
|
if (
|
||||||
|
err.toString().includes("unable to reshare") &&
|
||||||
|
zb.options.sudoCommands.includes("mount")
|
||||||
|
) {
|
||||||
|
await zb.zfs.share(datasetName);
|
||||||
|
} else {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async unsetShareProperty(zb, datasetName, propertyKey) {
|
||||||
|
if (zb.options.sudoCommands.includes("unshare")) {
|
||||||
|
await zb.zfs.unshare(datasetName, { idempotent: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await zb.zfs.inherit(datasetName, propertyKey);
|
||||||
|
} catch (err) {
|
||||||
|
if (err.toString().includes("dataset does not exist")) {
|
||||||
|
// do nothing
|
||||||
|
} else {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* should create any necessary share resources
|
* should create any necessary share resources
|
||||||
* should set the SHARE_VOLUME_CONTEXT_PROPERTY_NAME propery
|
* should set the SHARE_VOLUME_CONTEXT_PROPERTY_NAME propery
|
||||||
|
|
@ -118,7 +150,7 @@ class ControllerZfsGenericDriver extends ControllerZfsBaseDriver {
|
||||||
key
|
key
|
||||||
]
|
]
|
||||||
) {
|
) {
|
||||||
await zb.zfs.set(datasetName, {
|
await this.setShareProperty(zb, datasetName, {
|
||||||
[key]:
|
[key]:
|
||||||
this.options.nfs.shareStrategySetDatasetProperties
|
this.options.nfs.shareStrategySetDatasetProperties
|
||||||
.properties[key],
|
.properties[key],
|
||||||
|
|
@ -152,7 +184,7 @@ class ControllerZfsGenericDriver extends ControllerZfsBaseDriver {
|
||||||
key
|
key
|
||||||
]
|
]
|
||||||
) {
|
) {
|
||||||
await zb.zfs.set(datasetName, {
|
await this.setShareProperty(zb, datasetName, {
|
||||||
[key]:
|
[key]:
|
||||||
this.options.smb.shareStrategySetDatasetProperties
|
this.options.smb.shareStrategySetDatasetProperties
|
||||||
.properties[key],
|
.properties[key],
|
||||||
|
|
@ -572,15 +604,7 @@ save_config filename=${this.options.nvmeof.shareStrategySpdkCli.configPath}
|
||||||
key
|
key
|
||||||
]
|
]
|
||||||
) {
|
) {
|
||||||
try {
|
await this.unsetShareProperty(zb, datasetName, key);
|
||||||
await zb.zfs.inherit(datasetName, key);
|
|
||||||
} catch (err) {
|
|
||||||
if (err.toString().includes("dataset does not exist")) {
|
|
||||||
// do nothing
|
|
||||||
} else {
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
await GeneralUtils.sleep(2000); // let things settle
|
await GeneralUtils.sleep(2000); // let things settle
|
||||||
|
|
@ -602,15 +626,7 @@ save_config filename=${this.options.nvmeof.shareStrategySpdkCli.configPath}
|
||||||
key
|
key
|
||||||
]
|
]
|
||||||
) {
|
) {
|
||||||
try {
|
await this.unsetShareProperty(zb, datasetName, key);
|
||||||
await zb.zfs.inherit(datasetName, key);
|
|
||||||
} catch (err) {
|
|
||||||
if (err.toString().includes("dataset does not exist")) {
|
|
||||||
// do nothing
|
|
||||||
} else {
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
await GeneralUtils.sleep(2000); // let things settle
|
await GeneralUtils.sleep(2000); // let things settle
|
||||||
|
|
|
||||||
|
|
@ -66,6 +66,7 @@ class ControllerZfsLocalDriver extends ControllerZfsBaseDriver {
|
||||||
};
|
};
|
||||||
|
|
||||||
options.sudo = _.get(this.options, "zfs.cli.sudoEnabled", false);
|
options.sudo = _.get(this.options, "zfs.cli.sudoEnabled", false);
|
||||||
|
options.sudoCommands = _.get(this.options, "zfs.cli.sudoEnabledCommands", []);
|
||||||
|
|
||||||
if (typeof this.setZetabyteCustomOptions === "function") {
|
if (typeof this.setZetabyteCustomOptions === "function") {
|
||||||
await this.setZetabyteCustomOptions(options);
|
await this.setZetabyteCustomOptions(options);
|
||||||
|
|
|
||||||
|
|
@ -159,7 +159,7 @@ class ControllerZfsBaseDriver extends CsiBaseDriver {
|
||||||
driver.ctx.logger.verbose("whoami command: %s", command);
|
driver.ctx.logger.verbose("whoami command: %s", command);
|
||||||
const response = await execClient.exec(command);
|
const response = await execClient.exec(command);
|
||||||
if (response.code !== 0) {
|
if (response.code !== 0) {
|
||||||
throw new Error("failed to run uname to determine max zvol name length");
|
throw new Error("failed to run whoami to determine name of user");
|
||||||
} else {
|
} else {
|
||||||
return response.stdout.trim();
|
return response.stdout.trim();
|
||||||
}
|
}
|
||||||
|
|
@ -499,9 +499,10 @@ class ControllerZfsBaseDriver extends CsiBaseDriver {
|
||||||
async setFilesystemMode(path, mode) {
|
async setFilesystemMode(path, mode) {
|
||||||
const driver = this;
|
const driver = this;
|
||||||
const execClient = this.getExecClient();
|
const execClient = this.getExecClient();
|
||||||
|
const sudoEnabled = _.get(this.options, "zfs.cli.sudoEnabled", false);
|
||||||
|
|
||||||
let command = execClient.buildCommand("chmod", [mode, path]);
|
let command = execClient.buildCommand("chmod", [mode, path]);
|
||||||
if ((await driver.getWhoAmI()) != "root") {
|
if ((await driver.getWhoAmI()) != "root" && sudoEnabled) {
|
||||||
command = (await driver.getSudoPath()) + " " + command;
|
command = (await driver.getSudoPath()) + " " + command;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -80,6 +80,7 @@ class FreeNASSshDriver extends ControllerZfsBaseDriver {
|
||||||
}
|
}
|
||||||
|
|
||||||
options.sudo = _.get(this.options, "zfs.cli.sudoEnabled", false);
|
options.sudo = _.get(this.options, "zfs.cli.sudoEnabled", false);
|
||||||
|
options.sudoCommands = _.get(this.options, "zfs.cli.sudoEnabledCommands", []);
|
||||||
|
|
||||||
if (typeof this.setZetabyteCustomOptions === "function") {
|
if (typeof this.setZetabyteCustomOptions === "function") {
|
||||||
await this.setZetabyteCustomOptions(options);
|
await this.setZetabyteCustomOptions(options);
|
||||||
|
|
|
||||||
214
src/utils/zfs.js
214
src/utils/zfs.js
|
|
@ -904,6 +904,13 @@ class Zetabyte {
|
||||||
};
|
};
|
||||||
|
|
||||||
zb.zfs = {
|
zb.zfs = {
|
||||||
|
SUDO_COMMANDS: [
|
||||||
|
"mount",
|
||||||
|
"unmount",
|
||||||
|
"share",
|
||||||
|
"unshare"
|
||||||
|
],
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* zfs create [-pu] [-o property=value]... filesystem
|
* zfs create [-pu] [-o property=value]... filesystem
|
||||||
* zfs create [-ps] [-b blocksize] [-o property=value]... -V size volume
|
* zfs create [-ps] [-b blocksize] [-o property=value]... -V size volume
|
||||||
|
|
@ -913,6 +920,7 @@ class Zetabyte {
|
||||||
*/
|
*/
|
||||||
create: function (dataset, options = {}) {
|
create: function (dataset, options = {}) {
|
||||||
if (!(arguments.length >= 1)) throw new (Error("Invalid arguments"))();
|
if (!(arguments.length >= 1)) throw new (Error("Invalid arguments"))();
|
||||||
|
const zfs = this;
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const idempotent =
|
const idempotent =
|
||||||
|
|
@ -921,11 +929,12 @@ class Zetabyte {
|
||||||
: "idempotent" in zb.options
|
: "idempotent" in zb.options
|
||||||
? zb.options.idempotent
|
? zb.options.idempotent
|
||||||
: false;
|
: false;
|
||||||
|
const shouldSudoMount = !zb.options.sudo && zb.options.sudoCommands.includes("mount");
|
||||||
|
|
||||||
let args = [];
|
let args = [];
|
||||||
args.push("create");
|
args.push("create");
|
||||||
if (options.parents) args.push("-p");
|
if (options.parents) args.push("-p");
|
||||||
if (options.unmounted) args.push("-u");
|
if (options.unmounted || shouldSudoMount) args.push("-u");
|
||||||
if (options.blocksize) args = args.concat(["-b", options.blocksize]);
|
if (options.blocksize) args = args.concat(["-b", options.blocksize]);
|
||||||
if (options.properties) {
|
if (options.properties) {
|
||||||
for (let [key, value] of Object.entries(options.properties)) {
|
for (let [key, value] of Object.entries(options.properties)) {
|
||||||
|
|
@ -945,9 +954,17 @@ class Zetabyte {
|
||||||
if (
|
if (
|
||||||
error &&
|
error &&
|
||||||
!(idempotent && stderr.includes("dataset already exists"))
|
!(idempotent && stderr.includes("dataset already exists"))
|
||||||
)
|
) {
|
||||||
return reject(zb.helpers.zfsError(error, stderr));
|
return reject(zb.helpers.zfsError(error, stderr));
|
||||||
return resolve(stdout);
|
}
|
||||||
|
|
||||||
|
if (shouldSudoMount) {
|
||||||
|
zfs.mount(dataset, { idempotent })
|
||||||
|
.then(out => resolve(stdout + "; " + out))
|
||||||
|
.catch(err => reject(err));
|
||||||
|
} else {
|
||||||
|
return resolve(stdout);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
@ -964,6 +981,7 @@ class Zetabyte {
|
||||||
*/
|
*/
|
||||||
destroy: function (dataset, options = {}) {
|
destroy: function (dataset, options = {}) {
|
||||||
if (!(arguments.length >= 1)) throw Error("Invalid arguments");
|
if (!(arguments.length >= 1)) throw Error("Invalid arguments");
|
||||||
|
const zfs = this;
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const idempotent =
|
const idempotent =
|
||||||
|
|
@ -985,23 +1003,32 @@ class Zetabyte {
|
||||||
if (options.defer) args.push("-d");
|
if (options.defer) args.push("-d");
|
||||||
args.push(dataset);
|
args.push(dataset);
|
||||||
|
|
||||||
zb.exec(
|
const destroyExec = function() {
|
||||||
zb.options.paths.zfs,
|
zb.exec(
|
||||||
args,
|
zb.options.paths.zfs,
|
||||||
{ timeout: zb.options.timeout },
|
args,
|
||||||
function (error, stdout, stderr) {
|
{ timeout: zb.options.timeout },
|
||||||
if (
|
function (error, stdout, stderr) {
|
||||||
error &&
|
if (
|
||||||
!(
|
error &&
|
||||||
idempotent &&
|
!(
|
||||||
(stderr.includes("dataset does not exist") ||
|
idempotent &&
|
||||||
stderr.includes("could not find any snapshots to destroy"))
|
(stderr.includes("dataset does not exist") ||
|
||||||
|
stderr.includes("could not find any snapshots to destroy"))
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
return reject(zb.helpers.zfsError(error, stderr));
|
||||||
return reject(zb.helpers.zfsError(error, stderr));
|
return resolve(stdout);
|
||||||
return resolve(stdout);
|
}
|
||||||
}
|
);
|
||||||
);
|
};
|
||||||
|
if (zb.options.sudoCommands.includes("unmount")) {
|
||||||
|
zfs.unmount(dataset, { idempotent })
|
||||||
|
.then(() => destroyExec())
|
||||||
|
.catch(err => reject(err));
|
||||||
|
} else {
|
||||||
|
destroyExec();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -1224,7 +1251,7 @@ class Zetabyte {
|
||||||
let args = [];
|
let args = [];
|
||||||
args.push("rename");
|
args.push("rename");
|
||||||
if (options.parents) args.push("-p");
|
if (options.parents) args.push("-p");
|
||||||
if (options.unmounted) args.push("-u");
|
if (options.unmounted || (!zb.options.sudo && zb.options.sudoCommands.includes("mount"))) args.push("-u");
|
||||||
if (options.force) args.push("-f");
|
if (options.force) args.push("-f");
|
||||||
if (options.recurse) args.push("-r");
|
if (options.recurse) args.push("-r");
|
||||||
args.push(source);
|
args.push(source);
|
||||||
|
|
@ -1522,6 +1549,145 @@ class Zetabyte {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* zfs mount filesystem
|
||||||
|
*
|
||||||
|
* @param {*} dataset
|
||||||
|
* @param {*} options
|
||||||
|
*/
|
||||||
|
mount: function (dataset, options = {}) {
|
||||||
|
if (!(arguments.length >= 1)) throw Error("Invalid arguments");
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const idempotent =
|
||||||
|
"idempotent" in options
|
||||||
|
? options.idempotent
|
||||||
|
: "idempotent" in zb.options
|
||||||
|
? zb.options.idempotent
|
||||||
|
: false;
|
||||||
|
|
||||||
|
let args = [];
|
||||||
|
args.push("mount");
|
||||||
|
args.push(dataset);
|
||||||
|
|
||||||
|
zb.exec(
|
||||||
|
zb.options.paths.zfs,
|
||||||
|
args,
|
||||||
|
{ timeout: zb.options.timeout },
|
||||||
|
function (error, stdout, stderr) {
|
||||||
|
if (
|
||||||
|
error &&
|
||||||
|
!(idempotent && stderr.includes("filesystem already mounted"))
|
||||||
|
) {
|
||||||
|
return reject(zb.helpers.zfsError(error, stderr));
|
||||||
|
}
|
||||||
|
return resolve(stdout);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* zfs unmount filesystem
|
||||||
|
*
|
||||||
|
* @param {*} dataset
|
||||||
|
* @param {*} options
|
||||||
|
*/
|
||||||
|
unmount: function (dataset, options = {}) {
|
||||||
|
if (!(arguments.length >= 1)) throw Error("Invalid arguments");
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const idempotent =
|
||||||
|
"idempotent" in options
|
||||||
|
? options.idempotent
|
||||||
|
: "idempotent" in zb.options
|
||||||
|
? zb.options.idempotent
|
||||||
|
: false;
|
||||||
|
|
||||||
|
let args = [];
|
||||||
|
args.push("unmount");
|
||||||
|
args.push(dataset);
|
||||||
|
|
||||||
|
zb.exec(
|
||||||
|
zb.options.paths.zfs,
|
||||||
|
args,
|
||||||
|
{ timeout: zb.options.timeout },
|
||||||
|
function (error, stdout, stderr) {
|
||||||
|
if (
|
||||||
|
error &&
|
||||||
|
!(idempotent && stderr.includes("not currently mounted"))
|
||||||
|
) {
|
||||||
|
return reject(zb.helpers.zfsError(error, stderr));
|
||||||
|
}
|
||||||
|
return resolve(stdout);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* zfs share filesystem
|
||||||
|
*
|
||||||
|
* @param {*} dataset
|
||||||
|
*/
|
||||||
|
share: function (dataset) {
|
||||||
|
if (arguments.length != 1) throw Error("Invalid arguments");
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
let args = [];
|
||||||
|
args.push("share");
|
||||||
|
args.push(dataset);
|
||||||
|
|
||||||
|
zb.exec(
|
||||||
|
zb.options.paths.zfs,
|
||||||
|
args,
|
||||||
|
{ timeout: zb.options.timeout },
|
||||||
|
function (error, stdout, stderr) {
|
||||||
|
if (error) return reject(zb.helpers.zfsError(error, stderr));
|
||||||
|
return resolve(stdout);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* zfs unshare filesystem
|
||||||
|
*
|
||||||
|
* @param {*} dataset
|
||||||
|
* @param {*} options
|
||||||
|
*/
|
||||||
|
unshare: function (dataset, options = {}) {
|
||||||
|
if (!(arguments.length >= 1)) throw Error("Invalid arguments");
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const idempotent =
|
||||||
|
"idempotent" in options
|
||||||
|
? options.idempotent
|
||||||
|
: "idempotent" in zb.options
|
||||||
|
? zb.options.idempotent
|
||||||
|
: false;
|
||||||
|
|
||||||
|
let args = [];
|
||||||
|
args.push("unshare");
|
||||||
|
args.push(dataset);
|
||||||
|
|
||||||
|
zb.exec(
|
||||||
|
zb.options.paths.zfs,
|
||||||
|
args,
|
||||||
|
{ timeout: zb.options.timeout },
|
||||||
|
function (error, stdout, stderr) {
|
||||||
|
if (
|
||||||
|
error &&
|
||||||
|
!(idempotent && stderr.includes("not currently shared"))
|
||||||
|
) {
|
||||||
|
return reject(zb.helpers.zfsError(error, stderr));
|
||||||
|
}
|
||||||
|
return resolve(stdout);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1564,7 +1730,13 @@ class Zetabyte {
|
||||||
use_sudo = options.sudo;
|
use_sudo = options.sudo;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (use_sudo) {
|
if (
|
||||||
|
use_sudo || (
|
||||||
|
Array.isArray(args) && args.length > 0 &&
|
||||||
|
zb.zfs.SUDO_COMMANDS.includes(args[0]) &&
|
||||||
|
zb.options.sudoCommands.includes(args[0])
|
||||||
|
)
|
||||||
|
) {
|
||||||
args = args || [];
|
args = args || [];
|
||||||
args.unshift(command);
|
args.unshift(command);
|
||||||
command = zb.options.paths.sudo;
|
command = zb.options.paths.sudo;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue