package org.ovirt.engine.core.utils.pm;
import java.io.Serializable;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.regex.Pattern;
import org.apache.commons.lang.StringUtils;
import org.ovirt.engine.core.common.businessentities.ArchitectureType;
import org.ovirt.engine.core.common.config.Config;
import org.ovirt.engine.core.common.config.ConfigCommon;
import org.ovirt.engine.core.common.config.ConfigValues;
import org.ovirt.engine.core.compat.IntegerCompat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class VdsFenceOptions implements Serializable {
private static final long serialVersionUID = -8832636627473217232L;
private static final String COMMA = ",";
private static final String EQUAL = "=";
private static final String NEWLINE = "\n";
private static final String SEMICOLON = ";";
private static final String COLON = ":";
private static final String TRUE_STRING = "true";
private static final String FALSE_STRING = "false";
private static final String YES = "yes";
private static final String NO = "no";
private static final String AGENT_ERROR = "Cannot find fence agent named '{}' in fence option mapping";
private static final String MAPPING_FORMAT_ERROR = "Illegal fence mapping format '{}'";
private static final Logger log = LoggerFactory.getLogger(VdsFenceOptions.class);
private HashMap<String, HashMap<String, String>> fenceOptionMapping;
private static HashMap<String, String> fenceOptionTypes;
private String fenceAgent = "";
private String fenceOptions;
private static HashMap<String, String> fenceAgentInstanceOptions;
private static HashSet<String> fenceSpecialParams;
private String version;
/**
* Initializes a new instance of the <see cref="VdsFenceOptions"/> class.
*/
public VdsFenceOptions(String version) {
this(null, null, version);
}
/**
* Initializes a new instance of the <see cref="VdsFenceOptions"/> class.
* @param agent
* The agent.
* @param fenceOptions
* The fence options.
*/
public VdsFenceOptions(String agent, String fenceOptions, String version) {
if (StringUtils.isNotEmpty(agent)) {
this.fenceAgent = agent;
this.fenceOptions = fenceOptions;
}
this.version = version;
initCache();
init();
}
public HashMap<String, HashMap<String, String>> getFenceOptionMappingMap() {
return fenceOptionMapping;
}
/**
* Caches the fence agents options mapping. Mapping are stored in the following format <!--
* <agent>:{var=value}{[,]var=value}*; --> for example :
* alom:secure=secure,port=ipport;apc:secure=secure,port=ipport,slot=port
*/
private void cacheFenceAgentsOptionMapping() {
String localFenceOptionMapping = FenceConfigHelper.getFenceConfigurationValue(ConfigValues.VdsFenceOptionMapping.name(), version);
String[] agentsOptionsStr = localFenceOptionMapping.split(Pattern.quote(SEMICOLON), -1);
for (String agentOptionsStr : agentsOptionsStr) {
String[] parts = agentOptionsStr.split(Pattern.quote(COLON), -1);
if (parts.length == 2) {
String agent = parts[0];
HashMap<String, String> agentOptions = new HashMap<>();
// check for empty options
if (StringUtils.isNotEmpty(parts[1])) {
String[] options = parts[1].split(Pattern.quote(COMMA), -1);
for (String option : options) {
String[] optionKeyVal = option.split(Pattern.quote(EQUAL), -1);
agentOptions.put(optionKeyVal[0], optionKeyVal[1]);
// add mapped keys to special params
fenceSpecialParams.add(optionKeyVal[1]);
}
}
fenceOptionMapping.put(agent, agentOptions);
} else {
log.error(MAPPING_FORMAT_ERROR, agentOptionsStr);
break;
}
}
}
/**
* Caches the fence agents option types. Types are stored in the following format <!-- [key=type][,][key=type]*-->
* for example : secure=bool,port=int,slot=int
*/
private void cacheFenceAgentsOptionTypes() {
String localfenceOptionTypes = Config.getValue(ConfigValues.VdsFenceOptionTypes);
String[] types = localfenceOptionTypes.split(Pattern.quote(COMMA), -1);
for (String entry : types) {
String[] optionKeyVal = entry.split(Pattern.quote(EQUAL), -1);
fenceOptionTypes.put(optionKeyVal[0], optionKeyVal[1]);
}
}
/**
* Gets the real key given the displayed key.
*
* @param agent
* The agent.
* @param displayedKey
* The displayed key.
*/
private String getRealKey(String agent, String displayedKey) {
String result = "";
if (StringUtils.isNotEmpty(agent) && StringUtils.isNotEmpty(displayedKey)) {
if (fenceOptionMapping.containsKey(agent)) {
HashMap<String, String> agentOptions = fenceOptionMapping.get(agent);
result = agentOptions.getOrDefault(displayedKey, displayedKey);
} else {
log.error(AGENT_ERROR, agent);
}
}
return result;
}
/**
* Gets the displayed key given the real key.
*
* @param agent
* The agent.
* @param realKey
* The real key.
*/
private String getDisplayedKey(String agent, String realKey) {
String result = "";
if (StringUtils.isNotEmpty(agent) && StringUtils.isNotEmpty(realKey)) {
if (fenceOptionMapping.containsKey(agent)) {
HashMap<String, String> agentOptions = fenceOptionMapping.get(agent);
if (agentOptions.containsValue(realKey)) {
for (Map.Entry<String, String> pair : agentOptions.entrySet()) {
if (StringUtils.equals(pair.getValue(), realKey)) {
result = pair.getKey();
break;
}
}
} else {
// assume that a legal flag that not exists in mapping was
// used
result = realKey;
}
} else {
log.error(AGENT_ERROR, agent);
}
}
return result;
}
/**
* Gets the type of the key.
*
* @param key
* The key.
*/
private String getOptionType(String key) {
String result = "";
if (StringUtils.isNotEmpty(key) && fenceOptionTypes.containsKey(key)) {
result = fenceOptionTypes.get(key);
}
return result;
}
/**
* Translates the bool value to yes/no.
*
* @param value
* The value.
*/
private static String translateBoolValue(String value) {
String result;
if (value.equalsIgnoreCase(TRUE_STRING) || value.equalsIgnoreCase(FALSE_STRING)) {
if (Boolean.parseBoolean(value)) {
result = YES;
}
else {
result = NO;
}
} else {
result = value;
}
return result;
}
/**
* Inits this instance.
*/
private void init() {
initCache();
cacheFenceAgentInstanceOptions();
}
/**
* Cleans up.
*/
private void cleanUp() {
if (fenceAgentInstanceOptions != null && fenceOptionMapping != null
&& fenceOptionTypes != null) {
fenceAgentInstanceOptions.clear();
fenceOptionMapping.clear();
fenceOptionTypes.clear();
fenceSpecialParams.clear();
}
init();
}
/**
* Inits the cache.
*/
private void initCache() {
if (fenceOptionMapping == null) {
fenceAgentInstanceOptions = new HashMap<>();
fenceOptionMapping = new HashMap<>();
fenceOptionTypes = new HashMap<>();
fenceSpecialParams = new HashSet<>();
cacheFenceAgentsOptionMapping();
cacheFenceAgentsOptionTypes();
}
}
/**
* Caches the fence agent instance options.
*/
private void cacheFenceAgentInstanceOptions() {
if (StringUtils.isNotEmpty(getAgent())
&& StringUtils.isNotEmpty(getFenceOptions())) {
String[] options = getFenceOptions().split(Pattern.quote(COMMA), -1);
fenceAgentInstanceOptions.clear();
for (String option : options) {
String[] optionKeyVal = option.split(Pattern.quote(EQUAL), -1);
if (optionKeyVal.length == 1) {
add(getAgent(), optionKeyVal[0], "");
} else {
add(getAgent(), optionKeyVal[0], optionKeyVal[1]);
}
}
}
}
/**
* handles agent mapping, get the real agent for a given agent name
*
* @param agent
* the agent name
* @return string , the agent real name to be used
*/
public static String getRealAgent(String agent) {
String agentMapping = FenceConfigHelper.getFenceConfigurationValue(ConfigValues.FenceAgentMapping.name(), ConfigCommon.defaultConfigurationVersion);
String realAgent = agent;
// result has the format [<agent>=<real agent>[,]]*
String[] settings = agentMapping.split(Pattern.quote(COMMA), -1);
if (settings.length > 0) {
for (String setting : settings) {
// get the <agent>=<real agent> pair
String[] pair = setting.split(Pattern.quote(EQUAL), -1);
if (pair.length == 2) {
if (agent.equalsIgnoreCase(pair[0])) {
realAgent = pair[1];
break;
}
}
}
}
return realAgent;
}
/**
* handles agent default options
*
* @return String the options after adding default agent parameters
*/
public static String getDefaultAgentOptions(String agent, String fenceOptions, ArchitectureType architectureType) {
String agentDefaultParams = (architectureType != null && architectureType == ArchitectureType.ppc64)
?
FenceConfigHelper.getFenceConfigurationValue(ConfigValues.FenceAgentDefaultParamsForPPC.name(), ConfigCommon.defaultConfigurationVersion)
:
FenceConfigHelper.getFenceConfigurationValue(ConfigValues.FenceAgentDefaultParams.name(), ConfigCommon.defaultConfigurationVersion);
StringBuilder realOptions = new StringBuilder(fenceOptions);
// result has the format [<agent>:param=value[,]...;]*
String[] params = agentDefaultParams.split(Pattern.quote(SEMICOLON), -1);
for (String agentOptionsStr : params) {
String[] parts = agentOptionsStr.split(Pattern.quote(COLON), -1);
if (parts.length == 2) {
if (agent.equalsIgnoreCase(parts[0])) {
// check for empty options
if (StringUtils.isNotEmpty(parts[1])) {
String[] options = parts[1].split(Pattern.quote(COMMA), -1);
for (String option : options) {
String[] optionKeyVal = option.split(Pattern.quote(EQUAL), -1);
// if a value is set explicitly for a default param
// we respect that value and not use the default value
if (!fenceOptions.contains(optionKeyVal[0])) {
if (realOptions.length() > 0) {
realOptions.append(COMMA);
}
realOptions.append(optionKeyVal[0]);
if (optionKeyVal.length == 2) {
String val = (optionKeyVal[1] == null) ? "" : optionKeyVal[1];
realOptions.append(EQUAL);
realOptions.append(val);
}
}
}
}
break;
}
}
}
return realOptions.toString();
}
public String getAgent() {
return fenceAgent;
}
public void setAgent(String value) {
fenceAgent = value;
cleanUp();
}
public String getFenceOptions() {
return fenceOptions;
}
public void setFenceOptions(String value) {
fenceOptions = value;
cleanUp();
}
/**
* Adds the specified key.
*
* @param key
* The key.
* @param value
* The value.
*/
public void add(String key, String value) {
add(getAgent(), key, value);
}
/**
* Adds the specified key.
*
* @param agent
* The agent.
* @param key
* The key.
* @param value
* The value.
*/
public void add(String agent, String key, String value) {
key = getRealKey(agent, key);
fenceAgentInstanceOptions.put(key, value);
}
/**
* Checks if the agent is supported on the version that was set in object constructor
* @param agent
* The agent.
* @return <c>true</c> if the specified agent is supported; otherwise, <c>false</c>.
*/
public boolean isAgentSupported(String agent) {
return fenceOptionMapping.containsKey(agent);
}
/**
* Gets the specified key.
*
* @param key
* The key.
* @return The key value, null if key is not exist
*/
public Object get(String key) {
final String BOOL = "bool";
final String INT = "int";
final String LONG = "long";
final String DOUBLE = "double";
Object result = null;
if (StringUtils.isNotEmpty(key)) {
String type = getOptionType(key);
key = getRealKey(getAgent(), key);
if (fenceAgentInstanceOptions != null
&& fenceAgentInstanceOptions.containsKey(key)) {
if (StringUtils.isNotEmpty(type)) {
// Convert to the suitable type according to metadata.
if (type.equalsIgnoreCase(BOOL)) {
result = Boolean.parseBoolean(fenceAgentInstanceOptions.get(key));
}
else if (type.equalsIgnoreCase(INT)) {
Integer intVal = IntegerCompat.tryParse(fenceAgentInstanceOptions
.get(key));
if (intVal != null) {
result = intVal;
}
}
else if (type.equalsIgnoreCase(LONG)) {
try {
result = Long.parseLong(fenceAgentInstanceOptions.get(key));
} catch (NumberFormatException ignore) {
}
}
else if (type.equalsIgnoreCase(DOUBLE)) {
try {
result = Double.parseDouble(fenceAgentInstanceOptions.get(key));
} catch (NumberFormatException ignore) {
}
} else { // return as string
result = fenceAgentInstanceOptions.get(key);
}
} else {
// return value as an object
result = fenceAgentInstanceOptions.get(key);
}
}
}
return result;
}
/**
* Returns a <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
*
* @return A <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
*/
@Override
public String toString() {
StringBuilder value = new StringBuilder();
String delimiter = "";
for (Map.Entry<String, String> pair : fenceAgentInstanceOptions.entrySet()) {
value.append(delimiter)
.append(getDisplayedKey(getAgent(), pair.getKey()))
.append(pair.getValue().length() > 0 ? EQUAL + pair.getValue() : "");
delimiter = COMMA;
}
return value.toString();
}
/**
* Gets the internal representation of the options.
*/
public String toInternalString() {
StringBuilder value = new StringBuilder();
String delimiter = "";
for (Map.Entry<String, String> pair : fenceAgentInstanceOptions.entrySet()) {
if (pair.getValue().trim().length() > 0) {
value.append(delimiter).append(pair.getKey()).append(EQUAL).append(translateBoolValue(pair.getValue()));
// special params should not be sent if value is empty
} else if (!fenceSpecialParams.contains(pair.getKey())) {
value.append(delimiter).append(pair.getKey());
}
delimiter = NEWLINE;
}
return value.toString();
}
}