windows node support

windows node support for smb/iscsi (csi-proxy)
better logging
support (for testing) generating volume_id from name
wait for chown/chmod jobs to complete (freenas)
general improvement to smb behavior throughout

Signed-off-by: Travis Glenn Hansen <travisghansen@yahoo.com>
This commit is contained in:
Travis Glenn Hansen
2022-04-17 10:57:57 -06:00
parent 68d0d57a99
commit 3f51f5b7a6
44 changed files with 5203 additions and 997 deletions
+7 -5
View File
@@ -3,7 +3,7 @@ const { GrpcError, grpc } = require("../../utils/grpc");
const registry = require("../../utils/registry");
const SynologyHttpClient = require("./http").SynologyHttpClient;
const semver = require("semver");
const sleep = require("../../utils/general").sleep;
const GeneralUtils = require("../../utils/general");
const __REGISTRY_NS__ = "ControllerSynologyDriver";
@@ -171,7 +171,9 @@ class ControllerSynologyDriver extends CsiBaseDriver {
if (
capability.mount.fs_type &&
!["nfs", "cifs"].includes(capability.mount.fs_type)
!GeneralUtils.default_supported_file_filesystems().includes(
capability.mount.fs_type
)
) {
message = `invalid fs_type ${capability.mount.fs_type}`;
return false;
@@ -198,7 +200,7 @@ class ControllerSynologyDriver extends CsiBaseDriver {
if (capability.access_type == "mount") {
if (
capability.mount.fs_type &&
!["btrfs", "ext3", "ext4", "ext4dev", "xfs"].includes(
!GeneralUtils.default_supported_block_filesystems().includes(
capability.mount.fs_type
)
) {
@@ -609,12 +611,12 @@ class ControllerSynologyDriver extends CsiBaseDriver {
let waitTimeBetweenChecks = settleSeconds * 1000;
await sleep(waitTimeBetweenChecks);
await GeneralUtils.sleep(waitTimeBetweenChecks);
lun_uuid = await httpClient.GetLunUUIDByName(iscsiName);
while (currentCheck <= settleMaxRetries && lun_uuid) {
currentCheck++;
await sleep(waitTimeBetweenChecks);
await GeneralUtils.sleep(waitTimeBetweenChecks);
lun_uuid = await httpClient.GetLunUUIDByName(iscsiName);
}
+2 -1
View File
@@ -1,6 +1,7 @@
const _ = require("lodash");
const { ControllerZfsBaseDriver } = require("../controller-zfs");
const { GrpcError, grpc } = require("../../utils/grpc");
const GeneralUtils = require("../../utils/general");
const LocalCliExecClient = require("./exec").LocalCliClient;
const registry = require("../../utils/registry");
const { Zetabyte } = require("../../utils/zfs");
@@ -95,7 +96,7 @@ class ControllerZfsLocalDriver extends ControllerZfsBaseDriver {
case "filesystem":
return ["zfs"];
case "volume":
return ["btrfs", "ext3", "ext4", "ext4dev", "xfs"];
return GeneralUtils.default_supported_block_filesystems();
}
}
+30 -13
View File
@@ -1,7 +1,7 @@
const _ = require("lodash");
const { CsiBaseDriver } = require("../index");
const { GrpcError, grpc } = require("../../utils/grpc");
const sleep = require("../../utils/general").sleep;
const GeneralUtils = require("../../utils/general");
const getLargestNumber = require("../../utils/general").getLargestNumber;
const Handlebars = require("handlebars");
@@ -201,9 +201,9 @@ class ControllerZfsBaseDriver extends CsiBaseDriver {
const driverZfsResourceType = this.getDriverZfsResourceType();
switch (driverZfsResourceType) {
case "filesystem":
return ["nfs", "cifs"];
return GeneralUtils.default_supported_file_filesystems();
case "volume":
return ["btrfs", "ext3", "ext4", "ext4dev", "xfs"];
return GeneralUtils.default_supported_block_filesystems();
}
}
@@ -620,6 +620,7 @@ class ControllerZfsBaseDriver extends CsiBaseDriver {
let snapshotParentDatasetName = this.getDetachedSnapshotParentDatasetName();
let zvolBlocksize = this.options.zfs.zvolBlocksize || "16K";
let name = call.request.name;
let volume_id = await driver.getVolumeIdFromName(name);
let volume_content_source = call.request.volume_content_source;
if (!datasetParentName) {
@@ -710,7 +711,7 @@ class ControllerZfsBaseDriver extends CsiBaseDriver {
* NOTE: avoid the urge to templatize this given the name length limits for zvols
* ie: namespace-name may quite easily exceed 58 chars
*/
const datasetName = datasetParentName + "/" + name;
const datasetName = datasetParentName + "/" + volume_id;
// ensure volumes with the same name being requested a 2nd time but with a different size fails
try {
@@ -862,7 +863,7 @@ class ControllerZfsBaseDriver extends CsiBaseDriver {
volume_content_source_snapshot_id +
"@" +
VOLUME_SOURCE_CLONE_SNAPSHOT_PREFIX +
name;
volume_id;
}
driver.ctx.logger.debug("full snapshot name: %s", fullSnapshotName);
@@ -909,6 +910,12 @@ class ControllerZfsBaseDriver extends CsiBaseDriver {
});
} else {
try {
// remove readonly/undesired props
let cloneProperties = volumeProperties;
delete cloneProperties["aclmode"];
delete cloneProperties["aclinherit"];
delete cloneProperties["acltype"];
delete cloneProperties["casesensitivity"];
response = await zb.zfs.clone(fullSnapshotName, datasetName, {
properties: volumeProperties,
});
@@ -971,7 +978,7 @@ class ControllerZfsBaseDriver extends CsiBaseDriver {
volume_content_source_volume_id +
"@" +
VOLUME_SOURCE_CLONE_SNAPSHOT_PREFIX +
name;
volume_id;
driver.ctx.logger.debug("full snapshot name: %s", fullSnapshotName);
@@ -1024,9 +1031,15 @@ class ControllerZfsBaseDriver extends CsiBaseDriver {
} else {
// create clone
// zfs origin property contains parent info, ie: pool0/k8s/test/PVC-111@clone-test
// remove readonly/undesired props
let cloneProperties = volumeProperties;
delete cloneProperties["aclmode"];
delete cloneProperties["aclinherit"];
delete cloneProperties["acltype"];
delete cloneProperties["casesensitivity"];
try {
response = await zb.zfs.clone(fullSnapshotName, datasetName, {
properties: volumeProperties,
properties: cloneProperties,
});
} catch (err) {
if (err.toString().includes("dataset does not exist")) {
@@ -1128,8 +1141,13 @@ class ControllerZfsBaseDriver extends CsiBaseDriver {
// TODO: this is unsfafe approach, make it better
// probably could see if ^-.*\s and split and then shell escape
if (this.options.zfs.datasetPermissionsAcls) {
let aclBinary = _.get(
driver.options,
"zfs.datasetPermissionsAclsBinary",
"setfacl"
);
for (const acl of this.options.zfs.datasetPermissionsAcls) {
command = execClient.buildCommand("setfacl", [
command = execClient.buildCommand(aclBinary, [
acl,
properties.mountpoint.value,
]);
@@ -1147,7 +1165,6 @@ class ControllerZfsBaseDriver extends CsiBaseDriver {
}
}
}
break;
case "volume":
// set properties
@@ -1191,7 +1208,7 @@ class ControllerZfsBaseDriver extends CsiBaseDriver {
const res = {
volume: {
volume_id: name,
volume_id,
//capacity_bytes: capacity_bytes, // kubernetes currently pukes if capacity is returned as 0
capacity_bytes:
this.options.zfs.datasetEnableQuotas ||
@@ -1315,7 +1332,7 @@ class ControllerZfsBaseDriver extends CsiBaseDriver {
if (current_try > max_tries) {
throw err;
} else {
await sleep(sleep_time);
await GeneralUtils.sleep(sleep_time);
}
} else {
throw err;
@@ -2190,7 +2207,7 @@ class ControllerZfsBaseDriver extends CsiBaseDriver {
});
// let things settle down
//await sleep(3000);
//await GneralUtils.sleep(3000);
} else {
try {
await zb.zfs.snapshot(fullSnapshotName, {
@@ -2198,7 +2215,7 @@ class ControllerZfsBaseDriver extends CsiBaseDriver {
});
// let things settle down
//await sleep(3000);
//await GeneralUtils.sleep(3000);
} catch (err) {
if (err.toString().includes("dataset does not exist")) {
throw new GrpcError(
+31 -14
View File
@@ -4,9 +4,8 @@ const { CsiBaseDriver } = require("../index");
const HttpClient = require("./http").Client;
const TrueNASApiClient = require("./http/api").Api;
const { Zetabyte } = require("../../utils/zfs");
const getLargestNumber = require("../../utils/general").getLargestNumber;
const registry = require("../../utils/registry");
const sleep = require("../../utils/general").sleep;
const GeneralUtils = require("../../utils/general");
const Handlebars = require("handlebars");
const uuidv4 = require("uuid").v4;
@@ -1565,7 +1564,7 @@ class FreeNASApiDriver extends CsiBaseDriver {
targetId,
retries
);
await sleep(retryWait);
await GeneralUtils.sleep(retryWait);
response = await httpClient.delete(endpoint);
}
@@ -1958,7 +1957,7 @@ class FreeNASApiDriver extends CsiBaseDriver {
if (capability.access_type == "mount") {
if (
capability.mount.fs_type &&
!["btrfs", "ext3", "ext4", "ext4dev", "xfs"].includes(
!GeneralUtils.default_supported_block_filesystems().includes(
capability.mount.fs_type
)
) {
@@ -2066,6 +2065,7 @@ class FreeNASApiDriver extends CsiBaseDriver {
let snapshotParentDatasetName = this.getDetachedSnapshotParentDatasetName();
let zvolBlocksize = this.options.zfs.zvolBlocksize || "16K";
let name = call.request.name;
let volume_id = await driver.getVolumeIdFromName(name);
let volume_content_source = call.request.volume_content_source;
let minimum_volume_size = await driver.getMinimumVolumeSize();
let default_required_bytes = 1073741824;
@@ -2171,7 +2171,7 @@ class FreeNASApiDriver extends CsiBaseDriver {
* NOTE: avoid the urge to templatize this given the name length limits for zvols
* ie: namespace-name may quite easily exceed 58 chars
*/
const datasetName = datasetParentName + "/" + name;
const datasetName = datasetParentName + "/" + volume_id;
// ensure volumes with the same name being requested a 2nd time but with a different size fails
try {
@@ -2326,7 +2326,7 @@ class FreeNASApiDriver extends CsiBaseDriver {
volume_content_source_snapshot_id +
"@" +
VOLUME_SOURCE_CLONE_SNAPSHOT_PREFIX +
name;
volume_id;
}
driver.ctx.logger.debug("full snapshot name: %s", fullSnapshotName);
@@ -2378,7 +2378,7 @@ class FreeNASApiDriver extends CsiBaseDriver {
) {
job = await httpApiClient.CoreGetJobs({ id: job_id });
job = job[0];
await sleep(3000);
await GeneralUtils.sleep(3000);
}
job.error = job.error || "";
@@ -2488,7 +2488,7 @@ class FreeNASApiDriver extends CsiBaseDriver {
volume_content_source_volume_id +
"@" +
VOLUME_SOURCE_CLONE_SNAPSHOT_PREFIX +
name;
volume_id;
driver.ctx.logger.debug("full snapshot name: %s", fullSnapshotName);
@@ -2538,7 +2538,7 @@ class FreeNASApiDriver extends CsiBaseDriver {
) {
job = await httpApiClient.CoreGetJobs({ id: job_id });
job = job[0];
await sleep(3000);
await GeneralUtils.sleep(3000);
}
job.error = job.error || "";
@@ -2626,6 +2626,9 @@ class FreeNASApiDriver extends CsiBaseDriver {
volsize: driverZfsResourceType == "volume" ? capacity_bytes : undefined,
sparse: driverZfsResourceType == "volume" ? sparse : undefined,
create_ancestors: true,
share_type: driver.getDriverShareType().includes("smb")
? "SMB"
: "GENERIC",
user_properties: httpApiClient.getPropertiesKeyValueArray(
httpApiClient.getUserProperties(volumeProperties)
),
@@ -2721,7 +2724,18 @@ class FreeNASApiDriver extends CsiBaseDriver {
}
if (setPerms) {
await httpApiClient.FilesystemSetperm(perms);
response = await httpApiClient.FilesystemSetperm(perms);
await httpApiClient.CoreWaitForJob(response, 30);
// SetPerm does not alter ownership with extended ACLs
// run this in addition just for good measure
if (perms.uid || perms.gid) {
response = await httpApiClient.FilesystemChown({
path: perms.path,
uid: perms.uid,
gid: perms.gid,
});
await httpApiClient.CoreWaitForJob(response, 30);
}
}
// set acls
@@ -2777,7 +2791,7 @@ class FreeNASApiDriver extends CsiBaseDriver {
const res = {
volume: {
volume_id: name,
volume_id,
//capacity_bytes: capacity_bytes, // kubernetes currently pukes if capacity is returned as 0
capacity_bytes:
this.options.zfs.datasetEnableQuotas ||
@@ -3649,7 +3663,10 @@ class FreeNASApiDriver extends CsiBaseDriver {
// so we must be cognizant and use the highest possible value here
// note that whatever value is returned here can/will essentially impact the refquota
// value of a derived volume
size_bytes = getLargestNumber(row.referenced, row.logicalreferenced);
size_bytes = GeneralUtils.getLargestNumber(
row.referenced,
row.logicalreferenced
);
} else {
// get the size of the parent volume
size_bytes = row.volsize;
@@ -3930,7 +3947,7 @@ class FreeNASApiDriver extends CsiBaseDriver {
while (!job || !["SUCCESS", "ABORTED", "FAILED"].includes(job.state)) {
job = await httpApiClient.CoreGetJobs({ id: job_id });
job = job[0];
await sleep(3000);
await GeneralUtils.sleep(3000);
}
job.error = job.error || "";
@@ -4045,7 +4062,7 @@ class FreeNASApiDriver extends CsiBaseDriver {
// so we must be cognizant and use the highest possible value here
// note that whatever value is returned here can/will essentially impact the refquota
// value of a derived volume
size_bytes = getLargestNumber(
size_bytes = GeneralUtils.getLargestNumber(
properties.referenced.rawvalue,
properties.logicalreferenced.rawvalue
// TODO: perhaps include minimum volume size here?
+44 -6
View File
@@ -681,7 +681,13 @@ class Api {
throw new Error(JSON.stringify(response.body));
}
async CoreWaitForJob(job_id, timeout = 0) {
/**
*
* @param {*} job_id
* @param {*} timeout in seconds
* @returns
*/
async CoreWaitForJob(job_id, timeout = 0, check_interval = 3000) {
if (!job_id) {
throw new Error("invalid job_id");
}
@@ -692,16 +698,17 @@ class Api {
let job;
// wait for job to finish
while (!job || !["SUCCESS", "ABORTED", "FAILED"].includes(job.state)) {
do {
if (job) {
await sleep(check_interval);
}
job = await this.CoreGetJobs({ id: job_id });
job = job[0];
await sleep(3000);
currentTime = Date.now() / 1000;
if (timeout > 0 && currentTime > startTime + timeout) {
throw new Error("timeout waiting for job to complete");
}
}
} while (!["SUCCESS", "ABORTED", "FAILED"].includes(job.state));
return job;
}
@@ -754,7 +761,38 @@ class Api {
response = await httpClient.post(endpoint, data);
if (response.statusCode == 200) {
return;
return response.body;
}
throw new Error(JSON.stringify(response.body));
}
/**
*
* @param {*} data
*/
async FilesystemChown(data) {
/*
{
"path": "string",
"uid": 0,
"gid": 0,
"options": {
"recursive": false,
"traverse": false
}
}
*/
const httpClient = await this.getHttpClient(false);
let response;
let endpoint;
endpoint = `/filesystem/chown`;
response = await httpClient.post(endpoint, data);
if (response.statusCode == 200) {
return response.body;
}
throw new Error(JSON.stringify(response.body));
+3
View File
@@ -122,6 +122,9 @@ class Client {
_.set(options, prop, "redacted");
}
delete options.httpAgent;
delete options.httpsAgent;
this.logger.debug("FREENAS HTTP REQUEST: " + stringify(options));
this.logger.debug("FREENAS HTTP ERROR: " + error);
this.logger.debug("FREENAS HTTP STATUS: " + response.statusCode);
+12
View File
@@ -4,6 +4,7 @@ const { GrpcError, grpc } = require("../../utils/grpc");
const registry = require("../../utils/registry");
const SshClient = require("../../utils/ssh").SshClient;
const HttpClient = require("./http").Client;
const TrueNASApiClient = require("./http/api").Api;
const { Zetabyte, ZfsSshProcessManager } = require("../../utils/zfs");
const { sleep, stringify } = require("../../utils/general");
@@ -112,6 +113,13 @@ class FreeNASSshDriver extends ControllerZfsBaseDriver {
);
}
async getTrueNASHttpApiClient() {
return registry.getAsync(`${__REGISTRY_NS__}:api_client`, async () => {
const httpClient = await this.getHttpClient();
return new TrueNASApiClient(httpClient, this.ctx.cache);
});
}
getDriverShareType() {
switch (this.options.driver) {
case "freenas-nfs":
@@ -1716,6 +1724,7 @@ class FreeNASSshDriver extends ControllerZfsBaseDriver {
async setFilesystemMode(path, mode) {
const httpClient = await this.getHttpClient();
const apiVersion = httpClient.getApiVersion();
const httpApiClient = await this.getTrueNASHttpApiClient();
switch (apiVersion) {
case 1:
@@ -1747,6 +1756,7 @@ class FreeNASSshDriver extends ControllerZfsBaseDriver {
response = await httpClient.post(endpoint, perms);
if (response.statusCode == 200) {
await httpApiClient.CoreWaitForJob(response.body, 30);
return;
}
@@ -1764,6 +1774,7 @@ class FreeNASSshDriver extends ControllerZfsBaseDriver {
async setFilesystemOwnership(path, user = false, group = false) {
const httpClient = await this.getHttpClient();
const apiVersion = httpClient.getApiVersion();
const httpApiClient = await this.getTrueNASHttpApiClient();
if (user === false || typeof user == "undefined" || user === null) {
user = "";
@@ -1832,6 +1843,7 @@ class FreeNASSshDriver extends ControllerZfsBaseDriver {
response = await httpClient.post(endpoint, perms);
if (response.statusCode == 200) {
await httpApiClient.CoreWaitForJob(response.body, 30);
return;
}
+1855 -852
View File
File diff suppressed because it is too large Load Diff