Compare commits
4 Commits
f2f52b8be9
...
e9fe7ea61f
| Author | SHA1 | Date |
|---|---|---|
|
|
e9fe7ea61f | |
|
|
55c36d62ff | |
|
|
edfdf86f2d | |
|
|
8ef05936d8 |
|
|
@ -20,7 +20,7 @@ jobs:
|
|||
access_token: ${{ github.token }}
|
||||
|
||||
build-npm-linux-amd64:
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
|
|
|
|||
|
|
@ -15,6 +15,15 @@ zfs:
|
|||
# the example below is useful for TrueNAS 12
|
||||
#cli:
|
||||
# sudoEnabled: true
|
||||
#
|
||||
# instead of setting sudoEnabled to true, sudo can be
|
||||
# reduced only to specific (supported) zfs commands
|
||||
# sudoEnabledCommands:
|
||||
# - mount
|
||||
# - unmount
|
||||
# - share
|
||||
# - unshare
|
||||
#
|
||||
# paths:
|
||||
# zfs: /usr/local/sbin/zfs
|
||||
# zpool: /usr/local/sbin/zpool
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@ class ControllerZfsGenericDriver extends ControllerZfsBaseDriver {
|
|||
}
|
||||
|
||||
options.sudo = _.get(this.options, "zfs.cli.sudoEnabled", false);
|
||||
options.sudoCommands = _.get(this.options, "zfs.cli.sudoEnabledCommands", []);
|
||||
|
||||
if (typeof this.setZetabyteCustomOptions === "function") {
|
||||
await this.setZetabyteCustomOptions(options);
|
||||
|
|
@ -92,6 +93,37 @@ class ControllerZfsGenericDriver extends ControllerZfsBaseDriver {
|
|||
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 set the SHARE_VOLUME_CONTEXT_PROPERTY_NAME propery
|
||||
|
|
@ -118,7 +150,7 @@ class ControllerZfsGenericDriver extends ControllerZfsBaseDriver {
|
|||
key
|
||||
]
|
||||
) {
|
||||
await zb.zfs.set(datasetName, {
|
||||
await this.setShareProperty(zb, datasetName, {
|
||||
[key]:
|
||||
this.options.nfs.shareStrategySetDatasetProperties
|
||||
.properties[key],
|
||||
|
|
@ -152,7 +184,7 @@ class ControllerZfsGenericDriver extends ControllerZfsBaseDriver {
|
|||
key
|
||||
]
|
||||
) {
|
||||
await zb.zfs.set(datasetName, {
|
||||
await this.setShareProperty(zb, datasetName, {
|
||||
[key]:
|
||||
this.options.smb.shareStrategySetDatasetProperties
|
||||
.properties[key],
|
||||
|
|
@ -572,15 +604,7 @@ save_config filename=${this.options.nvmeof.shareStrategySpdkCli.configPath}
|
|||
key
|
||||
]
|
||||
) {
|
||||
try {
|
||||
await zb.zfs.inherit(datasetName, key);
|
||||
} catch (err) {
|
||||
if (err.toString().includes("dataset does not exist")) {
|
||||
// do nothing
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
await this.unsetShareProperty(zb, datasetName, key);
|
||||
}
|
||||
}
|
||||
await GeneralUtils.sleep(2000); // let things settle
|
||||
|
|
@ -602,15 +626,7 @@ save_config filename=${this.options.nvmeof.shareStrategySpdkCli.configPath}
|
|||
key
|
||||
]
|
||||
) {
|
||||
try {
|
||||
await zb.zfs.inherit(datasetName, key);
|
||||
} catch (err) {
|
||||
if (err.toString().includes("dataset does not exist")) {
|
||||
// do nothing
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
await this.unsetShareProperty(zb, datasetName, key);
|
||||
}
|
||||
}
|
||||
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.sudoCommands = _.get(this.options, "zfs.cli.sudoEnabledCommands", []);
|
||||
|
||||
if (typeof this.setZetabyteCustomOptions === "function") {
|
||||
await this.setZetabyteCustomOptions(options);
|
||||
|
|
|
|||
|
|
@ -159,7 +159,7 @@ class ControllerZfsBaseDriver extends CsiBaseDriver {
|
|||
driver.ctx.logger.verbose("whoami command: %s", command);
|
||||
const response = await execClient.exec(command);
|
||||
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 {
|
||||
return response.stdout.trim();
|
||||
}
|
||||
|
|
@ -499,9 +499,10 @@ class ControllerZfsBaseDriver extends CsiBaseDriver {
|
|||
async setFilesystemMode(path, mode) {
|
||||
const driver = this;
|
||||
const execClient = this.getExecClient();
|
||||
const sudoEnabled = _.get(this.options, "zfs.cli.sudoEnabled", false);
|
||||
|
||||
let command = execClient.buildCommand("chmod", [mode, path]);
|
||||
if ((await driver.getWhoAmI()) != "root") {
|
||||
if ((await driver.getWhoAmI()) != "root" && sudoEnabled) {
|
||||
command = (await driver.getSudoPath()) + " " + command;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -80,6 +80,7 @@ class FreeNASSshDriver extends ControllerZfsBaseDriver {
|
|||
}
|
||||
|
||||
options.sudo = _.get(this.options, "zfs.cli.sudoEnabled", false);
|
||||
options.sudoCommands = _.get(this.options, "zfs.cli.sudoEnabledCommands", []);
|
||||
|
||||
if (typeof this.setZetabyteCustomOptions === "function") {
|
||||
await this.setZetabyteCustomOptions(options);
|
||||
|
|
@ -120,6 +121,15 @@ class FreeNASSshDriver extends ControllerZfsBaseDriver {
|
|||
chroot: "/usr/sbin/chroot",
|
||||
};
|
||||
}
|
||||
|
||||
if (isScale && Number(majorMinor) >= 25) {
|
||||
options.paths = {
|
||||
zfs: "/usr/sbin/zfs",
|
||||
zpool: "/usr/sbin/zpool",
|
||||
sudo: "/usr/bin/sudo",
|
||||
chroot: "/usr/sbin/chroot",
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -142,10 +152,13 @@ class FreeNASSshDriver extends ControllerZfsBaseDriver {
|
|||
}
|
||||
|
||||
async getTrueNASHttpApiClient() {
|
||||
return this.ctx.registry.getAsync(`${__REGISTRY_NS__}:api_client`, async () => {
|
||||
const httpClient = await this.getHttpClient();
|
||||
return new TrueNASApiClient(httpClient, this.ctx.cache);
|
||||
});
|
||||
return this.ctx.registry.getAsync(
|
||||
`${__REGISTRY_NS__}:api_client`,
|
||||
async () => {
|
||||
const httpClient = await this.getHttpClient();
|
||||
return new TrueNASApiClient(httpClient, this.ctx.cache);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
getDriverShareType() {
|
||||
|
|
|
|||
214
src/utils/zfs.js
214
src/utils/zfs.js
|
|
@ -904,6 +904,13 @@ class Zetabyte {
|
|||
};
|
||||
|
||||
zb.zfs = {
|
||||
SUDO_COMMANDS: [
|
||||
"mount",
|
||||
"unmount",
|
||||
"share",
|
||||
"unshare"
|
||||
],
|
||||
|
||||
/**
|
||||
* zfs create [-pu] [-o property=value]... filesystem
|
||||
* zfs create [-ps] [-b blocksize] [-o property=value]... -V size volume
|
||||
|
|
@ -913,6 +920,7 @@ class Zetabyte {
|
|||
*/
|
||||
create: function (dataset, options = {}) {
|
||||
if (!(arguments.length >= 1)) throw new (Error("Invalid arguments"))();
|
||||
const zfs = this;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const idempotent =
|
||||
|
|
@ -921,11 +929,12 @@ class Zetabyte {
|
|||
: "idempotent" in zb.options
|
||||
? zb.options.idempotent
|
||||
: false;
|
||||
const shouldSudoMount = !zb.options.sudo && zb.options.sudoCommands.includes("mount");
|
||||
|
||||
let args = [];
|
||||
args.push("create");
|
||||
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.properties) {
|
||||
for (let [key, value] of Object.entries(options.properties)) {
|
||||
|
|
@ -945,9 +954,17 @@ class Zetabyte {
|
|||
if (
|
||||
error &&
|
||||
!(idempotent && stderr.includes("dataset already exists"))
|
||||
)
|
||||
) {
|
||||
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 = {}) {
|
||||
if (!(arguments.length >= 1)) throw Error("Invalid arguments");
|
||||
const zfs = this;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const idempotent =
|
||||
|
|
@ -985,23 +1003,32 @@ class Zetabyte {
|
|||
if (options.defer) args.push("-d");
|
||||
args.push(dataset);
|
||||
|
||||
zb.exec(
|
||||
zb.options.paths.zfs,
|
||||
args,
|
||||
{ timeout: zb.options.timeout },
|
||||
function (error, stdout, stderr) {
|
||||
if (
|
||||
error &&
|
||||
!(
|
||||
idempotent &&
|
||||
(stderr.includes("dataset does not exist") ||
|
||||
stderr.includes("could not find any snapshots to destroy"))
|
||||
const destroyExec = function() {
|
||||
zb.exec(
|
||||
zb.options.paths.zfs,
|
||||
args,
|
||||
{ timeout: zb.options.timeout },
|
||||
function (error, stdout, stderr) {
|
||||
if (
|
||||
error &&
|
||||
!(
|
||||
idempotent &&
|
||||
(stderr.includes("dataset does not exist") ||
|
||||
stderr.includes("could not find any snapshots to destroy"))
|
||||
)
|
||||
)
|
||||
)
|
||||
return reject(zb.helpers.zfsError(error, stderr));
|
||||
return resolve(stdout);
|
||||
}
|
||||
);
|
||||
return reject(zb.helpers.zfsError(error, stderr));
|
||||
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 = [];
|
||||
args.push("rename");
|
||||
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.recurse) args.push("-r");
|
||||
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;
|
||||
}
|
||||
|
||||
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.unshift(command);
|
||||
command = zb.options.paths.sudo;
|
||||
|
|
|
|||
Loading…
Reference in New Issue