support templatized/deterministic iqns

This commit is contained in:
Travis Glenn Hansen 2020-09-06 11:14:59 -06:00
parent ee3cb6af14
commit f5ba51c9b8
6 changed files with 84 additions and 37 deletions

View File

@ -20,7 +20,7 @@ ENV PATH=/usr/local/lib/nodejs/bin:$PATH
# node service requirements # node service requirements
RUN apt-get update && \ RUN apt-get update && \
apt-get install -y xfsprogs fatresize dosfstools open-iscsi lsscsi sg3-utils multipath-tools scsitools nfs-common sudo && \ apt-get install -y xfsprogs fatresize dosfstools open-iscsi lsscsi sg3-utils multipath-tools scsitools nfs-common cifs-utils sudo && \
rm -rf /var/lib/apt/lists/* rm -rf /var/lib/apt/lists/*
# controller requirements # controller requirements

View File

@ -52,6 +52,11 @@ iscsi:
targetPortals: [] targetPortals: []
# leave empty to omit usage of -I with iscsiadm # leave empty to omit usage of -I with iscsiadm
interface: interface:
# MUST ensure uniqueness
# full iqn limit is 223 bytes, plan accordingly
# default is "{{ name }}"
#nameTemplate: "{{ parameters.[csi.storage.k8s.io/pvc/namespace] }}-{{ parameters.[csi.storage.k8s.io/pvc/name] }}"
namePrefix: csi- namePrefix: csi-
nameSuffix: "-clustera" nameSuffix: "-clustera"
# add as many as needed # add as many as needed

View File

@ -72,5 +72,10 @@ iscsi:
targetPortal: "server address" targetPortal: "server address"
targetPortals: [] targetPortals: []
interface: "" interface: ""
# MUST ensure uniqueness
# full iqn limit is 223 bytes, plan accordingly
# default is "{{ name }}"
#nameTemplate: "{{ parameters.[csi.storage.k8s.io/pvc/namespace] }}-{{ parameters.[csi.storage.k8s.io/pvc/name] }}"
namePrefix: namePrefix:
nameSuffix: nameSuffix:

View File

@ -1,6 +1,8 @@
const { ControllerZfsSshBaseDriver } = require("../controller-zfs-ssh"); const { ControllerZfsSshBaseDriver } = require("../controller-zfs-ssh");
const { GrpcError, grpc } = require("../../utils/grpc"); const { GrpcError, grpc } = require("../../utils/grpc");
const Handlebars = require("handlebars");
class ControllerZfsGenericDriver extends ControllerZfsSshBaseDriver { class ControllerZfsGenericDriver extends ControllerZfsSshBaseDriver {
/** /**
* 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
@ -44,7 +46,7 @@ class ControllerZfsGenericDriver extends ControllerZfsSshBaseDriver {
) { ) {
await zb.zfs.set(datasetName, { await zb.zfs.set(datasetName, {
[key]: this.options.nfs.shareStrategySetDatasetProperties [key]: this.options.nfs.shareStrategySetDatasetProperties
.properties[key] .properties[key],
}); });
} }
} }
@ -61,13 +63,23 @@ class ControllerZfsGenericDriver extends ControllerZfsSshBaseDriver {
volume_context = { volume_context = {
node_attach_driver: "nfs", node_attach_driver: "nfs",
server: this.options.nfs.shareHost, server: this.options.nfs.shareHost,
share: properties.mountpoint.value share: properties.mountpoint.value,
}; };
return volume_context; return volume_context;
case "zfs-generic-iscsi": case "zfs-generic-iscsi":
let basename; let basename;
let iscsiName = zb.helpers.extractLeafName(datasetName); let iscsiName;
if (this.options.iscsi.nameTemplate) {
iscsiName = Handlebars.compile(this.options.iscsi.nameTemplate)({
name: call.request.name,
parameters: call.request.parameters,
});
} else {
iscsiName = zb.helpers.extractLeafName(datasetName);
}
if (this.options.iscsi.namePrefix) { if (this.options.iscsi.namePrefix) {
iscsiName = this.options.iscsi.namePrefix + iscsiName; iscsiName = this.options.iscsi.namePrefix + iscsiName;
} }
@ -153,7 +165,7 @@ create /backstores/block/${iscsiName}
portals: this.options.iscsi.targetPortals.join(","), portals: this.options.iscsi.targetPortals.join(","),
interface: this.options.iscsi.interface, interface: this.options.iscsi.interface,
iqn: iqn, iqn: iqn,
lun: 0 lun: 0,
}; };
return volume_context; return volume_context;

View File

@ -291,6 +291,10 @@ class ControllerZfsSshBaseDriver 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 + "/" + name;
let capacity_bytes = let capacity_bytes =
call.request.capacity_range.required_bytes || call.request.capacity_range.required_bytes ||

View File

@ -2,6 +2,8 @@ const { ControllerZfsSshBaseDriver } = require("../controller-zfs-ssh");
const { GrpcError, grpc } = require("../../utils/grpc"); const { GrpcError, grpc } = require("../../utils/grpc");
const HttpClient = require("./http").Client; const HttpClient = require("./http").Client;
const Handlebars = require("handlebars");
// freenas properties // freenas properties
const FREENAS_NFS_SHARE_PROPERTY_NAME = "democratic-csi:freenas_nfs_share_id"; const FREENAS_NFS_SHARE_PROPERTY_NAME = "democratic-csi:freenas_nfs_share_id";
const FREENAS_ISCSI_TARGET_ID_PROPERTY_NAME = const FREENAS_ISCSI_TARGET_ID_PROPERTY_NAME =
@ -19,8 +21,10 @@ class FreeNASDriver extends ControllerZfsSshBaseDriver {
getDriverZfsResourceType() { getDriverZfsResourceType() {
switch (this.options.driver) { switch (this.options.driver) {
case "freenas-nfs": case "freenas-nfs":
case "truenas-nfs":
return "filesystem"; return "filesystem";
case "freenas-iscsi": case "freenas-iscsi":
case "truenas-iscsi":
return "volume"; return "volume";
default: default:
throw new Error("unknown driver: " + this.ctx.args.driver); throw new Error("unknown driver: " + this.ctx.args.driver);
@ -36,8 +40,10 @@ class FreeNASDriver extends ControllerZfsSshBaseDriver {
getDriverShareType() { getDriverShareType() {
switch (this.options.driver) { switch (this.options.driver) {
case "freenas-nfs": case "freenas-nfs":
case "truenas-nfs":
return "nfs"; return "nfs";
case "freenas-iscsi": case "freenas-iscsi":
case "truenas-iscsi":
return "iscsi"; return "iscsi";
default: default:
throw new Error("unknown driver: " + this.ctx.args.driver); throw new Error("unknown driver: " + this.ctx.args.driver);
@ -74,7 +80,7 @@ class FreeNASDriver extends ControllerZfsSshBaseDriver {
if (response.body.length < 1) { if (response.body.length < 1) {
break; break;
} }
response.body.some(i => { response.body.some((i) => {
let isMatch = true; let isMatch = true;
for (let property in match) { for (let property in match) {
if (match[property] != i[property]) { if (match[property] != i[property]) {
@ -124,7 +130,7 @@ class FreeNASDriver extends ControllerZfsSshBaseDriver {
case "nfs": case "nfs":
properties = await zb.zfs.get(datasetName, [ properties = await zb.zfs.get(datasetName, [
"mountpoint", "mountpoint",
FREENAS_NFS_SHARE_PROPERTY_NAME FREENAS_NFS_SHARE_PROPERTY_NAME,
]); ]);
properties = properties[datasetName]; properties = properties[datasetName];
this.ctx.logger.debug("zfs props data: %j", properties); this.ctx.logger.debug("zfs props data: %j", properties);
@ -154,7 +160,7 @@ class FreeNASDriver extends ControllerZfsSshBaseDriver {
nfs_maproot_group: this.options.nfs.shareMaprootGroup, nfs_maproot_group: this.options.nfs.shareMaprootGroup,
nfs_mapall_user: this.options.nfs.shareMapallUser, nfs_mapall_user: this.options.nfs.shareMapallUser,
nfs_mapall_group: this.options.nfs.shareMapallGroup, nfs_mapall_group: this.options.nfs.shareMapallGroup,
nfs_security: [] nfs_security: [],
}; };
break; break;
case 2: case 2:
@ -170,7 +176,7 @@ class FreeNASDriver extends ControllerZfsSshBaseDriver {
maproot_group: this.options.nfs.shareMaprootGroup, maproot_group: this.options.nfs.shareMaprootGroup,
mapall_user: this.options.nfs.shareMapallUser, mapall_user: this.options.nfs.shareMapallUser,
mapall_group: this.options.nfs.shareMapallGroup, mapall_group: this.options.nfs.shareMapallGroup,
security: [] security: [],
}; };
break; break;
} }
@ -184,7 +190,7 @@ class FreeNASDriver extends ControllerZfsSshBaseDriver {
if ([200, 201].includes(response.statusCode)) { if ([200, 201].includes(response.statusCode)) {
//set zfs property //set zfs property
await zb.zfs.set(datasetName, { await zb.zfs.set(datasetName, {
[FREENAS_NFS_SHARE_PROPERTY_NAME]: response.body.id [FREENAS_NFS_SHARE_PROPERTY_NAME]: response.body.id,
}); });
} else { } else {
/** /**
@ -209,7 +215,7 @@ class FreeNASDriver extends ControllerZfsSshBaseDriver {
let volume_context = { let volume_context = {
node_attach_driver: "nfs", node_attach_driver: "nfs",
server: this.options.nfs.shareHost, server: this.options.nfs.shareHost,
share: properties.mountpoint.value share: properties.mountpoint.value,
}; };
return volume_context; return volume_context;
@ -223,7 +229,7 @@ class FreeNASDriver extends ControllerZfsSshBaseDriver {
let volume_context = { let volume_context = {
node_attach_driver: "nfs", node_attach_driver: "nfs",
server: this.options.nfs.shareHost, server: this.options.nfs.shareHost,
share: properties.mountpoint.value share: properties.mountpoint.value,
}; };
return volume_context; return volume_context;
} }
@ -232,13 +238,23 @@ class FreeNASDriver extends ControllerZfsSshBaseDriver {
properties = await zb.zfs.get(datasetName, [ properties = await zb.zfs.get(datasetName, [
FREENAS_ISCSI_TARGET_ID_PROPERTY_NAME, FREENAS_ISCSI_TARGET_ID_PROPERTY_NAME,
FREENAS_ISCSI_EXTENT_ID_PROPERTY_NAME, FREENAS_ISCSI_EXTENT_ID_PROPERTY_NAME,
FREENAS_ISCSI_TARGETTOEXTENT_ID_PROPERTY_NAME FREENAS_ISCSI_TARGETTOEXTENT_ID_PROPERTY_NAME,
]); ]);
properties = properties[datasetName]; properties = properties[datasetName];
this.ctx.logger.debug("zfs props data: %j", properties); this.ctx.logger.debug("zfs props data: %j", properties);
let basename; let basename;
let iscsiName = zb.helpers.extractLeafName(datasetName); let iscsiName;
if (this.options.iscsi.nameTemplate) {
iscsiName = Handlebars.compile(this.options.iscsi.nameTemplate)({
name: call.request.name,
parameters: call.request.parameters,
});
} else {
iscsiName = zb.helpers.extractLeafName(datasetName);
}
if (this.options.iscsi.namePrefix) { if (this.options.iscsi.namePrefix) {
iscsiName = this.options.iscsi.namePrefix + iscsiName; iscsiName = this.options.iscsi.namePrefix + iscsiName;
} }
@ -247,6 +263,9 @@ class FreeNASDriver extends ControllerZfsSshBaseDriver {
iscsiName += this.options.iscsi.nameSuffix; iscsiName += this.options.iscsi.nameSuffix;
} }
// According to RFC3270, 'Each iSCSI node, whether an initiator or target, MUST have an iSCSI name. Initiators and targets MUST support the receipt of iSCSI names of up to the maximum length of 223 bytes.'
// https://kb.netapp.com/Advice_and_Troubleshooting/Miscellaneous/What_is_the_maximum_length_of_a_iSCSI_iqn_name
// https://tools.ietf.org/html/rfc3720
iscsiName = iscsiName.toLowerCase(); iscsiName = iscsiName.toLowerCase();
let extentDiskName = "zvol/" + datasetName; let extentDiskName = "zvol/" + datasetName;
@ -323,7 +342,7 @@ class FreeNASDriver extends ControllerZfsSshBaseDriver {
// create target // create target
let target = { let target = {
iscsi_target_name: iscsiName, iscsi_target_name: iscsiName,
iscsi_target_alias: "" iscsi_target_alias: "",
}; };
response = await httpClient.post("/services/iscsi/target", target); response = await httpClient.post("/services/iscsi/target", target);
@ -340,7 +359,7 @@ class FreeNASDriver extends ControllerZfsSshBaseDriver {
target = await this.findResourceByProperties( target = await this.findResourceByProperties(
"/services/iscsi/target", "/services/iscsi/target",
{ {
iscsi_target_name: iscsiName iscsi_target_name: iscsiName,
} }
); );
} else { } else {
@ -366,7 +385,7 @@ class FreeNASDriver extends ControllerZfsSshBaseDriver {
// set target.id on zvol // set target.id on zvol
await zb.zfs.set(datasetName, { await zb.zfs.set(datasetName, {
[FREENAS_ISCSI_TARGET_ID_PROPERTY_NAME]: target.id [FREENAS_ISCSI_TARGET_ID_PROPERTY_NAME]: target.id,
}); });
// create targetgroup(s) // create targetgroup(s)
@ -382,7 +401,7 @@ class FreeNASDriver extends ControllerZfsSshBaseDriver {
targetGroupConfig.targetGroupPortalGroup, targetGroupConfig.targetGroupPortalGroup,
iscsi_target_initiatorgroup: iscsi_target_initiatorgroup:
targetGroupConfig.targetGroupInitiatorGroup, targetGroupConfig.targetGroupInitiatorGroup,
iscsi_target_initialdigest: "Auto" iscsi_target_initialdigest: "Auto",
}; };
response = await httpClient.post( response = await httpClient.post(
"/services/iscsi/targetgroup", "/services/iscsi/targetgroup",
@ -421,7 +440,7 @@ class FreeNASDriver extends ControllerZfsSshBaseDriver {
iscsi_target_portalgroup: iscsi_target_portalgroup:
targetGroupConfig.targetGroupPortalGroup, targetGroupConfig.targetGroupPortalGroup,
iscsi_target_initiatorgroup: iscsi_target_initiatorgroup:
targetGroupConfig.targetGroupInitiatorGroup targetGroupConfig.targetGroupInitiatorGroup,
} }
); );
} else { } else {
@ -463,7 +482,7 @@ class FreeNASDriver extends ControllerZfsSshBaseDriver {
iscsi_target_extent_rpm: isNaN(Number(extentRpm)) iscsi_target_extent_rpm: isNaN(Number(extentRpm))
? "SSD" ? "SSD"
: Number(extentRpm), : Number(extentRpm),
iscsi_target_extent_ro: false iscsi_target_extent_ro: false,
}; };
response = await httpClient.post("/services/iscsi/extent", extent); response = await httpClient.post("/services/iscsi/extent", extent);
@ -501,14 +520,14 @@ class FreeNASDriver extends ControllerZfsSshBaseDriver {
this.ctx.logger.verbose("FreeNAS ISCSI EXTENT: %j", extent); this.ctx.logger.verbose("FreeNAS ISCSI EXTENT: %j", extent);
await zb.zfs.set(datasetName, { await zb.zfs.set(datasetName, {
[FREENAS_ISCSI_EXTENT_ID_PROPERTY_NAME]: extent.id [FREENAS_ISCSI_EXTENT_ID_PROPERTY_NAME]: extent.id,
}); });
// create targettoextent // create targettoextent
let targetToExtent = { let targetToExtent = {
iscsi_target: target.id, iscsi_target: target.id,
iscsi_extent: extent.id, iscsi_extent: extent.id,
iscsi_lunid: 0 iscsi_lunid: 0,
}; };
response = await httpClient.post( response = await httpClient.post(
"/services/iscsi/targettoextent", "/services/iscsi/targettoextent",
@ -535,7 +554,7 @@ class FreeNASDriver extends ControllerZfsSshBaseDriver {
{ {
iscsi_target: target.id, iscsi_target: target.id,
iscsi_extent: extent.id, iscsi_extent: extent.id,
iscsi_lunid: 0 iscsi_lunid: 0,
} }
); );
} else { } else {
@ -562,7 +581,8 @@ class FreeNASDriver extends ControllerZfsSshBaseDriver {
); );
await zb.zfs.set(datasetName, { await zb.zfs.set(datasetName, {
[FREENAS_ISCSI_TARGETTOEXTENT_ID_PROPERTY_NAME]: targetToExtent.id [FREENAS_ISCSI_TARGETTOEXTENT_ID_PROPERTY_NAME]:
targetToExtent.id,
}); });
break; break;
@ -596,14 +616,14 @@ class FreeNASDriver extends ControllerZfsSshBaseDriver {
? targetGroupConfig.targetGroupAuthType ? targetGroupConfig.targetGroupAuthType
.toUpperCase() .toUpperCase()
.replace(" ", "_") .replace(" ", "_")
: "NONE" : "NONE",
}); });
} }
let target = { let target = {
name: iscsiName, name: iscsiName,
alias: null, // cannot send "" error: handler error - driver: FreeNASDriver method: CreateVolume error: {"name":"GrpcError","code":2,"message":"received error creating iscsi target - code: 422 body: {\"iscsi_target_create.alias\":[{\"message\":\"Alias already exists\",\"errno\":22}]}"} alias: null, // cannot send "" error: handler error - driver: FreeNASDriver method: CreateVolume error: {"name":"GrpcError","code":2,"message":"received error creating iscsi target - code: 422 body: {\"iscsi_target_create.alias\":[{\"message\":\"Alias already exists\",\"errno\":22}]}"}
mode: "ISCSI", mode: "ISCSI",
groups: targetGroups groups: targetGroups,
}; };
response = await httpClient.post("/iscsi/target", target); response = await httpClient.post("/iscsi/target", target);
@ -618,7 +638,7 @@ class FreeNASDriver extends ControllerZfsSshBaseDriver {
) )
) { ) {
target = await this.findResourceByProperties("/iscsi/target", { target = await this.findResourceByProperties("/iscsi/target", {
name: iscsiName name: iscsiName,
}); });
} else { } else {
throw new GrpcError( throw new GrpcError(
@ -643,7 +663,7 @@ class FreeNASDriver extends ControllerZfsSshBaseDriver {
// set target.id on zvol // set target.id on zvol
await zb.zfs.set(datasetName, { await zb.zfs.set(datasetName, {
[FREENAS_ISCSI_TARGET_ID_PROPERTY_NAME]: target.id [FREENAS_ISCSI_TARGET_ID_PROPERTY_NAME]: target.id,
}); });
let extent = { let extent = {
@ -658,7 +678,7 @@ class FreeNASDriver extends ControllerZfsSshBaseDriver {
blocksize: Number(extentBlocksize), blocksize: Number(extentBlocksize),
pblocksize: extentDisablePhysicalBlocksize, pblocksize: extentDisablePhysicalBlocksize,
rpm: "" + extentRpm, // should be a string rpm: "" + extentRpm, // should be a string
ro: false ro: false,
}; };
response = await httpClient.post("/iscsi/extent", extent); response = await httpClient.post("/iscsi/extent", extent);
@ -673,7 +693,7 @@ class FreeNASDriver extends ControllerZfsSshBaseDriver {
) )
) { ) {
extent = await this.findResourceByProperties("/iscsi/extent", { extent = await this.findResourceByProperties("/iscsi/extent", {
name: iscsiName name: iscsiName,
}); });
} else { } else {
throw new GrpcError( throw new GrpcError(
@ -696,14 +716,14 @@ class FreeNASDriver extends ControllerZfsSshBaseDriver {
this.ctx.logger.verbose("FreeNAS ISCSI EXTENT: %j", extent); this.ctx.logger.verbose("FreeNAS ISCSI EXTENT: %j", extent);
await zb.zfs.set(datasetName, { await zb.zfs.set(datasetName, {
[FREENAS_ISCSI_EXTENT_ID_PROPERTY_NAME]: extent.id [FREENAS_ISCSI_EXTENT_ID_PROPERTY_NAME]: extent.id,
}); });
// create targettoextent // create targettoextent
let targetToExtent = { let targetToExtent = {
target: target.id, target: target.id,
extent: extent.id, extent: extent.id,
lunid: 0 lunid: 0,
}; };
response = await httpClient.post( response = await httpClient.post(
"/iscsi/targetextent", "/iscsi/targetextent",
@ -729,7 +749,7 @@ class FreeNASDriver extends ControllerZfsSshBaseDriver {
{ {
target: target.id, target: target.id,
extent: extent.id, extent: extent.id,
lunid: 0 lunid: 0,
} }
); );
} else { } else {
@ -756,7 +776,8 @@ class FreeNASDriver extends ControllerZfsSshBaseDriver {
); );
await zb.zfs.set(datasetName, { await zb.zfs.set(datasetName, {
[FREENAS_ISCSI_TARGETTOEXTENT_ID_PROPERTY_NAME]: targetToExtent.id [FREENAS_ISCSI_TARGETTOEXTENT_ID_PROPERTY_NAME]:
targetToExtent.id,
}); });
break; break;
@ -793,7 +814,7 @@ class FreeNASDriver extends ControllerZfsSshBaseDriver {
//chapDiscoveryEnabled: this.options.iscsi.chapDiscoveryEnabled, //chapDiscoveryEnabled: this.options.iscsi.chapDiscoveryEnabled,
//chapSessionEnabled: this.options.iscsi.chapSessionEnabled, //chapSessionEnabled: this.options.iscsi.chapSessionEnabled,
iqn: iqn, iqn: iqn,
lun: 0 lun: 0,
}; };
return volume_context; return volume_context;
@ -819,7 +840,7 @@ class FreeNASDriver extends ControllerZfsSshBaseDriver {
case "nfs": case "nfs":
try { try {
properties = await zb.zfs.get(datasetName, [ properties = await zb.zfs.get(datasetName, [
FREENAS_NFS_SHARE_PROPERTY_NAME FREENAS_NFS_SHARE_PROPERTY_NAME,
]); ]);
} catch (err) { } catch (err) {
if (err.toString().includes("dataset does not exist")) { if (err.toString().includes("dataset does not exist")) {
@ -884,7 +905,7 @@ class FreeNASDriver extends ControllerZfsSshBaseDriver {
properties = await zb.zfs.get(datasetName, [ properties = await zb.zfs.get(datasetName, [
FREENAS_ISCSI_TARGET_ID_PROPERTY_NAME, FREENAS_ISCSI_TARGET_ID_PROPERTY_NAME,
FREENAS_ISCSI_EXTENT_ID_PROPERTY_NAME, FREENAS_ISCSI_EXTENT_ID_PROPERTY_NAME,
FREENAS_ISCSI_TARGETTOEXTENT_ID_PROPERTY_NAME FREENAS_ISCSI_TARGETTOEXTENT_ID_PROPERTY_NAME,
]); ]);
} catch (err) { } catch (err) {
if (err.toString().includes("dataset does not exist")) { if (err.toString().includes("dataset does not exist")) {