further abstraction to support zfs-local

Signed-off-by: Travis Glenn Hansen <travisghansen@yahoo.com>
This commit is contained in:
Travis Glenn Hansen 2022-02-01 11:35:58 -07:00
parent 7c5448bff7
commit 94f6df240b
3 changed files with 147 additions and 42 deletions

View File

@ -1,12 +1,50 @@
const { ControllerZfsSshBaseDriver } = require("../controller-zfs-ssh"); const { ControllerZfsBaseDriver } = require("../controller-zfs");
const { GrpcError, grpc } = require("../../utils/grpc"); const { GrpcError, grpc } = require("../../utils/grpc");
const SshClient = require("../../utils/ssh").SshClient;
const sleep = require("../../utils/general").sleep; const sleep = require("../../utils/general").sleep;
const { Zetabyte, ZfsSshProcessManager } = require("../../utils/zfs");
const Handlebars = require("handlebars"); const Handlebars = require("handlebars");
const ISCSI_ASSETS_NAME_PROPERTY_NAME = "democratic-csi:iscsi_assets_name"; const ISCSI_ASSETS_NAME_PROPERTY_NAME = "democratic-csi:iscsi_assets_name";
class ControllerZfsGenericDriver extends ControllerZfsSshBaseDriver { class ControllerZfsGenericDriver extends ControllerZfsBaseDriver {
getExecClient() {
return new SshClient({
logger: this.ctx.logger,
connection: this.options.sshConnection,
});
}
async getZetabyte() {
const execClient = this.getExecClient();
const options = {};
options.executor = new ZfsSshProcessManager(execClient);
options.idempotent = true;
if (
this.options.zfs.hasOwnProperty("cli") &&
this.options.zfs.cli &&
this.options.zfs.cli.hasOwnProperty("paths")
) {
options.paths = this.options.zfs.cli.paths;
}
if (
this.options.zfs.hasOwnProperty("cli") &&
this.options.zfs.cli &&
this.options.zfs.cli.hasOwnProperty("sudoEnabled")
) {
options.sudo = this.getSudoEnabled();
}
if (typeof this.setZetabyteCustomOptions === "function") {
await this.setZetabyteCustomOptions(options);
}
return new Zetabyte(options);
}
/** /**
* cannot make this a storage class parameter as storage class/etc context is *not* sent * cannot make this a storage class parameter as storage class/etc context is *not* sent
* into various calls such as GetControllerCapabilities etc * into various calls such as GetControllerCapabilities etc
@ -30,7 +68,7 @@ class ControllerZfsGenericDriver extends ControllerZfsSshBaseDriver {
*/ */
async createShare(call, datasetName) { async createShare(call, datasetName) {
const zb = await this.getZetabyte(); const zb = await this.getZetabyte();
const sshClient = this.getSshClient(); const execClient = this.getExecClient();
let properties; let properties;
let response; let response;
@ -194,7 +232,7 @@ create /backstores/block/${iscsiName}
async deleteShare(call, datasetName) { async deleteShare(call, datasetName) {
const zb = await this.getZetabyte(); const zb = await this.getZetabyte();
const sshClient = this.getSshClient(); const execClient = this.getExecClient();
let response; let response;
let properties; let properties;
@ -317,7 +355,7 @@ delete ${iscsiName}
} }
async targetCliCommand(data) { async targetCliCommand(data) {
const sshClient = this.getSshClient(); const execClient = this.getExecClient();
const driver = this; const driver = this;
data = data.trim(); data = data.trim();
@ -361,8 +399,8 @@ delete ${iscsiName}
let options = { let options = {
pty: true, pty: true,
}; };
let response = await sshClient.exec( let response = await execClient.exec(
sshClient.buildCommand(command, args), execClient.buildCommand(command, args),
options options
); );
if (response.code != 0) { if (response.code != 0) {

View File

@ -35,6 +35,9 @@ const VOLUME_CONTEXT_PROVISIONER_INSTANCE_ID_PROPERTY_NAME =
* - async getZetabyte() * - async getZetabyte()
* - async setZetabyteCustomOptions(options) // optional * - async setZetabyteCustomOptions(options) // optional
* - getDriverZfsResourceType() // return "filesystem" or "volume" * - getDriverZfsResourceType() // return "filesystem" or "volume"
* - getFSTypes() // optional
* - getAccessModes() // optional
* - async getAccessibleTopology() // optional
* - async createShare(call, datasetName) // return appropriate volume_context for Node operations * - async createShare(call, datasetName) // return appropriate volume_context for Node operations
* - async deleteShare(call, datasetName) // no return expected * - async deleteShare(call, datasetName) // no return expected
* - async expandVolume(call, datasetName) // no return expected, used for restarting services etc if needed * - async expandVolume(call, datasetName) // no return expected, used for restarting services etc if needed
@ -182,6 +185,43 @@ class ControllerZfsBaseDriver extends CsiBaseDriver {
await zb.zfs.destroy(datasetName + "@%", options); await zb.zfs.destroy(datasetName + "@%", options);
} }
getFSTypes() {
const driverZfsResourceType = this.getDriverZfsResourceType();
switch (driverZfsResourceType) {
case "filesystem":
return ["nfs", "cifs"];
case "volume":
return ["ext3", "ext4", "ext4dev", "xfs"];
}
}
getAccessModes() {
const driverZfsResourceType = this.getDriverZfsResourceType();
switch (driverZfsResourceType) {
case "filesystem":
return [
"UNKNOWN",
"SINGLE_NODE_WRITER",
"SINGLE_NODE_SINGLE_WRITER", // added in v1.5.0
"SINGLE_NODE_MULTI_WRITER", // added in v1.5.0
"SINGLE_NODE_READER_ONLY",
"MULTI_NODE_READER_ONLY",
"MULTI_NODE_SINGLE_WRITER",
"MULTI_NODE_MULTI_WRITER",
];
case "volume":
return [
"UNKNOWN",
"SINGLE_NODE_WRITER",
"SINGLE_NODE_SINGLE_WRITER", // added in v1.5.0
"SINGLE_NODE_MULTI_WRITER", // added in v1.5.0
"SINGLE_NODE_READER_ONLY",
"MULTI_NODE_READER_ONLY",
"MULTI_NODE_SINGLE_WRITER",
];
}
}
assertCapabilities(capabilities) { assertCapabilities(capabilities) {
const driverZfsResourceType = this.getDriverZfsResourceType(); const driverZfsResourceType = this.getDriverZfsResourceType();
this.ctx.logger.verbose("validating capabilities: %j", capabilities); this.ctx.logger.verbose("validating capabilities: %j", capabilities);
@ -198,24 +238,13 @@ class ControllerZfsBaseDriver extends CsiBaseDriver {
if ( if (
capability.mount.fs_type && capability.mount.fs_type &&
!["nfs", "cifs"].includes(capability.mount.fs_type) !this.getFSTypes().includes(capability.mount.fs_type)
) { ) {
message = `invalid fs_type ${capability.mount.fs_type}`; message = `invalid fs_type ${capability.mount.fs_type}`;
return false; return false;
} }
if ( if (!this.getAccessModes().includes(capability.access_mode.mode)) {
![
"UNKNOWN",
"SINGLE_NODE_WRITER",
"SINGLE_NODE_SINGLE_WRITER", // added in v1.5.0
"SINGLE_NODE_MULTI_WRITER", // added in v1.5.0
"SINGLE_NODE_READER_ONLY",
"MULTI_NODE_READER_ONLY",
"MULTI_NODE_SINGLE_WRITER",
"MULTI_NODE_MULTI_WRITER",
].includes(capability.access_mode.mode)
) {
message = `invalid access_mode, ${capability.access_mode.mode}`; message = `invalid access_mode, ${capability.access_mode.mode}`;
return false; return false;
} }
@ -225,26 +254,14 @@ class ControllerZfsBaseDriver extends CsiBaseDriver {
if (capability.access_type == "mount") { if (capability.access_type == "mount") {
if ( if (
capability.mount.fs_type && capability.mount.fs_type &&
!["ext3", "ext4", "ext4dev", "xfs"].includes( !this.getFSTypes().includes(capability.mount.fs_type)
capability.mount.fs_type
)
) { ) {
message = `invalid fs_type ${capability.mount.fs_type}`; message = `invalid fs_type ${capability.mount.fs_type}`;
return false; return false;
} }
} }
if ( if (!this.getAccessModes().includes(capability.access_mode.mode)) {
![
"UNKNOWN",
"SINGLE_NODE_WRITER",
"SINGLE_NODE_SINGLE_WRITER", // added in v1.5.0
"SINGLE_NODE_MULTI_WRITER", // added in v1.5.0
"SINGLE_NODE_READER_ONLY",
"MULTI_NODE_READER_ONLY",
"MULTI_NODE_SINGLE_WRITER",
].includes(capability.access_mode.mode)
) {
message = `invalid access_mode, ${capability.access_mode.mode}`; message = `invalid access_mode, ${capability.access_mode.mode}`;
return false; return false;
} }
@ -344,6 +361,11 @@ class ControllerZfsBaseDriver extends CsiBaseDriver {
} }
} }
let accessible_topology;
if (typeof this.getAccessibleTopology === "function") {
accessible_topology = await this.getAccessibleTopology();
}
let volume = { let volume = {
// remove parent dataset info // remove parent dataset info
volume_id: row["name"].replace(new RegExp("^" + datasetName + "/"), ""), volume_id: row["name"].replace(new RegExp("^" + datasetName + "/"), ""),
@ -353,6 +375,7 @@ class ControllerZfsBaseDriver extends CsiBaseDriver {
: row["volsize"], : row["volsize"],
content_source: volume_content_source, content_source: volume_content_source,
volume_context, volume_context,
accessible_topology,
}; };
return volume; return volume;
@ -1101,6 +1124,11 @@ class ControllerZfsBaseDriver extends CsiBaseDriver {
// this should give us a relatively sane way to clean up artifacts over time // this should give us a relatively sane way to clean up artifacts over time
await zb.zfs.set(datasetName, { [SUCCESS_PROPERTY_NAME]: "true" }); await zb.zfs.set(datasetName, { [SUCCESS_PROPERTY_NAME]: "true" });
let accessible_topology;
if (typeof this.getAccessibleTopology === "function") {
accessible_topology = await this.getAccessibleTopology();
}
const res = { const res = {
volume: { volume: {
volume_id: name, volume_id: name,
@ -1112,6 +1140,7 @@ class ControllerZfsBaseDriver extends CsiBaseDriver {
: 0, : 0,
content_source: volume_content_source, content_source: volume_content_source,
volume_context, volume_context,
accessible_topology,
}, },
}; };

View File

@ -1,6 +1,8 @@
const { ControllerZfsSshBaseDriver } = require("../controller-zfs-ssh"); const { ControllerZfsBaseDriver } = require("../controller-zfs");
const { GrpcError, grpc } = require("../../utils/grpc"); const { GrpcError, grpc } = require("../../utils/grpc");
const SshClient = require("../../utils/ssh").SshClient;
const HttpClient = require("./http").Client; const HttpClient = require("./http").Client;
const { Zetabyte, ZfsSshProcessManager } = require("../../utils/zfs");
const Handlebars = require("handlebars"); const Handlebars = require("handlebars");
@ -18,7 +20,43 @@ const FREENAS_ISCSI_ASSETS_NAME_PROPERTY_NAME =
// used for in-memory cache of the version info // used for in-memory cache of the version info
const FREENAS_SYSTEM_VERSION_CACHE_KEY = "freenas:system_version"; const FREENAS_SYSTEM_VERSION_CACHE_KEY = "freenas:system_version";
class FreeNASSshDriver extends ControllerZfsSshBaseDriver { class FreeNASSshDriver extends ControllerZfsBaseDriver {
getExecClient() {
return new SshClient({
logger: this.ctx.logger,
connection: this.options.sshConnection,
});
}
async getZetabyte() {
const sshClient = this.getExecClient();
const options = {};
options.executor = new ZfsSshProcessManager(sshClient);
options.idempotent = true;
if (
this.options.zfs.hasOwnProperty("cli") &&
this.options.zfs.cli &&
this.options.zfs.cli.hasOwnProperty("paths")
) {
options.paths = this.options.zfs.cli.paths;
}
if (
this.options.zfs.hasOwnProperty("cli") &&
this.options.zfs.cli &&
this.options.zfs.cli.hasOwnProperty("sudoEnabled")
) {
options.sudo = this.getSudoEnabled();
}
if (typeof this.setZetabyteCustomOptions === "function") {
await this.setZetabyteCustomOptions(options);
}
return new Zetabyte(options);
}
/** /**
* cannot make this a storage class parameter as storage class/etc context is *not* sent * cannot make this a storage class parameter as storage class/etc context is *not* sent
* into various calls such as GetControllerCapabilities etc * into various calls such as GetControllerCapabilities etc
@ -169,7 +207,7 @@ class FreeNASSshDriver extends ControllerZfsSshBaseDriver {
async createShare(call, datasetName) { async createShare(call, datasetName) {
const driver = this; const driver = this;
const driverShareType = this.getDriverShareType(); const driverShareType = this.getDriverShareType();
const sshClient = this.getSshClient(); const execClient = this.getExecClient();
const httpClient = await this.getHttpClient(); const httpClient = await this.getHttpClient();
const apiVersion = httpClient.getApiVersion(); const apiVersion = httpClient.getApiVersion();
const zb = await this.getZetabyte(); const zb = await this.getZetabyte();
@ -1626,7 +1664,7 @@ class FreeNASSshDriver extends ControllerZfsSshBaseDriver {
async expandVolume(call, datasetName) { async expandVolume(call, datasetName) {
const driverShareType = this.getDriverShareType(); const driverShareType = this.getDriverShareType();
const sshClient = this.getSshClient(); const execClient = this.getExecClient();
const zb = await this.getZetabyte(); const zb = await this.getZetabyte();
switch (driverShareType) { switch (driverShareType) {
@ -1645,7 +1683,7 @@ class FreeNASSshDriver extends ControllerZfsSshBaseDriver {
properties[FREENAS_ISCSI_ASSETS_NAME_PROPERTY_NAME].value; properties[FREENAS_ISCSI_ASSETS_NAME_PROPERTY_NAME].value;
/** /**
* command = sshClient.buildCommand("systemctl", ["reload", "scst"]); * command = execClient.buildCommand("systemctl", ["reload", "scst"]);
* does not help ^ * does not help ^
* *
* echo 1 > /sys/kernel/scst_tgt/devices/${iscsiName}/resync_size * echo 1 > /sys/kernel/scst_tgt/devices/${iscsiName}/resync_size
@ -1657,13 +1695,13 @@ class FreeNASSshDriver extends ControllerZfsSshBaseDriver {
* midclt resync_lun_size_for_zvol tank/foo/bar * midclt resync_lun_size_for_zvol tank/foo/bar
* works on SCALE only ^ * works on SCALE only ^
*/ */
command = sshClient.buildCommand("sh", [ command = execClient.buildCommand("sh", [
"-c", "-c",
`echo 1 > /sys/kernel/scst_tgt/devices/${iscsiName}/resync_size`, `echo 1 > /sys/kernel/scst_tgt/devices/${iscsiName}/resync_size`,
]); ]);
reload = true; reload = true;
} else { } else {
command = sshClient.buildCommand("/etc/rc.d/ctld", ["reload"]); command = execClient.buildCommand("/etc/rc.d/ctld", ["reload"]);
reload = true; reload = true;
} }
@ -1677,7 +1715,7 @@ class FreeNASSshDriver extends ControllerZfsSshBaseDriver {
command command
); );
let response = await sshClient.exec(command); let response = await execClient.exec(command);
if (response.code != 0) { if (response.code != 0) {
throw new GrpcError( throw new GrpcError(
grpc.status.UNKNOWN, grpc.status.UNKNOWN,