package edu.washington.escience.myria.tools; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Map; import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.apache.reef.tang.Configuration; import org.apache.reef.tang.formats.AvroConfigurationSerializer; import org.apache.reef.tang.formats.ConfigurationModule; import org.apache.reef.tang.formats.OptionalParameter; import org.ini4j.ConfigParser; import org.ini4j.ConfigParser.InterpolationException; import org.ini4j.ConfigParser.InterpolationMissingOptionException; import org.ini4j.ConfigParser.NoOptionException; import org.ini4j.ConfigParser.NoSectionException; import edu.washington.escience.myria.MyriaConstants; import edu.washington.escience.myria.MyriaSystemConfigKeys; import edu.washington.escience.myria.api.MyriaApiConstants; import edu.washington.escience.myria.coordinator.ConfigFileException; /** The class to read Myria configuration file, e.g. deployment.cfg. */ public final class MyriaConfigurationParser { // this is a static utility class private MyriaConfigurationParser() {} /** * load the config file. * * @param filename filename. * @return parsed mapping from sections to keys to values. * @throws ConfigFileException if error occurred when parsing the config file * */ public static Configuration loadConfiguration(final String filename) throws ConfigFileException { final ConfigParser parser = new ConfigParser(); try { parser.read(new File(filename)); } catch (IOException e) { throw new ConfigFileException(e); } ConfigurationModule cm = MyriaGlobalConfigurationModule.CONF; cm = setGlobalConfVariables(parser, cm); cm = setWorkerConfs(parser, cm); cm = setJvmOptions(parser, cm); return cm.build(); } private static ConfigurationModule setGlobalConfVariables( final ConfigParser parser, final ConfigurationModule cm) throws ConfigFileException { // Required parameters ConfigurationModule conf = cm.set( MyriaGlobalConfigurationModule.INSTANCE_NAME, getRequired(parser, "deployment", MyriaSystemConfigKeys.DESCRIPTION)) .set( MyriaGlobalConfigurationModule.DEFAULT_INSTANCE_PATH, getPath(parser, MyriaConstants.MASTER_ID)) .set( MyriaGlobalConfigurationModule.MASTER_HOST, getHostname(parser, MyriaConstants.MASTER_ID)) .set( MyriaGlobalConfigurationModule.MASTER_RPC_PORT, getPort(parser, MyriaConstants.MASTER_ID)) .set( MyriaGlobalConfigurationModule.PERSIST_URI, getRequired(parser, "persist", MyriaSystemConfigKeys.PERSIST_URI)); // Optional parameters conf = setOptional( conf, MyriaGlobalConfigurationModule.STORAGE_DBMS, getOptional( parser, "deployment", MyriaSystemConfigKeys.WORKER_STORAGE_DATABASE_SYSTEM)); conf = setOptional( conf, MyriaGlobalConfigurationModule.DEFAULT_STORAGE_DB_NAME, getOptional(parser, "deployment", MyriaSystemConfigKeys.WORKER_STORAGE_DATABASE_NAME)); conf = setOptional( conf, MyriaGlobalConfigurationModule.DEFAULT_STORAGE_DB_USERNAME, getOptional(parser, "deployment", MyriaSystemConfigKeys.USERNAME)); conf = setOptional( conf, MyriaGlobalConfigurationModule.DEFAULT_STORAGE_DB_PASSWORD, getOptional( parser, "deployment", MyriaSystemConfigKeys.WORKER_STORAGE_DATABASE_PASSWORD)); conf = setOptional( conf, MyriaGlobalConfigurationModule.DEFAULT_STORAGE_DB_PORT, getOptional(parser, "deployment", MyriaSystemConfigKeys.WORKER_STORAGE_DATABASE_PORT)); conf = setOptional( conf, MyriaGlobalConfigurationModule.REST_API_PORT, getOptional(parser, "deployment", MyriaSystemConfigKeys.REST_PORT)); conf = setOptional( conf, MyriaGlobalConfigurationModule.API_ADMIN_PASSWORD, getOptional(parser, "deployment", MyriaSystemConfigKeys.ADMIN_PASSWORD)); conf = setOptional( conf, MyriaGlobalConfigurationModule.USE_SSL, getOptional(parser, "deployment", MyriaSystemConfigKeys.SSL)); conf = setOptional( conf, MyriaGlobalConfigurationModule.SSL_KEYSTORE_PATH, getOptional(parser, "deployment", MyriaApiConstants.MYRIA_API_SSL_KEYSTORE)); conf = setOptional( conf, MyriaGlobalConfigurationModule.SSL_KEYSTORE_PASSWORD, getOptional(parser, "deployment", MyriaApiConstants.MYRIA_API_SSL_KEYSTORE_PASSWORD)); conf = setOptional( conf, MyriaGlobalConfigurationModule.ENABLE_DEBUG, getOptional(parser, "deployment", MyriaSystemConfigKeys.DEBUG)); conf = setOptional( conf, MyriaGlobalConfigurationModule.MASTER_NUMBER_VCORES, getOptional(parser, "runtime", MyriaSystemConfigKeys.MASTER_NUMBER_VCORES)); conf = setOptional( conf, MyriaGlobalConfigurationModule.WORKER_NUMBER_VCORES, getOptional(parser, "runtime", MyriaSystemConfigKeys.WORKER_NUMBER_VCORES)); conf = setOptional( conf, MyriaGlobalConfigurationModule.DRIVER_MEMORY_QUOTA_GB, getOptional(parser, "runtime", MyriaSystemConfigKeys.DRIVER_MEMORY_QUOTA_GB)); conf = setOptional( conf, MyriaGlobalConfigurationModule.MASTER_MEMORY_QUOTA_GB, getOptional(parser, "runtime", MyriaSystemConfigKeys.MASTER_MEMORY_QUOTA_GB)); conf = setOptional( conf, MyriaGlobalConfigurationModule.WORKER_MEMORY_QUOTA_GB, getOptional(parser, "runtime", MyriaSystemConfigKeys.WORKER_MEMORY_QUOTA_GB)); conf = setOptional( conf, MyriaGlobalConfigurationModule.MASTER_JVM_HEAP_SIZE_MIN_GB, getOptional(parser, "runtime", MyriaSystemConfigKeys.MASTER_JVM_HEAP_SIZE_MIN_GB)); conf = setOptional( conf, MyriaGlobalConfigurationModule.WORKER_JVM_HEAP_SIZE_MIN_GB, getOptional(parser, "runtime", MyriaSystemConfigKeys.WORKER_JVM_HEAP_SIZE_MIN_GB)); conf = setOptional( conf, MyriaGlobalConfigurationModule.MASTER_JVM_HEAP_SIZE_MAX_GB, getOptional(parser, "runtime", MyriaSystemConfigKeys.MASTER_JVM_HEAP_SIZE_MAX_GB)); conf = setOptional( conf, MyriaGlobalConfigurationModule.WORKER_JVM_HEAP_SIZE_MAX_GB, getOptional(parser, "runtime", MyriaSystemConfigKeys.WORKER_JVM_HEAP_SIZE_MAX_GB)); conf = setOptional( conf, MyriaGlobalConfigurationModule.FLOW_CONTROL_WRITE_BUFFER_LOW_MARK_BYTES, getOptional( parser, "deployment", MyriaSystemConfigKeys.FLOW_CONTROL_WRITE_BUFFER_LOW_MARK_BYTES)); conf = setOptional( conf, MyriaGlobalConfigurationModule.FLOW_CONTROL_WRITE_BUFFER_HIGH_MARK_BYTES, getOptional( parser, "deployment", MyriaSystemConfigKeys.FLOW_CONTROL_WRITE_BUFFER_HIGH_MARK_BYTES)); conf = setOptional( conf, MyriaGlobalConfigurationModule.OPERATOR_INPUT_BUFFER_CAPACITY, getOptional( parser, "deployment", MyriaSystemConfigKeys.OPERATOR_INPUT_BUFFER_CAPACITY)); conf = setOptional( conf, MyriaGlobalConfigurationModule.OPERATOR_INPUT_BUFFER_RECOVER_TRIGGER, getOptional( parser, "deployment", MyriaSystemConfigKeys.OPERATOR_INPUT_BUFFER_RECOVER_TRIGGER)); conf = setOptional( conf, MyriaGlobalConfigurationModule.TCP_CONNECTION_TIMEOUT_MILLIS, getOptional(parser, "deployment", MyriaSystemConfigKeys.TCP_CONNECTION_TIMEOUT_MILLIS)); conf = setOptional( conf, MyriaGlobalConfigurationModule.TCP_SEND_BUFFER_SIZE_BYTES, getOptional(parser, "deployment", MyriaSystemConfigKeys.TCP_SEND_BUFFER_SIZE_BYTES)); conf = setOptional( conf, MyriaGlobalConfigurationModule.TCP_RECEIVE_BUFFER_SIZE_BYTES, getOptional(parser, "deployment", MyriaSystemConfigKeys.TCP_RECEIVE_BUFFER_SIZE_BYTES)); return conf; } private static ConfigurationModule setWorkerConfs( final ConfigParser parser, final ConfigurationModule cm) throws ConfigFileException { ConfigurationModule conf = cm; for (Integer workerId : getWorkerIds(parser)) { Configuration workerConfig = MyriaWorkerConfigurationModule.CONF .set(MyriaWorkerConfigurationModule.WORKER_ID, workerId + "") .set(MyriaWorkerConfigurationModule.WORKER_HOST, getHostname(parser, workerId)) .set(MyriaWorkerConfigurationModule.WORKER_PORT, getPort(parser, workerId)) .set( MyriaWorkerConfigurationModule.WORKER_STORAGE_DB_NAME, getWorkerDatabaseName(parser, workerId)) .set(MyriaWorkerConfigurationModule.WORKER_FILESYSTEM_PATH, getPath(parser, workerId)) .build(); String serializedWorkerConfig = new AvroConfigurationSerializer().toString(workerConfig); conf = conf.set(MyriaGlobalConfigurationModule.WORKER_CONF, serializedWorkerConfig); } return conf; } private static ConfigurationModule setJvmOptions( final ConfigParser parser, final ConfigurationModule cm) throws ConfigFileException { ConfigurationModule conf = cm; final String options = getOptional(parser, "runtime", MyriaSystemConfigKeys.JVM_OPTIONS); if (options != null) { final List<String> optionList = Arrays.asList(options.split(" ")); for (final String option : optionList) { conf = conf.set(MyriaGlobalConfigurationModule.JVM_OPTIONS, option); } } return conf; } /** * * @param nodeId the node ID * @return working directory * @throws ConfigFileException if error occurred when getting the value */ private static String getPath(final ConfigParser parser, final int nodeId) throws ConfigFileException { if (nodeId != MyriaConstants.MASTER_ID) { // worker, check if its path is specified String[] tmp = getRequired(parser, "workers", nodeId + "").split(":"); if (tmp.length > 2) { return tmp[2]; } } return getRequired(parser, "deployment", MyriaSystemConfigKeys.DEPLOYMENT_PATH); } /** * * @param workerId the worker ID * @return the database name * @throws ConfigFileException if error occurred when getting the value */ private static String getWorkerDatabaseName(final ConfigParser parser, final int workerId) throws ConfigFileException { String[] tmp = getRequired(parser, "workers", workerId + "").split(":"); if (tmp.length > 3) { // use the value specified for this worker return tmp[3]; } // otherwise the default one return getOptional(parser, "deployment", MyriaSystemConfigKeys.WORKER_STORAGE_DATABASE_NAME); } /** * * @param nodeId the worker/master ID * @return the hostname * @throws ConfigFileException if error occurred when getting the value */ private static String getHostname(final ConfigParser parser, final int nodeId) throws ConfigFileException { if (nodeId == MyriaConstants.MASTER_ID) { return getRequired(parser, "master", nodeId + "").split(":")[0]; } else { return getRequired(parser, "workers", nodeId + "").split(":")[0]; } } /** * * @param nodeId the worker/master ID * @return the port number * @throws ConfigFileException if error occurred when getting the value */ private static int getPort(final ConfigParser parser, final int nodeId) throws ConfigFileException { if (nodeId == MyriaConstants.MASTER_ID) { return Integer.parseInt(getRequired(parser, "master", nodeId + "").split(":")[1]); } else { return Integer.parseInt(getRequired(parser, "workers", nodeId + "").split(":")[1]); } } /** * * @return a list of worker IDs * @throws ConfigFileException if error occurred when getting the value */ private static List<Integer> getWorkerIds(final ConfigParser parser) throws ConfigFileException { List<Integer> ret = new ArrayList<Integer>(); try { for (Map.Entry<String, String> node : parser.items("workers")) { ret.add(Integer.parseInt(node.getKey())); } } catch (InterpolationMissingOptionException | NoSectionException e) { throw new ConfigFileException(e); } return ret; } /** * * @param section the section * @param key the key * @return the value, must exist * @throws ConfigFileException if error occurred when getting the value */ @Nonnull private static String getRequired( final ConfigParser parser, final String section, final String key) throws ConfigFileException { try { return parser.get(section, key); } catch (NoSectionException | NoOptionException | InterpolationException e) { throw new ConfigFileException(e); } } /** * * @param section the section * @param key the key * @return the value, null if not exist */ @Nullable private static String getOptional( final ConfigParser parser, final String section, final String key) { try { return parser.get(section, key); } catch (NoSectionException | NoOptionException | InterpolationException e) { return null; } } @Nonnull private static <T> ConfigurationModule setOptional( final ConfigurationModule cm, final OptionalParameter<T> param, @Nullable final String optionValue) { ConfigurationModule conf = cm; if (optionValue != null) { conf = conf.set(param, optionValue); } return conf; } }