Add iSCSI targets/LUNs through Pacemaker clusters
Support pcs for zfs-generic to configure iSCSI targets and LUNs. It uses the targetcli implementation since it's the default pcs resources and also to keep compatibility with the rest of the code. closes #462
This commit is contained in:
parent
93e0446fa3
commit
9252b4e779
|
|
@ -44,6 +44,7 @@ zfs:
|
||||||
|
|
||||||
iscsi:
|
iscsi:
|
||||||
shareStrategy: "targetCli"
|
shareStrategy: "targetCli"
|
||||||
|
#shareStrategy: "pcs"
|
||||||
|
|
||||||
# https://kifarunix.com/how-to-install-and-configure-iscsi-storage-server-on-ubuntu-18-04/
|
# https://kifarunix.com/how-to-install-and-configure-iscsi-storage-server-on-ubuntu-18-04/
|
||||||
# https://kifarunix.com/how-install-and-configure-iscsi-storage-server-on-centos-7/
|
# https://kifarunix.com/how-install-and-configure-iscsi-storage-server-on-centos-7/
|
||||||
|
|
@ -75,6 +76,17 @@ iscsi:
|
||||||
attributes:
|
attributes:
|
||||||
# set to 1 to enable Thin Provisioning Unmap
|
# set to 1 to enable Thin Provisioning Unmap
|
||||||
emulate_tpu: 0
|
emulate_tpu: 0
|
||||||
|
|
||||||
|
shareStrategyPcs:
|
||||||
|
#sudoEnabled: true
|
||||||
|
pcs_group: "group-nas"
|
||||||
|
basename: "iqn.2003-01.org.linux-iscsi.ubuntu-19.x8664"
|
||||||
|
auth:
|
||||||
|
enabled: 0
|
||||||
|
# CHAP – incoming only, mutual not supported by the Pacemaker resource agent
|
||||||
|
incoming_username: "foo"
|
||||||
|
incoming_password: "bar"
|
||||||
|
|
||||||
targetPortal: "server[:port]"
|
targetPortal: "server[:port]"
|
||||||
# for multipath
|
# for multipath
|
||||||
targetPortals: [] # [ "server[:port]", "server[:port]", ... ]
|
targetPortals: [] # [ "server[:port]", "server[:port]", ... ]
|
||||||
|
|
|
||||||
|
|
@ -298,8 +298,70 @@ create /backstores/block/${assetName}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
default:
|
case "pcs":
|
||||||
|
basename = this.options.iscsi.shareStrategyPcs.basename;
|
||||||
|
pcs_group = this.options.iscsi.shareStrategyPcs.pcs_group;
|
||||||
|
|
||||||
|
let groupText = `group ${pcs_group}`;
|
||||||
|
let createTargetText = [
|
||||||
|
`resource create --future target-${assetName} iSCSITarget`,
|
||||||
|
'implementation="lio-t"',
|
||||||
|
`iqn="${basename}:${assetName}"`
|
||||||
|
];
|
||||||
|
|
||||||
|
if (this.options.iscsi.shareStrategyPcs.auth.enabled) {
|
||||||
|
createTargetText.push(`incoming_username="${this.options.iscsi.shareStrategyPcs.auth.incoming_username}"`);
|
||||||
|
createTargetText.push(`incoming_password="${this.options.iscsi.shareStrategyPcs.auth.incoming_password}"`);
|
||||||
|
}
|
||||||
|
|
||||||
|
createTargetText.push(groupText);
|
||||||
|
|
||||||
|
await GeneralUtils.retry(
|
||||||
|
3,
|
||||||
|
2000,
|
||||||
|
async () => {
|
||||||
|
await this.pcsCommand(createTargetText);
|
||||||
|
},
|
||||||
|
{
|
||||||
|
retryCondition: (err) => {
|
||||||
|
if (err.stdout && err.stdout.includes("Timed Out")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
let createLunText = [
|
||||||
|
`resource create --future lun-${assetName} iSCSILogicalUnit`,
|
||||||
|
'implementation="lio-t"',
|
||||||
|
`target_iqn="${basename}:${assetName}" lun="1"`,
|
||||||
|
`path="/dev/${extentDiskName}"`,
|
||||||
|
groupText
|
||||||
|
];
|
||||||
|
|
||||||
|
await GeneralUtils.retry(
|
||||||
|
3,
|
||||||
|
2000,
|
||||||
|
async () => {
|
||||||
|
await this.pcsCommand(createLunText);
|
||||||
|
},
|
||||||
|
{
|
||||||
|
retryCondition: (err) => {
|
||||||
|
if (err.stdout && err.stdout.includes("Timed Out")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new GrpcError(
|
||||||
|
grpc.status.FAILED_PRECONDITION,
|
||||||
|
`invalid configuration: unknown shareStrategy ${this.options.iscsi.shareStrategy}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// iqn = target
|
// iqn = target
|
||||||
|
|
@ -690,8 +752,54 @@ delete ${assetName}
|
||||||
);
|
);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
default:
|
case "pcs":
|
||||||
|
let deleteLunText = [
|
||||||
|
`resource delete lun-${assetName}`
|
||||||
|
];
|
||||||
|
|
||||||
|
await GeneralUtils.retry(
|
||||||
|
3,
|
||||||
|
2000,
|
||||||
|
async () => {
|
||||||
|
await this.pcsCommand(deleteLunText);
|
||||||
|
},
|
||||||
|
{
|
||||||
|
retryCondition: (err) => {
|
||||||
|
if (err.stdout && err.stdout.includes("Timed Out")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
let deleteTargetText = [
|
||||||
|
`resource delete target-${assetName}`
|
||||||
|
];
|
||||||
|
|
||||||
|
await GeneralUtils.retry(
|
||||||
|
3,
|
||||||
|
2000,
|
||||||
|
async () => {
|
||||||
|
await this.pcsCommand(deleteTargetText);
|
||||||
|
},
|
||||||
|
{
|
||||||
|
retryCondition: (err) => {
|
||||||
|
if (err.stdout && err.stdout.includes("Timed Out")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new GrpcError(
|
||||||
|
grpc.status.FAILED_PRECONDITION,
|
||||||
|
`invalid configuration: unknown shareStrategy ${this.options.iscsi.shareStrategy}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -841,6 +949,9 @@ save_config filename=${this.options.nvmeof.shareStrategySpdkCli.configPath}
|
||||||
case "targetCli":
|
case "targetCli":
|
||||||
// nothing required, just need to rescan on the node
|
// nothing required, just need to rescan on the node
|
||||||
break;
|
break;
|
||||||
|
case "pcs":
|
||||||
|
// nothing required, just need to rescan on the node
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -851,6 +962,56 @@ save_config filename=${this.options.nvmeof.shareStrategySpdkCli.configPath}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async pcsCommand(commandLines) {
|
||||||
|
const execClient = this.getExecClient();
|
||||||
|
const driver = this;
|
||||||
|
|
||||||
|
let command = "sh";
|
||||||
|
let args = ["-c"];
|
||||||
|
|
||||||
|
let cliArgs = ["pcs"];
|
||||||
|
if (
|
||||||
|
_.get(this.options, "iscsi.shareStrategyPcs.sudoEnabled", false)
|
||||||
|
) {
|
||||||
|
cliArgs.unshift("sudo");
|
||||||
|
}
|
||||||
|
|
||||||
|
let cliCommand = [];
|
||||||
|
cliCommand.push(cliArgs.join(" "));
|
||||||
|
cliCommand.push(commandLines.join(" "));
|
||||||
|
args.push("'" + cliCommand.join(" ") + "'");
|
||||||
|
|
||||||
|
let logCommandTmp = command + " " + args.join(" ");
|
||||||
|
let logCommand = "";
|
||||||
|
|
||||||
|
logCommandTmp.split(" ").forEach((term) => {
|
||||||
|
logCommand += " ";
|
||||||
|
|
||||||
|
if (term.startsWith("incoming_password=")) {
|
||||||
|
logCommand += "incoming_password=<redacted>";
|
||||||
|
} else {
|
||||||
|
logCommand += term;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
driver.ctx.logger.verbose("Pcs command:" + logCommand);
|
||||||
|
|
||||||
|
let options = {
|
||||||
|
pty: true,
|
||||||
|
};
|
||||||
|
let response = await execClient.exec(
|
||||||
|
execClient.buildCommand(command, args),
|
||||||
|
options
|
||||||
|
);
|
||||||
|
driver.ctx.logger.verbose(
|
||||||
|
"Pcs response: " + JSON.stringify(response)
|
||||||
|
);
|
||||||
|
if (response.code != 0) {
|
||||||
|
throw response;
|
||||||
|
}
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
async targetCliCommand(data) {
|
async targetCliCommand(data) {
|
||||||
const execClient = this.getExecClient();
|
const execClient = this.getExecClient();
|
||||||
const driver = this;
|
const driver = this;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue