/*
* This file is part of DRBD Management Console by LINBIT HA-Solutions GmbH
* written by Rasto Levrinc.
*
* Copyright (C) 2009, LINBIT HA-Solutions GmbH.
* Copyright (C) 2011-2012, Rastislav Levrinc.
*
* DRBD Management Console is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as published
* by the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* DRBD Management Console is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with drbd; see the file COPYING. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package lcmc.crm.service;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.regex.Matcher;
import lcmc.configs.DistResource;
import lcmc.common.domain.Application;
import lcmc.crm.domain.CrmXml;
import lcmc.host.domain.Host;
import lcmc.crm.domain.HostLocation;
import lcmc.logger.Logger;
import lcmc.logger.LoggerFactory;
import lcmc.common.domain.util.Tools;
import lcmc.cluster.service.ssh.ExecCommandConfig;
import lcmc.cluster.service.ssh.SshOutput;
/**
* This class provides cib commands. There are commands that use cibadmin and
* crm_resource commands to manipulate the cib, crm, etc.
*/
public final class CRM {
private static final Logger LOG = LoggerFactory.getLogger(CRM.class);
/** Output of the ptest. */
private static volatile String ptestOutput = null;
private static final ReadWriteLock M_PTEST_LOCK = new ReentrantReadWriteLock();
private static final Lock M_PTEST_READLOCK = M_PTEST_LOCK.readLock();
private static final Lock M_PTEST_WRITELOCK = M_PTEST_LOCK.writeLock();
/** Delimiter that delimits the ptest and test cib part. */
public static final String PTEST_END_DELIM = "--- PTEST END ---";
/** Location of lcmc-test.xml file. */
public static final String LCMC_TEST_FILE = "/tmp/lcmc-test-" + UUID.randomUUID() + ".xml";
public static String getCibCommand(final String command, final String objType, final String xml) {
final StringBuilder cmd = new StringBuilder(300);
cmd.append(DistResource.SUDO + "/usr/sbin/cibadmin -o ");
cmd.append(objType);
cmd.append(' ');
cmd.append(command);
cmd.append(" -X ");
cmd.append(xml);
return cmd.toString();
}
/** Executes specified command on the host. */
private static SshOutput execCommand(final Host host, final String command, final Application.RunMode runMode) {
M_PTEST_WRITELOCK.lock();
try {
ptestOutput = null;
} finally {
M_PTEST_WRITELOCK.unlock();
}
if (Application.isTest(runMode)) {
final String testCmd =
"if [ ! -e " + LCMC_TEST_FILE + " ]; "
+ "then " + DistResource.SUDO + "/usr/sbin/cibadmin -Ql > "
+ LCMC_TEST_FILE + ";fi;"
+ "export CIB_file=" + LCMC_TEST_FILE + ';';
return host.captureCommand(new ExecCommandConfig().command(testCmd + command)
.silentCommand()
.silentOutput());
} else {
LOG.debug1("execCommand: crm command: " + command);
return host.captureCommandProgressIndicator(Tools.getString("CIB.ExecutingCommand"),
new ExecCommandConfig().command(command));
}
}
/** Executes the ptest command and returns results. */
public static String getPtest(final Host host) {
M_PTEST_READLOCK.lock();
try {
if (ptestOutput != null) {
return ptestOutput;
}
} finally {
M_PTEST_READLOCK.unlock();
}
final String command = "export PROG=/usr/sbin/crm_simulate;"
+ "if [ -e /usr/sbin/ptest ];"
+ " then export PROG=/usr/sbin/ptest; "
+ "fi;"
+ DistResource.SUDO + "$PROG -VVVVV -S -x "
+ LCMC_TEST_FILE
+ " 2>&1;echo '"
+ PTEST_END_DELIM
+ "';cat " + LCMC_TEST_FILE + " 2>/dev/null;"
+ "mv -f " + LCMC_TEST_FILE + "{,.last} 2>/dev/null";
final SshOutput output = host.captureCommand(new ExecCommandConfig().command(command)
.silentCommand()
.silentOutput());
M_PTEST_WRITELOCK.lock();
try {
final String po = output.getOutput();
if (ptestOutput == null) {
ptestOutput = po;
}
return po;
} finally {
M_PTEST_WRITELOCK.unlock();
}
}
private static String getPrimitiveXML(final Host host,
final String resId,
final Map<String, String> pacemakerResAttrs,
final Map<String, String> pacemakerResArgs,
final Map<String, String> pacemakerMetaArgs,
String instanceAttrId,
final Map<String, String> nvpairIdsHash,
final Map<String, Map<String, String>> pacemakerOps,
String operationsId,
final String metaAttrsRefId,
final String operationsRefId,
final Boolean stonith,
final Application.RunMode runMode) {
final StringBuilder xml = new StringBuilder(360);
if (resId != null) {
if (instanceAttrId == null) {
instanceAttrId = resId + "-instance_attributes";
}
final StringBuilder attrsString = new StringBuilder(100);
for (final Map.Entry<String, String> pacemakerResEntry : pacemakerResAttrs.entrySet()) {
final String value = pacemakerResEntry.getValue();
attrsString.append(pacemakerResEntry.getKey());
attrsString.append("=\"");
attrsString.append(value);
attrsString.append("\" ");
}
xml.append("<primitive ");
xml.append(attrsString);
xml.append('>');
/* instance_attributes */
if (!pacemakerResArgs.isEmpty()) {
xml.append("<instance_attributes id=\"");
xml.append(instanceAttrId);
xml.append("\">");
if (Tools.versionBeforePacemaker(host)) {
/* 2.1.4 */
xml.append("<attributes>");
}
for (final String paramName : pacemakerResArgs.keySet()) {
final String value = pacemakerResArgs.get(paramName);
String nvpairId = null;
if (nvpairIdsHash != null) {
nvpairId = nvpairIdsHash.get(paramName);
}
final String newParamName;
if ((stonith != null && stonith) && CrmXml.STONITH_PRIORITY_INSTANCE_ATTR.equals(paramName)) {
newParamName = "priority";
} else {
newParamName = paramName;
}
if (nvpairId == null) {
nvpairId = "nvpair-" + resId + '-' + newParamName;
}
xml.append("<nvpair id=\"");
xml.append(nvpairId);
xml.append("\" name=\"");
xml.append(newParamName);
xml.append("\" value=\"");
xml.append(value);
xml.append("\"/>");
}
if (Tools.versionBeforePacemaker(host)) {
/* 2.1.4 */
xml.append("</attributes>");
}
xml.append("</instance_attributes>");
}
}
/* operations */
if (operationsRefId != null) {
xml.append("<operations id-ref=\"");
xml.append(operationsRefId);
xml.append("\"/>");
} else if (pacemakerOps != null && !pacemakerOps.isEmpty()) {
//TODO: not "else if" but update the referred service.
if (Tools.versionBeforePacemaker(host)) {
xml.append("<operations>");
} else {
/* 2.1.4 does not have the id. */
if (operationsId == null) {
operationsId = resId + "-operations";
}
xml.append("<operations id=\"");
xml.append(operationsId);
xml.append("\">");
}
for (final Map.Entry<String, Map<String, String>> pacemakerOpsEntry : pacemakerOps.entrySet()) {
final Map<String, String> opHash = pacemakerOpsEntry.getValue();
xml.append("<op");
boolean checkLevel = false;
for (final Map.Entry<String, String> opEntry : opHash.entrySet()) {
if (CrmXml.PARAM_OCF_CHECK_LEVEL.equals(opEntry.getKey())) {
checkLevel = true;
continue;
}
final String value = opEntry.getValue();
xml.append(' ');
xml.append(opEntry.getKey());
xml.append("=\"");
xml.append(value);
xml.append('"');
}
if (checkLevel) {
xml.append('>');
final String iaId = resId + "-monitor-instance_attributes";
xml.append("<instance_attributes id=\"");
xml.append(iaId);
xml.append("\"><nvpair id=\"");
xml.append(iaId);
xml.append('-');
xml.append(CrmXml.PARAM_OCF_CHECK_LEVEL);
xml.append("\" name=\"");
xml.append(CrmXml.PARAM_OCF_CHECK_LEVEL);
xml.append("\" value=\"");
xml.append(opHash.get(CrmXml.PARAM_OCF_CHECK_LEVEL));
xml.append("\"/></instance_attributes></op>");
} else {
xml.append("/>");
}
}
xml.append("</operations>");
}
/* meta_attributes */
if (resId != null) {
xml.append(getMetaAttributes(host, resId, pacemakerMetaArgs, metaAttrsRefId));
xml.append("</primitive>");
}
return xml.toString();
}
/** Adds or updates resource in the CIB. */
public static boolean setParameters(final Host host,
final String command, /* -R or -C */
final String resId,
final String cloneId,
final boolean master,
final Map<String, String> cloneMetaArgs,
final Map<String, String> groupMetaArgs,
final String groupId,
final Map<String, String> pacemakerResAttrs,
final Map<String, String> pacemakerResArgs,
final Map<String, String> pacemakerMetaArgs,
final String instanceAttrId,
final Map<String, String> nvpairIdsHash,
final Map<String, Map<String, String>> pacemakerOps,
final String operationsId,
final String metaAttrsRefId,
final String cloneMetaAttrsRefId,
final String groupMetaAttrsRefId,
final String operationsRefId,
final Boolean stonith,
final Application.RunMode runMode) {
final StringBuilder xml = new StringBuilder(360);
xml.append('\'');
if (cloneId != null) {
if (master) {
if (Tools.versionBeforePacemaker(host)) {
xml.append("<master_slave id=\"");
} else {
xml.append("<master id=\"");
}
} else {
xml.append("<clone id=\"");
}
xml.append(cloneId);
xml.append("\">");
/* mater/slave meta_attributes */
//if (!cloneMetaArgs.isEmpty()) {
xml.append(getMetaAttributes(host, cloneId, cloneMetaArgs, cloneMetaAttrsRefId));
//}
}
if (groupId != null) {
if (resId != null) {
xml.append("<group id=\"");
xml.append(groupId);
xml.append("\">");
}
xml.append(getMetaAttributes(host, groupId, groupMetaArgs, groupMetaAttrsRefId));
}
xml.append(getPrimitiveXML(host,
resId,
pacemakerResAttrs,
pacemakerResArgs,
pacemakerMetaArgs,
instanceAttrId,
nvpairIdsHash,
pacemakerOps,
operationsId,
metaAttrsRefId,
operationsRefId,
stonith,
runMode));
if (groupId != null && resId != null) {
xml.append("</group>");
}
if (cloneId != null) {
if (master) {
if (Tools.versionBeforePacemaker(host)) {
xml.append("</master_slave>");
} else {
xml.append("</master>");
}
} else {
xml.append("</clone>");
}
}
xml.append('\'');
final SshOutput ret = execCommand(host, getCibCommand(command, "resources", xml.toString()), runMode);
return ret.getExitCode() == 0;
}
/** Replaces the whole group. */
public static boolean replaceGroup(final boolean createGroup,
final Host host,
final String cloneId,
final boolean master,
final Map<String, String> cloneMetaArgs,
final String cloneMetaAttrsRefId,
final Iterable<String> resourceIds,
final Map<String, String> groupMetaArgs,
final String groupId,
final Map<String, Map<String, String>> pacemakerResAttrs,
final Map<String, Map<String, String>> pacemakerResArgs,
final Map<String, Map<String, String>> pacemakerMetaArgs,
final Map<String, String> instanceAttrId,
final Map<String, Map<String, String>> nvpairIdsHash,
final Map<String, Map<String, Map<String, String>>> pacemakerOps,
final Map<String, String> operationsId,
final Map<String, String> metaAttrsRefId,
final String groupMetaAttrsRefId,
final Map<String, String> operationsRefId,
final Map<String, Boolean> stonith,
final Application.RunMode runMode) {
final StringBuilder xml = new StringBuilder(720);
xml.append('\'');
if (cloneId != null) {
if (master) {
if (Tools.versionBeforePacemaker(host)) {
xml.append("<master_slave id=\"");
} else {
xml.append("<master id=\"");
}
} else {
xml.append("<clone id=\"");
}
xml.append(cloneId);
xml.append("\">");
/* mater/slave meta_attributes */
xml.append(getMetaAttributes(host, cloneId, cloneMetaArgs, cloneMetaAttrsRefId));
}
xml.append("<group id=\"");
xml.append(groupId);
xml.append("\">");
xml.append(getMetaAttributes(host, groupId, groupMetaArgs, groupMetaAttrsRefId));
for (final String resId : resourceIds) {
xml.append(getPrimitiveXML(host,
resId,
pacemakerResAttrs.get(resId),
pacemakerResArgs.get(resId),
pacemakerMetaArgs.get(resId),
instanceAttrId.get(resId),
nvpairIdsHash.get(resId),
pacemakerOps.get(resId),
operationsId.get(resId),
metaAttrsRefId.get(resId),
operationsRefId.get(resId),
stonith.get(resId),
runMode));
}
xml.append("</group>");
if (cloneId != null) {
if (master) {
if (Tools.versionBeforePacemaker(host)) {
xml.append("</master_slave>");
} else {
xml.append("</master>");
}
} else {
xml.append("</clone>");
}
}
xml.append('\'');
final String cibadminOpt;
if (createGroup) {
cibadminOpt = "-C";
} else {
cibadminOpt = "-R";
}
final SshOutput ret = execCommand(host, getCibCommand(cibadminOpt, "resources", xml.toString()), runMode);
return ret.getExitCode() == 0;
}
/**
* Sets colocation and order constraint between service with resId
* and parents. If colAttrsList or ordAttrsList are null, there is only
* colocation or order but not both defined.
*/
public static void setOrderAndColocation(final Host host,
final String resId,
final String[] parents,
final List<Map<String, String>> colAttrsList,
final List<Map<String, String>> ordAttrsList,
final Application.RunMode runMode) {
for (int i = 0; i < parents.length; i++) {
if (colAttrsList.get(i) != null) {
addColocation(host, null, resId, parents[i], colAttrsList.get(i), runMode);
}
if (ordAttrsList.get(i) != null) {
addOrder(host, null, parents[i], resId, ordAttrsList.get(i), runMode);
}
}
}
private static String getOneRscSet(
final String rscSetId, final CrmXml.RscSet rscSet, Map<String, String> attrs) {
final StringBuilder xml = new StringBuilder(120);
xml.append("<resource_set id=\"");
xml.append(rscSetId);
if (attrs == null) {
attrs = new LinkedHashMap<String, String>();
final String colocationRole = rscSet.getColocationRole();
if (colocationRole != null) {
attrs.put("role", colocationRole);
}
final String orderAction = rscSet.getOrderAction();
if (orderAction != null) {
attrs.put("action", orderAction);
}
final String sequential = rscSet.getSequential();
if (sequential != null) {
attrs.put("sequential", sequential);
}
final String requireAll = rscSet.getRequireAll();
if (requireAll != null
&& !requireAll.equals(CrmXml.REQUIRE_ALL_TRUE_VALUE.getValueForConfig())) {
attrs.put(CrmXml.REQUIRE_ALL_ATTR, requireAll);
}
}
for (final Map.Entry<String, String> attrsEntry : attrs.entrySet()) {
final String value = attrsEntry.getValue();
if (value != null && value.isEmpty()) {
continue;
}
xml.append("\" ").append(attrsEntry.getKey()).append("=\"");
xml.append(value);
}
xml.append("\">");
for (final String rscId : rscSet.getRscIds()) {
xml.append("<resource_ref id=\"");
xml.append(rscId);
xml.append("\"/>");
}
xml.append("</resource_set>");
return xml.toString();
}
public static boolean setRscSet(final Host host,
final String colId,
final boolean createCol,
final String ordId,
final boolean createOrd,
final Map< CrmXml.RscSet, Map<String, String>> rscSetsColAttrs,
final Map<CrmXml.RscSet, Map<String, String>> rscSetsOrdAttrs,
final Map<String, String> attrs,
final Application.RunMode runMode) {
if (colId != null) {
if (rscSetsColAttrs.isEmpty()) {
return removeColocation(host, colId, runMode);
}
final String cibadminOpt;
if (createCol) {
cibadminOpt = "-C";
} else {
cibadminOpt = "-R";
}
final boolean ret = setRscSetConstraint(host, "rsc_colocation",
colId,
rscSetsColAttrs,
attrs,
cibadminOpt,
runMode);
if (!ret) {
return false;
}
}
if (ordId != null) {
if (rscSetsOrdAttrs.isEmpty()) {
return removeOrder(host, ordId, runMode);
}
final String cibadminOpt;
if (createOrd) {
cibadminOpt = "-C";
} else {
cibadminOpt = "-R";
}
return setRscSetConstraint(host,
"rsc_order",
ordId,
rscSetsOrdAttrs,
attrs,
cibadminOpt,
runMode);
}
return true;
}
private static boolean setRscSetConstraint(final Host host,
final String tag,
final String constraintId,
final Map<CrmXml.RscSet, Map<String, String>> rscSetsAttrs,
final Map<String, String> attrs,
final String cibadminOpt,
final Application.RunMode runMode) {
final StringBuilder xml = new StringBuilder(360);
xml.append("'<");
xml.append(tag);
xml.append(" id=\"");
xml.append(constraintId);
if (attrs != null) {
for (final Map.Entry<String, String> attrsEntry : attrs.entrySet()) {
final String value = attrsEntry.getValue();
if (value == null || value.isEmpty()) {
continue;
}
xml.append("\" ").append(attrsEntry.getKey()).append("=\"");
xml.append(value);
}
}
xml.append("\">");
int rsId = 0;
for (final Map.Entry<CrmXml.RscSet, Map<String, String>> rscSetsEntry : rscSetsAttrs.entrySet()) {
if (rscSetsEntry.getKey() != null) {
xml.append(getOneRscSet(constraintId + '-' + rsId, rscSetsEntry.getKey(), rscSetsEntry.getValue()));
rsId++;
}
}
xml.append("</");
xml.append(tag);
xml.append(">'");
final String command = getCibCommand(cibadminOpt, "constraints", xml.toString());
final SshOutput ret = execCommand(host, command, runMode);
return ret.getExitCode() == 0;
}
private static String getLocationXML(final String resId,
final String onHost,
final String attribute,
final String score,
final String scoreAttribute,
final String op,
final String role,
final String locationId) {
final StringBuilder xml = new StringBuilder(360);
xml.append("'<rsc_location id=\"");
xml.append(locationId);
xml.append("\" rsc=\"");
xml.append(resId);
if (op == null || ("eq".equals(op) && !"pingd".equals(attribute) && role == null)) {
/* eq */
if (onHost != null) {
xml.append("\" node=\"");
xml.append(onHost);
}
if (score != null) {
xml.append("\" score=\"");
xml.append(score);
}
xml.append("\"/>'");
} else {
/* ne, etc. */
xml.append("\"><rule id=\"");
xml.append(locationId);
xml.append("-rule\"");
if (score != null) {
xml.append(" score=\"");
xml.append(score);
xml.append('"');
}
if (role != null) {
xml.append(" role=\"");
xml.append(role);
xml.append('"');
}
if (scoreAttribute != null) {
xml.append(" score-attribute=\"");
xml.append(scoreAttribute);
xml.append('"');
}
if (attribute != null) {
xml.append("><expression attribute=\"");
xml.append(attribute);
xml.append("\" id=\"");
xml.append(locationId);
xml.append("-expression\" operation=\"");
xml.append(op);
if (onHost != null) {
xml.append("\" value=\"");
xml.append(onHost);
}
xml.append("\"/");
}
xml.append("></rule></rsc_location>'");
}
return xml.toString();
}
public static boolean setLocation(final Host host,
final String resId,
final String onHost,
final HostLocation hostLocation,
String locationId,
final Application.RunMode runMode) {
String command = "-U";
if (locationId == null) {
locationId = "loc_" + resId + '_' + onHost;
command = "-C";
} else if ("migration".equals(locationId)) {
locationId = "cli-standby-" + resId;
command = "-C";
} else if ("remigration".equals(locationId)) {
locationId = "cli-standby-" + resId;
command = "-U";
}
String score = null;
String op = null;
String role = null;
if (hostLocation != null) {
score = hostLocation.getScore();
op = hostLocation.getOperation();
role = hostLocation.getRole();
}
final String xml = getLocationXML(resId, onHost, "#uname", score, null, op, role, locationId);
final SshOutput ret = execCommand(host, getCibCommand(command, "constraints", xml), runMode);
return ret.getExitCode() == 0;
}
public static boolean setPingLocation(final Host host,
final String resId,
final String ruleType,
String locationId,
final Application.RunMode runMode) {
String op = null;
String value = null;
String score = null;
String scoreAttribute = null;
String idPart = "";
if ("defined".equals(ruleType)) {
scoreAttribute = "pingd";
op = "defined";
idPart = "prefer";
} else if ("eq0".equals(ruleType)) {
score = "-INFINITY";
op = "eq";
value = "0";
idPart = "exclude";
}
String command = "-U";
if (locationId == null) {
locationId = "loc_" + resId + "-ping-" + idPart;
command = "-C";
}
final String attribute = "pingd";
final String xml = getLocationXML(resId, value, attribute, score, scoreAttribute, op, null, locationId);
final SshOutput ret = execCommand(host, getCibCommand(command, "constraints", xml), runMode);
return ret.getExitCode() == 0;
}
public static boolean removeLocation(final Host host,
final String locationId,
final String resId,
final Application.RunMode runMode) {
final String xml = getLocationXML(resId, null, null, null, null, null, null, locationId);
final String command = getCibCommand("-D", "constraints", xml);
final SshOutput ret = execCommand(host, command, runMode);
return ret.getExitCode() == 0;
}
/**
* Removes a resource from crm. If heartbeat id is null and group id is
* not, the whole group will be removed.
*/
public static boolean removeResource(final Host host,
final String resId,
final String groupId,
final String cloneId,
final boolean master,
final Application.RunMode runMode) {
final StringBuilder xml = new StringBuilder(360);
xml.append('\'');
if (cloneId != null) {
if (master) {
if (Tools.versionBeforePacemaker(host)) {
xml.append("<master_slave id=\"");
} else {
xml.append("<master id=\"");
}
} else {
xml.append("<clone id=\"");
}
xml.append(cloneId);
xml.append("\">");
}
if (groupId != null) {
/* when removing the last resource in a group, remove the
* whole group. */
xml.append("<group id=\"");
xml.append(groupId);
xml.append("\">");
}
if (resId != null) {
xml.append("<primitive id=\"");
xml.append(resId);
xml.append("\"></primitive>");
}
if (groupId != null) {
xml.append("</group>");
}
if (cloneId != null) {
if (master) {
if (Tools.versionBeforePacemaker(host)) {
xml.append("</master_slave>");
} else {
xml.append("</master>");
}
} else {
xml.append("</clone>");
}
}
xml.append('\'');
final String command = getCibCommand("-D", "resources", xml.toString());
final SshOutput ret = execCommand(host, command, runMode);
return ret.getExitCode() == 0;
}
public static boolean cleanupResource(final Host host,
final String resId,
final Host[] clusterHosts,
final Application.RunMode runMode) {
if (Application.isTest(runMode)) {
return true;
}
/* make cleanup on all cluster hosts. */
final Map<String, String> replaceHash = new HashMap<String, String>();
replaceHash.put("@ID@", resId);
for (final Host clusterHost : clusterHosts) {
replaceHash.put("@HOST@", clusterHost.getName());
final String command = host.getDistCommand("CRM.cleanupResource", replaceHash);
execCommand(host, command, runMode);
}
return true; /* always return true */
}
public static boolean startResource(final Host host, final String resId, final Application.RunMode runMode) {
String cmd = "CRM.startResource";
if (Tools.versionBeforePacemaker(host)) {
cmd = "CRM.2.1.4.startResource";
}
final Map<String, String> replaceHash = new HashMap<String, String>();
replaceHash.put("@ID@", resId);
final String command = host.getDistCommand(cmd, replaceHash);
final SshOutput ret = execCommand(host, command, runMode);
return ret.getExitCode() == 0;
}
private static String getMetaAttributes(final Host host,
final String resId,
final Map<String, String> attrs,
final String metaAttrsRefId) {
final StringBuilder xml = new StringBuilder(360);
String idPostfix = "-meta_attributes";
if (Tools.versionBeforePacemaker(host)) {
idPostfix = "-meta-options";
}
if (metaAttrsRefId == null) {
xml.append("<meta_attributes id=\"");
xml.append(resId);
xml.append(idPostfix);
xml.append("\">");
if (Tools.versionBeforePacemaker(host)) {
/* 2.1.4 */
xml.append("<attributes>");
}
for (final Map.Entry<String, String> attrsEntry : attrs.entrySet()) {
xml.append("<nvpair id=\"");
xml.append(resId);
xml.append(idPostfix);
xml.append('-');
xml.append(attrsEntry.getKey());
xml.append("\" name=\"");
xml.append(attrsEntry.getKey());
xml.append("\" value=\"");
xml.append(attrsEntry.getValue());
xml.append("\"/>");
}
if (Tools.versionBeforePacemaker(host)) {
/* 2.1.4 */
xml.append("</attributes>");
}
xml.append("</meta_attributes>");
} else {
xml.append("<meta_attributes id-ref=\"");
xml.append(metaAttrsRefId);
xml.append("\"/>");
}
return xml.toString();
}
public static boolean setManaged(final Host host,
final String resId,
final boolean isManaged,
final Application.RunMode runMode) {
final String label;
if (isManaged) {
label = ".isManagedOn";
} else {
label = ".isManagedOff";
}
String cmd = "CRM" + label;
if (Tools.versionBeforePacemaker(host)) {
cmd = "CRM.2.1.4" + label;
}
final Map<String, String> replaceHash = new HashMap<String, String>();
replaceHash.put("@ID@", resId);
final String command = host.getDistCommand(cmd, replaceHash);
final SshOutput ret = execCommand(host, command, runMode);
return ret.getExitCode() == 0;
}
public static boolean stopResource(final Host host, final String resId, final Application.RunMode runMode) {
if (resId == null) {
return false;
}
String cmd = "CRM.stopResource";
if (Tools.versionBeforePacemaker(host)) {
cmd = "CRM.2.1.4.stopResource";
}
final Map<String, String> replaceHash = new HashMap<String, String>();
replaceHash.put("@ID@", resId);
final String command = host.getDistCommand(cmd, replaceHash);
final SshOutput ret = execCommand(host, command, runMode);
return ret.getExitCode() == 0;
}
public static boolean migrateResource(final Host host,
final String resId,
final String onHost,
final Application.RunMode runMode) {
final Map<String, String> replaceHash = new HashMap<String, String>();
replaceHash.put("@ID@", resId);
replaceHash.put("@HOST@", onHost);
final String command = host.getDistCommand("CRM.migrateResource", replaceHash);
final SshOutput ret = execCommand(host, command, runMode);
return ret.getExitCode() == 0;
}
public static boolean migrateFromResource(final Host host, final String resId, final Application.RunMode runMode) {
final Map<String, String> replaceHash = new HashMap<String, String>();
replaceHash.put("@ID@", resId);
final String command = host.getDistCommand("CRM.migrateFromResource", replaceHash);
final SshOutput ret = execCommand(host, command, runMode);
return ret.getExitCode() == 0;
}
public static boolean forceMigrateResource(final Host host,
final String resId,
final String onHost,
final Application.RunMode runMode) {
final Map<String, String> replaceHash = new HashMap<String, String>();
replaceHash.put("@ID@", resId);
replaceHash.put("@HOST@", onHost);
final String command = host.getDistCommand("CRM.forceMigrateResource", replaceHash);
final SshOutput ret = execCommand(host, command, runMode);
return ret.getExitCode() == 0;
}
public static boolean unmigrateResource(final Host host, final String resId, final Application.RunMode runMode) {
final Map<String, String> replaceHash = new HashMap<String, String>();
replaceHash.put("@ID@", resId);
final String command = host.getDistCommand("CRM.unmigrateResource", replaceHash);
final SshOutput ret = execCommand(host, command, runMode);
return ret.getExitCode() == 0;
}
public static boolean setGlobalParameters(final Host host,
final Map<String, String> args,
final Map<String, String> rdiMetaArgs,
String rscDefaultsId,
final Application.RunMode runMode) {
final StringBuilder xml = new StringBuilder(360);
xml.append("'<crm_config><cluster_property_set id=\"cib-bootstrap-options\">");
if (Tools.versionBeforePacemaker(host)) {
/* 2.1.4 */
xml.append("<attributes>");
}
for (final Map.Entry<String, String> argsEntry : args.entrySet()) {
final String id = "cib-bootstrap-options-" + argsEntry.getKey();
xml.append("<nvpair id=\"");
xml.append(id);
xml.append("\" name=\"");
xml.append(argsEntry.getKey());
xml.append("\" value=\"");
xml.append(argsEntry.getValue());
xml.append("\"/>");
}
if (Tools.versionBeforePacemaker(host)) {
/* 2.1.4 */
xml.append("</attributes>");
}
xml.append("</cluster_property_set></crm_config>'");
final StringBuilder command = new StringBuilder(getCibCommand("-R", "crm_config", xml.toString()));
if (rdiMetaArgs != null && !Tools.versionBeforePacemaker(host)) {
String updateOrReplace = "-R";
if (rscDefaultsId == null) {
rscDefaultsId = "rsc-options";
updateOrReplace = "-U";
}
final StringBuilder rscdXML = new StringBuilder(360);
rscdXML.append("'<rsc_defaults><meta_attributes id=\"");
rscdXML.append(rscDefaultsId);
rscdXML.append("\">");
for (final Map.Entry<String, String> rdiMetaEntry : rdiMetaArgs.entrySet()) {
final String id = "rsc-options-" + rdiMetaEntry.getKey();
rscdXML.append("<nvpair id=\"");
rscdXML.append(id);
rscdXML.append("\" name=\"");
rscdXML.append(rdiMetaEntry.getKey());
rscdXML.append("\" value=\"");
rscdXML.append(rdiMetaEntry.getValue());
rscdXML.append("\"/>");
}
rscdXML.append("</meta_attributes></rsc_defaults>'");
command.append(';');
command.append(getCibCommand(updateOrReplace, "rsc_defaults", rscdXML.toString()));
}
final SshOutput ret = execCommand(host, command.toString(), runMode);
return ret.getExitCode() == 0;
}
public static boolean removeColocation(
final Host host, final String colocationId, final Application.RunMode runMode) {
final StringBuilder xml = new StringBuilder(360);
xml.append("'<rsc_colocation id=\"");
xml.append(colocationId);
xml.append("\"/>'");
final String command = getCibCommand("-D", "constraints", xml.toString());
final SshOutput ret = execCommand(host, command, runMode);
return ret.getExitCode() == 0;
}
public static boolean addColocation(final Host host,
final String colId,
final String resId,
final String parentHbId,
Map<String, String> attrs,
final Application.RunMode runMode) {
if (parentHbId == null) {
return false;
}
final String colocationId;
final String cibadminOpt;
if (colId == null) {
cibadminOpt = "-C"; /* create */
if (parentHbId.compareTo(resId) < 0) {
colocationId = "col_" + resId + '_' + parentHbId;
} else {
colocationId = "col_" + parentHbId + '_' + resId;
}
} else {
cibadminOpt = "-R"; /* replace */
colocationId = colId;
}
if (attrs == null) {
attrs = new LinkedHashMap<String, String>();
}
attrs.put("rsc", resId);
attrs.put("with-rsc", parentHbId);
final StringBuilder xml = new StringBuilder(360);
xml.append("'<rsc_colocation id=\"");
xml.append(colocationId);
final Map<String, String> convertHash = new HashMap<String, String>();
if (Tools.versionBeforePacemaker(host)) {
/* <= 2.1.4 */
convertHash.put("rsc", "from");
convertHash.put("with-rsc", "to");
convertHash.put("rsc-role", "from_role");
convertHash.put("with-rsc-role", "to_role");
}
for (String attr : attrs.keySet()) {
final String value = attrs.get(attr);
if (value != null && value.isEmpty()) {
continue;
}
if (convertHash.containsKey(attr)) {
attr = convertHash.get(attr);
}
xml.append("\" ").append(attr).append("=\"");
xml.append(value);
}
xml.append("\"/>'");
final String command = getCibCommand(cibadminOpt, "constraints", xml.toString());
final SshOutput ret = execCommand(host, command, runMode);
return ret.getExitCode() == 0;
}
public static boolean removeOrder(final Host host, final String orderId, final Application.RunMode runMode) {
final StringBuilder xml = new StringBuilder(360);
xml.append("'<rsc_order id=\"");
xml.append(orderId);
xml.append("\"/>'");
final String command = getCibCommand("-D", "constraints", xml.toString());
final SshOutput ret = execCommand(host, command, runMode);
return ret.getExitCode() == 0;
}
public static boolean addOrder(final Host host,
final String ordId,
final String parentHbId,
final String resId,
Map<String, String> attrs,
final Application.RunMode runMode) {
final String orderId;
final String cibadminOpt;
if (ordId == null) {
cibadminOpt = "-C"; /* create */
orderId = "ord_" + parentHbId + '_' + resId;
} else {
cibadminOpt = "-R"; /* replace */
orderId = ordId;
}
if (attrs == null) {
attrs = new LinkedHashMap<String, String>();
}
final StringBuilder xml = new StringBuilder(360);
xml.append("'<rsc_order id=\"");
xml.append(orderId);
attrs.put("first", parentHbId);
attrs.put("then", resId);
final Map<String, String> convertHash = new HashMap<String, String>();
if (Tools.versionBeforePacemaker(host)) {
/* <= 2.1.4 */
convertHash.put("first", "to");
convertHash.put("then", "from");
convertHash.put("first-action", "action");
convertHash.put("then-action", "to_action");
}
for (String attr : attrs.keySet()) {
final String value = attrs.get(attr);
if (value != null && value.isEmpty()) {
continue;
}
if (convertHash.containsKey(attr)) {
attr = convertHash.get(attr);
}
xml.append("\" ").append(attr).append("=\"");
xml.append(value);
}
xml.append("\"/>'");
final String command = getCibCommand(cibadminOpt, "constraints", xml.toString());
final SshOutput ret = execCommand(host, command, runMode);
return ret.getExitCode() == 0;
}
public static boolean standByOn(final Host host, final Host standByHost, final Application.RunMode runMode) {
String cmd = "CRM.standByOn";
if (Tools.versionBeforePacemaker(host)) {
cmd = "CRM.2.1.4.standByOn";
}
final Map<String, String> replaceHash = new HashMap<String, String>();
replaceHash.put("@HOST@", standByHost.getName());
final String command = host.getDistCommand(cmd, replaceHash);
final SshOutput ret = execCommand(host, command, runMode);
return ret.getExitCode() == 0;
}
public static boolean standByOff(final Host host, final Host standByHost, final Application.RunMode runMode) {
final Map<String, String> replaceHash = new HashMap<String, String>();
String cmd = "CRM.standByOff";
if (Tools.versionBeforePacemaker(host)) {
cmd = "CRM.2.1.4.standByOff";
}
replaceHash.put("@HOST@", standByHost.getName());
final String command = host.getDistCommand(cmd, replaceHash);
final SshOutput ret = execCommand(host, command, runMode);
return ret.getExitCode() == 0;
}
public static boolean erase(final Host host, final Application.RunMode runMode) {
final Map<String, String> replaceHash = Collections.emptyMap();
final String command = host.getDistCommand("CRM.erase", replaceHash);
final SshOutput ret = execCommand(host, command, runMode);
return ret.getExitCode() == 0;
}
public static String crmConfigureCommit(final Host host, final String config, final Application.RunMode runMode) {
final Map<String, String> replaceHash = new HashMap<String, String>();
replaceHash.put("@CONFIG@", Tools.escapeQuotes(Matcher.quoteReplacement(config), 1));
final String command = host.getDistCommand("CRM.configureCommit", replaceHash);
final SshOutput ret = execCommand(host, command, runMode);
if (ret.getExitCode() == 0) {
return ret.getOutput();
}
return "error";
}
private CRM() {
/* empty */
}
}