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:
|
||||
shareStrategy: "targetCli"
|
||||
#shareStrategy: "pcs"
|
||||
|
||||
# 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/
|
||||
|
|
@ -75,6 +76,17 @@ iscsi:
|
|||
attributes:
|
||||
# set to 1 to enable Thin Provisioning Unmap
|
||||
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]"
|
||||
# for multipath
|
||||
targetPortals: [] # [ "server[:port]", "server[:port]", ... ]
|
||||
|
|
|
|||
|
|
@ -298,8 +298,70 @@ create /backstores/block/${assetName}
|
|||
}
|
||||
);
|
||||
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;
|
||||
|
||||
default:
|
||||
throw new GrpcError(
|
||||
grpc.status.FAILED_PRECONDITION,
|
||||
`invalid configuration: unknown shareStrategy ${this.options.iscsi.shareStrategy}`
|
||||
);
|
||||
}
|
||||
|
||||
// iqn = target
|
||||
|
|
@ -690,8 +752,54 @@ delete ${assetName}
|
|||
);
|
||||
|
||||
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;
|
||||
|
||||
default:
|
||||
throw new GrpcError(
|
||||
grpc.status.FAILED_PRECONDITION,
|
||||
`invalid configuration: unknown shareStrategy ${this.options.iscsi.shareStrategy}`
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
@ -841,6 +949,9 @@ save_config filename=${this.options.nvmeof.shareStrategySpdkCli.configPath}
|
|||
case "targetCli":
|
||||
// nothing required, just need to rescan on the node
|
||||
break;
|
||||
case "pcs":
|
||||
// nothing required, just need to rescan on the node
|
||||
break;
|
||||
default:
|
||||
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) {
|
||||
const execClient = this.getExecClient();
|
||||
const driver = this;
|
||||
|
|
|
|||
Loading…
Reference in New Issue