blob: b87b4765c5023c137e3fbfcc1030c1618a3c6d94 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.sysml.conf;
import java.io.ByteArrayInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.StringWriter;
import java.util.HashMap;
import java.util.Map;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import org.apache.sysml.hops.OptimizerUtils;
import org.apache.sysml.parser.ParseException;
import org.apache.sysml.runtime.DMLRuntimeException;
import org.apache.sysml.runtime.util.LocalFileUtils;
public class DMLConfig
{
public static final String DEFAULT_SYSTEMML_CONFIG_FILEPATH = "./SystemML-config.xml";
private static final Log LOG = LogFactory.getLog(DMLConfig.class.getName());
// external names of configuration properties
// (single point of change for all internal refs)
public static final String LOCAL_TMP_DIR = "localtmpdir";
public static final String SCRATCH_SPACE = "scratch";
public static final String OPTIMIZATION_LEVEL = "optlevel";
public static final String NUM_REDUCERS = "numreducers";
public static final String JVM_REUSE = "jvmreuse";
public static final String DEFAULT_BLOCK_SIZE = "defaultblocksize";
public static final String YARN_APPMASTER = "dml.yarn.appmaster";
public static final String YARN_APPMASTERMEM = "dml.yarn.appmaster.mem";
public static final String YARN_MAPREDUCEMEM = "dml.yarn.mapreduce.mem";
public static final String YARN_APPQUEUE = "dml.yarn.app.queue";
public static final String CP_PARALLEL_MATRIXMULT = "cp.parallel.matrixmult";
public static final String CP_PARALLEL_TEXTIO = "cp.parallel.textio";
public static final String COMPRESSED_LINALG = "compressed.linalg";
// supported prefixes for custom map/reduce configurations
public static final String PREFIX_MAPRED = "mapred";
public static final String PREFIX_MAPREDUCE = "mapreduce";
//internal config
public static final String DEFAULT_SHARED_DIR_PERMISSION = "777"; //for local fs and DFS
public static String LOCAL_MR_MODE_STAGING_DIR = null;
//configuration default values
private static HashMap<String, String> _defaultVals = null;
private String _fileName = null;
private Element _xmlRoot = null;
static
{
_defaultVals = new HashMap<String, String>();
_defaultVals.put(LOCAL_TMP_DIR, "/tmp/systemml" );
_defaultVals.put(SCRATCH_SPACE, "scratch_space" );
_defaultVals.put(OPTIMIZATION_LEVEL, String.valueOf(OptimizerUtils.DEFAULT_OPTLEVEL.ordinal()) );
_defaultVals.put(NUM_REDUCERS, "10" );
_defaultVals.put(JVM_REUSE, "false" );
_defaultVals.put(DEFAULT_BLOCK_SIZE, String.valueOf(OptimizerUtils.DEFAULT_BLOCKSIZE) );
_defaultVals.put(YARN_APPMASTER, "false" );
_defaultVals.put(YARN_APPMASTERMEM, "2048" );
_defaultVals.put(YARN_MAPREDUCEMEM, "-1" );
_defaultVals.put(YARN_APPQUEUE, "default" );
_defaultVals.put(CP_PARALLEL_MATRIXMULT, "true" );
_defaultVals.put(CP_PARALLEL_TEXTIO, "true" );
_defaultVals.put(COMPRESSED_LINALG, "false" );
}
public DMLConfig()
{
}
/**
*
* @param fileName
* @throws ParseException
* @throws FileNotFoundException
*/
public DMLConfig(String fileName)
throws ParseException, FileNotFoundException
{
this( fileName, false );
}
/**
*
* @param fileName
* @param silent
* @throws ParseException
* @throws FileNotFoundException
*/
public DMLConfig(String fileName, boolean silent)
throws ParseException, FileNotFoundException
{
_fileName = fileName;
try {
parseConfig();
} catch (FileNotFoundException fnfe) {
LOCAL_MR_MODE_STAGING_DIR = getTextValue(LOCAL_TMP_DIR) + "/hadoop/mapred/staging";
throw fnfe;
} catch (Exception e){
//log error, since signature of generated ParseException doesn't allow to pass it
if( !silent )
LOG.error("Failed to parse DML config file ",e);
throw new ParseException("ERROR: error parsing DMLConfig file " + fileName);
}
LOCAL_MR_MODE_STAGING_DIR = getTextValue(LOCAL_TMP_DIR) + "/hadoop/mapred/staging";
}
public DMLConfig( Element root )
{
_xmlRoot = root;
}
public void merge(DMLConfig otherConfig)
throws ParseException
{
if (otherConfig == null)
return;
try {
// for each element in otherConfig, either overwrite existing value OR add to defaultConfig
NodeList otherConfigNodeList = otherConfig._xmlRoot.getChildNodes();
if (otherConfigNodeList != null && otherConfigNodeList.getLength() > 0){
for (int i=0; i<otherConfigNodeList.getLength(); i++){
org.w3c.dom.Node optionalConfigNode = otherConfigNodeList.item(i);
if (optionalConfigNode.getNodeType() == org.w3c.dom.Node.ELEMENT_NODE){
// try to find optional config node in default config node
String paramName = optionalConfigNode.getNodeName();
String paramValue = ((Element)optionalConfigNode).getFirstChild().getNodeValue();
if (_xmlRoot.getElementsByTagName(paramName) != null)
LOG.info("Updating " + paramName + " with value " + paramValue);
else
LOG.info("Defining new attribute" + paramName + " with value " + paramValue);
DMLConfig.setTextValue(_xmlRoot, paramName, paramValue);
}
}
} // end if (otherConfigNodeList != null && otherConfigNodeList.getLength() > 0){
} catch (Exception e){
LOG.error("Failed in merge default config file with optional config file",e);
throw new ParseException("ERROR: error merging config file " + otherConfig._fileName + " with " + _fileName);
}
}
/**
* Method to parse configuration
* @throws ParserConfigurationException
* @throws SAXException
* @throws IOException
*/
private void parseConfig () throws ParserConfigurationException, SAXException, IOException
{
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setIgnoringComments(true); //ignore XML comments
DocumentBuilder builder = factory.newDocumentBuilder();
Document domTree = null;
if (_fileName.startsWith("hdfs:") ||
_fileName.startsWith("gpfs:") ) // config file from DFS
{
if( !LocalFileUtils.validateExternalFilename(_fileName, true) )
throw new IOException("Invalid (non-trustworthy) hdfs config filename.");
FileSystem DFS = FileSystem.get(ConfigurationManager.getCachedJobConf());
Path configFilePath = new Path(_fileName);
domTree = builder.parse(DFS.open(configFilePath));
}
else // config from local file system
{
if( !LocalFileUtils.validateExternalFilename(_fileName, false) )
throw new IOException("Invalid (non-trustworthy) local config filename.");
domTree = builder.parse(_fileName);
}
_xmlRoot = domTree.getDocumentElement();
}
/**
* Method to get string value of a configuration parameter
* Handles processing of configuration parameters
* @param tagName the name of the DMLConfig parameter being retrieved
* @return a string representation of the DMLConfig parameter value.
*/
public String getTextValue(String tagName)
{
//get the actual value
String retVal = (_xmlRoot!=null)?getTextValue(_xmlRoot,tagName):null;
if (retVal == null)
{
if( _defaultVals.containsKey(tagName) )
retVal = _defaultVals.get(tagName);
else
LOG.error("Error: requested dml configuration property '"+tagName+"' is invalid.");
}
return retVal;
}
public int getIntValue( String tagName )
{
return Integer.parseInt( getTextValue(tagName) );
}
public boolean getBooleanValue( String tagName )
{
return Boolean.parseBoolean( getTextValue(tagName) );
}
/**
* Method to get the string value of an element identified by tag
* @param ele
* @param tagName
* @return
*/
private static String getTextValue(Element element, String tagName) {
String textVal = null;
NodeList list = element.getElementsByTagName(tagName);
if (list != null && list.getLength() > 0) {
Element elem = (Element) list.item(0);
textVal = elem.getFirstChild().getNodeValue();
}
return textVal;
}
/**
* Method to update the string value of an element identified by tagname
* @param ele
* @param tagName
* @param newTextValue
*/
private static void setTextValue(Element element, String tagName, String newTextValue) {
NodeList list = element.getElementsByTagName(tagName);
if (list != null && list.getLength() > 0) {
Element elem = (Element) list.item(0);
elem.getFirstChild().setNodeValue(newTextValue);
}
}
/**
* Method to update the key value
* @param paramName
* @param paramValue
*/
public void setTextValue(String paramName, String paramValue) throws DMLRuntimeException {
if(_xmlRoot != null)
DMLConfig.setTextValue(_xmlRoot, paramName, paramValue);
else {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setIgnoringComments(true); //ignore XML comments
DocumentBuilder builder;
try {
builder = factory.newDocumentBuilder();
String configString = "<root><" + paramName + ">"+paramValue+"</" + paramName + "></root>";
Document domTree = builder.parse(new ByteArrayInputStream(configString.getBytes("UTF-8")));
_xmlRoot = domTree.getDocumentElement();
} catch (Exception e) {
throw new DMLRuntimeException("Unable to set config value", e);
}
}
}
/**
* Get a map of key/value pairs of all configurations w/ the prefix 'mapred'
* or 'mapreduce'.
*
* @return
*/
public Map<String, String> getCustomMRConfig()
{
HashMap<String, String> ret = new HashMap<String, String>();
//check for non-existing config xml tree
if( _xmlRoot == null )
return ret;
//get all mapred.* and mapreduce.* tag / value pairs
NodeList list = _xmlRoot.getElementsByTagName("*");
for( int i=0; list!=null && i<list.getLength(); i++ ) {
if( list.item(i) instanceof Element &&
( ((Element)list.item(i)).getNodeName().startsWith(PREFIX_MAPRED)
|| ((Element)list.item(i)).getNodeName().startsWith(PREFIX_MAPREDUCE)) )
{
Element elem = (Element) list.item(i);
ret.put(elem.getNodeName(),
elem.getFirstChild().getNodeValue());
}
}
return ret;
}
/**
*
* @return
* @throws DMLRuntimeException
*/
public synchronized String serializeDMLConfig()
throws DMLRuntimeException
{
String ret = null;
try
{
Transformer transformer = TransformerFactory.newInstance().newTransformer();
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
//transformer.setOutputProperty(OutputKeys.INDENT, "yes");
StreamResult result = new StreamResult(new StringWriter());
DOMSource source = new DOMSource(_xmlRoot);
transformer.transform(source, result);
ret = result.getWriter().toString();
}
catch(Exception ex)
{
throw new DMLRuntimeException("Unable to serialize DML config.", ex);
}
return ret;
}
/**
*
* @param content
* @return
* @throws DMLRuntimeException
*/
public static DMLConfig parseDMLConfig( String content )
throws DMLRuntimeException
{
DMLConfig ret = null;
try
{
//System.out.println(content);
DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
Document domTree = null;
domTree = builder.parse( new ByteArrayInputStream(content.getBytes("utf-8")) );
Element root = domTree.getDocumentElement();
ret = new DMLConfig( root );
}
catch(Exception ex)
{
throw new DMLRuntimeException("Unable to parse DML config.", ex);
}
return ret;
}
/**
* Start with the internal default settings, then merge in the
* settings from any specified configuration file, if available.
* If it is not explicitly given, then merge in settings from
* the default configuration file location, if available.
*
* @param configPath User-defined path of the configuration file.
* @return DMLConfig object containing configuration settings.
* @throws ParseException
* @throws FileNotFoundException
*/
public static DMLConfig readConfigurationFile(String configPath)
throws ParseException, FileNotFoundException
{
// Always start with the internal defaults
DMLConfig config = new DMLConfig();
// Merge in any specified or default configs if available
if (configPath != null) {
// specified
try {
config = new DMLConfig(configPath, false);
} catch (FileNotFoundException fnfe) {
LOG.error("Custom config file " + configPath + " not found.");
throw fnfe;
} catch (ParseException e) {
throw e;
}
} else {
// default
try {
config = new DMLConfig(DEFAULT_SYSTEMML_CONFIG_FILEPATH, false);
} catch (FileNotFoundException fnfe) {
LOG.info("Using internal default configuration settings. If you wish to " +
"customize any settings, please supply a `SystemML-config.xml` file.");
config = new DMLConfig();
} catch (ParseException e) {
throw e;
}
}
return config;
}
/**
*
* @return
*/
public String getConfigInfo()
{
String[] tmpConfig = new String[] {
LOCAL_TMP_DIR,SCRATCH_SPACE,OPTIMIZATION_LEVEL,
NUM_REDUCERS, DEFAULT_BLOCK_SIZE,
YARN_APPMASTER, YARN_APPMASTERMEM, YARN_MAPREDUCEMEM,
CP_PARALLEL_MATRIXMULT, CP_PARALLEL_TEXTIO
};
StringBuilder sb = new StringBuilder();
for( String tmp : tmpConfig )
{
sb.append("INFO: ");
sb.append(tmp);
sb.append(": ");
sb.append(getTextValue(tmp));
sb.append("\n");
}
return sb.toString();
}
/**
*
* @param amMem
* @param mrMem
*/
public void updateYarnMemorySettings(String amMem, String mrMem)
{
//app master memory
NodeList list1 = _xmlRoot.getElementsByTagName(YARN_APPMASTERMEM);
if (list1 != null && list1.getLength() > 0) {
Element elem = (Element) list1.item(0);
elem.getFirstChild().setNodeValue(String.valueOf(amMem));
}
//mapreduce memory
NodeList list2 = _xmlRoot.getElementsByTagName(YARN_MAPREDUCEMEM);
if (list2 != null && list2.getLength() > 0) {
Element elem = (Element) list2.item(0);
elem.getFirstChild().setNodeValue(String.valueOf(mrMem));
}
}
/**
*
* @throws IOException
*/
@SuppressWarnings("deprecation")
public void makeQualifiedScratchSpacePath()
throws IOException
{
NodeList list2 = _xmlRoot.getElementsByTagName(SCRATCH_SPACE);
if (list2 != null && list2.getLength() > 0) {
Element elem = (Element) list2.item(0);
FileSystem fs = FileSystem.get(ConfigurationManager.getCachedJobConf());
String fname = elem.getFirstChild().getNodeValue();
Path path = new Path(fname).makeQualified(fs);
elem.getFirstChild().setNodeValue(path.toString());
}
}
/**
*
* @param key
* @return
*/
public static String getDefaultTextValue( String key ) {
return _defaultVals.get( key );
}
/**
*
*/
public DMLConfig clone() {
DMLConfig conf = new DMLConfig();
conf._fileName = _fileName;
conf._xmlRoot = (Element) _xmlRoot.cloneNode(true);
return conf;
}
}