package org.ovirt.engine.core.config; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.ConnectException; import java.security.GeneralSecurityException; import java.security.InvalidParameterException; import java.sql.SQLException; import java.text.MessageFormat; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.commons.configuration.Configuration; import org.apache.commons.configuration.HierarchicalConfiguration; import org.apache.commons.configuration.PropertiesConfiguration; import org.apache.commons.configuration.SubnodeConfiguration; import org.apache.commons.configuration.tree.ConfigurationNode; import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; import org.ovirt.engine.core.config.db.ConfigDAO; import org.ovirt.engine.core.config.db.ConfigDaoImpl; import org.ovirt.engine.core.config.entity.ConfigKey; import org.ovirt.engine.core.config.entity.ConfigKeyFactory; import org.ovirt.engine.core.config.validation.ConfigActionType; /** * The <code>EngineConfigLogic</code> class is responsible for the logic of the EngineConfig tool. */ public class EngineConfigLogic { private final static String ALTERNATE_KEY = "alternateKey"; private final static Logger log = Logger.getLogger(EngineConfigLogic.class); private Configuration appConfig; private HierarchicalConfiguration keysConfig; private Map<String, String> alternateKeysMap; private ConfigKeyFactory configKeyFactory; private ConfigDAO configDAO; private EngineConfigCLIParser parser; public EngineConfigLogic(EngineConfigCLIParser parser) throws Exception { this.parser = parser; init(); } /** * Initiates the members of the class. * * @throws Exception */ private void init() throws Exception { log.debug("init: beginning initiation of EngineConfigLogic"); appConfig = new AppConfig<PropertiesConfiguration>(parser.getAlternateConfigFile()).getFile(); keysConfig = new KeysConfig<HierarchicalConfiguration>(parser.getAlternatePropertiesFile()).getFile(); populateAlternateKeyMap(keysConfig); ConfigKeyFactory.init(keysConfig, alternateKeysMap); configKeyFactory = ConfigKeyFactory.getInstance(); try { this.configDAO = new ConfigDaoImpl(appConfig); } catch (SQLException se) { log.debug("init: caught connection error. Error details: ", se); throw new ConnectException("Connection to the Database failed. Please check that the hostname and port number are correct and that the Database service is up and running."); } } private void populateAlternateKeyMap(HierarchicalConfiguration config) { List<SubnodeConfiguration> configurationsAt = config.configurationsAt("/*/" + ALTERNATE_KEY); alternateKeysMap = new HashMap<String, String>(configurationsAt.size()); for (SubnodeConfiguration node : configurationsAt) { String rootKey = node.getRootNode() .getParentNode().getName(); String[] alternateKeys = config.getStringArray("/" + rootKey + "/" + ALTERNATE_KEY); for (String token : alternateKeys) { alternateKeysMap.put(token, rootKey); } } } /** * Executes desired action. Assumes the parser is now holding valid arguments. * * @throws Exception */ public void execute() throws Exception { ConfigActionType actionType = parser.getConfigAction(); log.debug("execute: beginning execution of action " + actionType + "."); switch (actionType) { case ACTION_ALL: printAllValues(); break; case ACTION_LIST: printAvailableKeys(); break; case ACTION_GET: printKey(); break; case ACTION_SET: persistValue(); break; default: // Should have already been discovered before execute log.debug("execute: unable to recognize action: " + actionType + "."); throw new UnsupportedOperationException("Please tell me what to do: list? get? set? get-all?"); } } /** * Prints the values of the given key from the DB. * * @throws Exception */ private void printAllValuesForKey(String key) throws Exception { List<ConfigKey> keysForName = getConfigDAO().getKeysForName(key); if (keysForName.size() == 0) { log.debug("printKeyValues: failed to fetch key " + key + " value: no such entry with default version."); throw new RuntimeException("Error fetching " + key + " value: no such entry with default version."); } StringBuilder buffer = new StringBuilder(); for (ConfigKey configKey : keysForName) { buffer.append(String.format("%s: %s version: %s\n", key, configKey.getDisplayValue(), configKey.getVersion()) ); } buffer.deleteCharAt(buffer.length() - 1); log.info(buffer); } /** * Prints all configuration values. Is the actual execution of the 'get-all' action ('-a', '--all') */ private void printAllValues() { List<ConfigurationNode> configNodes = keysConfig.getRootNode().getChildren(); for (ConfigurationNode node : configNodes) { ConfigKey key = configKeyFactory.generateByPropertiesKey(node.getName()); // TODO - move to one statement for all - time permitting; try { printAllValuesForKey(key.getKey()); } catch (Exception e) { log.error("Skipping " + key.getKey() + " due to an error: " + e.getMessage()); log.debug("details:", e); } } } /** * Prints all available configuration keys. Is the actual execution of the 'list' action ('-l', '--list') */ public void printAvailableKeys() { List<ConfigurationNode> configNodes = keysConfig.getRootNode().getChildren(); for (ConfigurationNode node : configNodes) { ConfigKey key = configKeyFactory.generateByPropertiesKey(node.getName()); log.info(MessageFormat.format("{0}: {1} (Value Type: {2})", key.getKey(), key.getDescription(), key.getType())); } } /** * If a version has been given, prints the specific value for the key and version, otherwise prints all the values * for the key. Is the actual execution of the 'get' action ('-g', '--get') * @throws Exception */ private void printKey() throws Exception { String key = parser.getKey(); String version = parser.getVersion(); if (StringUtils.isBlank(version)) { if (getConfigKey(key) == null) { throw new RuntimeException("Error fetching " + key + " value: no such entry. Please verify key name and property file support."); } printAllValuesForKey(key); } else { printKeyWithSpecifiedVersion(key, version); } } /** * Fetches the given key with the given version from the DB and prints it. * * @param key * @param version * @throws Exception */ private void printKeyWithSpecifiedVersion(String key, String version) throws Exception { ConfigKey configKey = fetchConfigKey(key, version); if (configKey == null || configKey.getKey() == null) { log.debug("getValue: error fetching " + key + " value: no such entry with version '" + version + "'."); throw new RuntimeException("Error fetching " + key + " value: no such entry with version '" + version + "'."); } else { log.info(configKey.getDisplayValue()); } } /** * Sets the value of the given key for the given version. Is the actual execution of the 'set' action ('-s', * '--set') */ private void persistValue() throws Exception { String key = parser.getKey(); String value = parser.getValue(); String version = parser.getVersion(); if (version == null) { version = startVersionDialog(key); } boolean sucessUpdate = persist(key, value, version); if (!sucessUpdate) { log.debug("setValue: error setting " + key + "'s value. No such entry" + (version == null ? "" : " with version " + version) + "."); throw new IllegalArgumentException("Error setting " + key + "'s value. No such entry" + (version == null ? "" : " with version " + version) + "."); } } /** * Is called when it is unclear which version is desired. If only one version exists for the given key, assumes that * is the desired version, if more than one exist, prompts the user to choose one. * * @param key * The version needs to be found for this key * @return A version for the given key * @throws IOException * @throws SQLException */ private String startVersionDialog(String key) throws IOException, SQLException { log.debug("starting version dialog."); String version = null; List<ConfigKey> keys = configDAO.getKeysForName(key); if (keys.size() == 1) { version = keys.get(0).getVersion(); } else if (keys.size() > 1) { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); while (true) { System.out.println("Please select a version:"); for (int i = 0; i < keys.size(); i++) { System.out.println(i + 1 + ". " + keys.get(i).getVersion()); } int index = 0; try { index = Integer.valueOf(br.readLine()); } catch (NumberFormatException e) { continue; } if (index >= 1 && index <= keys.size()) { version = keys.get(index - 1).getVersion(); break; } } } return version; } /** * Sets the given key with the given version to the given value. Is protected for test purposes. * * @param key * @param value * @param version * @return * @throws IllegalAccessException */ protected boolean persist(String key, String value, String version) throws IllegalAccessException { ConfigKey configKey = configKeyFactory.generateByPropertiesKey(key); configKey.setVersion(version); String message = null; boolean res = true; try { configKey.safeSetValue(value); res = (getConfigDAO().updateKey(configKey) == 1); } catch (InvalidParameterException ipe) { message = ( "'" + value + "' is not a valid value for type " + configKey.getType() + ". " + (configKey.getValidValues().isEmpty() ? "" : "Valid values are " + configKey.getValidValues()) ); } catch (Exception e) { message = ( "Error setting " + key + "'s value. No such entry with version '" + version + "'." ); log.debug("Error details: ", e); } if (message != null) { log.debug(message); throw new IllegalAccessException(message); } return res; } public boolean persist(String key, String value) throws Exception { return persist(key, value, ""); } private ConfigKey getConfigKey(String key) { ConfigKey ckReturn = null; ckReturn = configKeyFactory.generateByPropertiesKey(key); if (ckReturn == null || ckReturn.getKey() == null) { ckReturn = null; log.debug("getConfigKey: Unable to fetch the value of " + key + "."); } return ckReturn; } public ConfigKey fetchConfigKey(String key, String version) { ConfigKey configKey = getConfigKey(key); if (configKey == null || configKey.getKey() == null) { log.debug("Unable to fetch the value of " + key + " in version " + version); return null; } configKey.setVersion(version); log.debug("Fetching key=" + configKey.getKey() + " ver=" + version); try { return getConfigDAO().getKey(configKey); } catch (SQLException e) { return null; } } public ConfigDAO getConfigDAO() { return configDAO; } }