package org.apache.cassandra.config;
/*
*
* 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.
*
*/
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.xpath.XPathExpressionException;
import org.apache.cassandra.auth.SimpleAuthenticator;
import org.apache.cassandra.auth.SimpleAuthority;
import org.apache.cassandra.db.ColumnFamilyType;
import org.apache.cassandra.utils.FBUtilities;
import org.apache.cassandra.utils.SkipNullRepresenter;
import org.apache.cassandra.utils.XMLUtils;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import org.yaml.snakeyaml.Dumper;
import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.nodes.Tag;
/**
* @deprecated Yaml configuration for Keyspaces and ColumnFamilies is deprecated in 0.7
*/
public class Converter
{
private static Config conf = new Config();
private static List<RawKeyspace> readTablesFromXml(XMLUtils xmlUtils) throws ConfigurationException
{
List<RawKeyspace> keyspaces = new ArrayList<RawKeyspace>();
/* Read the table related stuff from config */
try
{
String endPointSnitchClassName = null; // Used as a sentinel. EPS cannot be undefined in 0.6.
NodeList tablesxml = xmlUtils.getRequestedNodeList("/Storage/Keyspaces/Keyspace");
// Retrieve values that were previously global.
String gcGrace = xmlUtils.getNodeValue("/Storage/GCGraceSeconds");
int gc_grace_seconds = CFMetaData.DEFAULT_GC_GRACE_SECONDS;
if ( gcGrace != null )
gc_grace_seconds = Integer.parseInt(gcGrace);
String lifetime = xmlUtils.getNodeValue("/Storage/MemtableFlushAfterMinutes");
int memtime = CFMetaData.DEFAULT_MEMTABLE_LIFETIME_IN_MINS;
if (lifetime != null)
memtime = Integer.parseInt(lifetime);
String memtableSize = xmlUtils.getNodeValue("/Storage/MemtableThroughputInMB");
int memsize = CFMetaData.DEFAULT_MEMTABLE_THROUGHPUT_IN_MB;
if (memtableSize != null)
memsize = Integer.parseInt(memtableSize);
String memtableOps = xmlUtils.getNodeValue("/Storage/MemtableOperationsInMillions");
double memops = CFMetaData.DEFAULT_MEMTABLE_OPERATIONS_IN_MILLIONS;
if (memtableOps != null)
memops = Double.parseDouble(memtableOps);
int size = tablesxml.getLength();
for ( int i = 0; i < size; ++i )
{
String value;
RawKeyspace ks = new RawKeyspace();
Node table = tablesxml.item(i);
/* parsing out the table ksName */
ks.name = XMLUtils.getAttributeValue(table, "Name");
value = xmlUtils.getNodeValue("/Storage/Keyspaces/Keyspace[@Name='" + ks.name + "']/EndPointSnitch");
if (endPointSnitchClassName == null) {
endPointSnitchClassName = value;
}
else if (!endPointSnitchClassName.equals(value)) {
throw new ConfigurationException("ERROR : EndPointSnitch is global in 0.7 -- multiple choices present.");
}
ks.replica_placement_strategy = xmlUtils.getNodeValue("/Storage/Keyspaces/Keyspace[@Name='" + ks.name + "']/ReplicaPlacementStrategy");
/* Data replication factor */
value = xmlUtils.getNodeValue("/Storage/Keyspaces/Keyspace[@Name='" + ks.name + "']/ReplicationFactor");
if (value != null)
{
ks.replication_factor = Integer.parseInt(value);
}
String xqlTable = "/Storage/Keyspaces/Keyspace[@Name='" + ks.name + "']/";
NodeList columnFamilies = xmlUtils.getRequestedNodeList(xqlTable + "ColumnFamily");
int size2 = columnFamilies.getLength();
ks.column_families = new RawColumnFamily[size2];
for ( int j = 0; j < size2; ++j )
{
Node columnFamily = columnFamilies.item(j);
ks.column_families[j] = new RawColumnFamily();
ks.column_families[j].name = XMLUtils.getAttributeValue(columnFamily, "Name");
String xqlCF = xqlTable + "ColumnFamily[@Name='" + ks.column_families[j].name + "']/";
ks.column_families[j].column_type = ColumnFamilyType.create(XMLUtils.getAttributeValue(columnFamily, "ColumnType"));
ks.column_families[j].compare_with = XMLUtils.getAttributeValue(columnFamily, "CompareWith");
if (ks.column_families[j].column_type != null && ks.column_families[j].column_type == ColumnFamilyType.Super)
ks.column_families[j].compare_subcolumns_with = XMLUtils.getAttributeValue(columnFamily, "CompareSubcolumnsWith");
if ((value = XMLUtils.getAttributeValue(columnFamily, "KeysCached")) != null)
{
ks.column_families[j].keys_cached = FBUtilities.parseDoubleOrPercent(value);
}
if ((value = XMLUtils.getAttributeValue(columnFamily, "RowsCached")) != null)
{
ks.column_families[j].rows_cached = FBUtilities.parseDoubleOrPercent(value);
}
if ((value = XMLUtils.getAttributeValue(columnFamily, "RowCacheSavePeriodInSeconds")) != null)
{
ks.column_families[j].row_cache_save_period_in_seconds = Integer.parseInt(value);
}
if ((value = XMLUtils.getAttributeValue(columnFamily, "KeyCacheSavePeriodInSeconds")) != null)
{
ks.column_families[j].key_cache_save_period_in_seconds = Integer.parseInt(value);
}
if ((value = XMLUtils.getAttributeValue(columnFamily, "ReadRepairChance")) != null)
{
ks.column_families[j].read_repair_chance = FBUtilities.parseDoubleOrPercent(value);
}
// Values that were previously global and are now per-CF go here.
ks.column_families[j].gc_grace_seconds = gc_grace_seconds;
ks.column_families[j].memtable_flush_after_mins = memtime;
ks.column_families[j].memtable_throughput_in_mb = memsize;
ks.column_families[j].memtable_operations_in_millions = memops;
ks.column_families[j].comment = xmlUtils.getNodeValue(xqlCF + "Comment");
}
keyspaces.add(ks);
}
if (endPointSnitchClassName.equals("org.apache.cassandra.locator.EndPointSnitch")) {
endPointSnitchClassName = "org.apache.cassandra.locator.RackInferringSnitch";
System.out.println("WARN : org.apache.cassandra.locator.EndPointSnitch has been replaced by org.apache.cassandra.locator.RackInferringSnitch");
}
else if (endPointSnitchClassName.equals("org.apache.cassandra.locator.PropertyFileEndpointSnitch")) {
endPointSnitchClassName = "org.apache.cassandra.locator.PropertyFileSnitch";
System.out.println("WARN : org.apache.cassandra.locator.PropertyFileEndpointSnich has been replaced by org.apache.cassandra.locator.PropertyFileSnitch");
}
else {
System.out.println("INFO : EndPointSnitch is global in 0.7 and may need to be updated.");
}
conf.endpoint_snitch = endPointSnitchClassName;
return keyspaces;
}
catch (XPathExpressionException e)
{
throw new ConfigurationException("XPath expression error.");
}
catch (TransformerException e)
{
throw new ConfigurationException("Error occurred during the transformation process.");
}
}
private static void loadPreviousConfig(String config) throws ConfigurationException
{
try {
XMLUtils xmlUtils = new XMLUtils(config);
conf.cluster_name = xmlUtils.getNodeValue("/Storage/ClusterName");
String syncRaw = xmlUtils.getNodeValue("/Storage/CommitLogSync");
conf.commitlog_sync = Config.CommitLogSync.valueOf(syncRaw);
if (conf.commitlog_sync != null)
{
if (conf.commitlog_sync == Config.CommitLogSync.batch)
conf.commitlog_sync_batch_window_in_ms = Double.valueOf(xmlUtils.getNodeValue("/Storage/CommitLogSyncBatchWindowInMS"));
else
conf.commitlog_sync_period_in_ms = Integer.valueOf(xmlUtils.getNodeValue("/Storage/CommitLogSyncPeriodInMS"));
}
String modeRaw = xmlUtils.getNodeValue("/Storage/DiskAccessMode");
conf.disk_access_mode = Config.DiskAccessMode.valueOf(modeRaw);
conf.authenticator = xmlUtils.getNodeValue("/Storage/Authenticator");
// handle the authc/authz split by configuring SimpleAuthority if SimpleAuthenticator is in use
if (conf.authenticator != null && conf.authenticator.equals(SimpleAuthenticator.class.getName()))
conf.authority = SimpleAuthority.class.getName();
/* Hashing strategy */
conf.partitioner = xmlUtils.getNodeValue("/Storage/Partitioner");
conf.job_tracker_host = xmlUtils.getNodeValue("/Storage/JobTrackerHost");
conf.job_jar_file_location = xmlUtils.getNodeValue("/Storage/JobJarFileLocation");
conf.initial_token = xmlUtils.getNodeValue("/Storage/InitialToken");
String rpcTimeout = xmlUtils.getNodeValue("/Storage/RpcTimeoutInMillis");
if ( rpcTimeout != null )
conf.rpc_timeout_in_ms = Long.parseLong(rpcTimeout);
String rawReaders = xmlUtils.getNodeValue("/Storage/ConcurrentReads");
if (rawReaders != null)
{
conf.concurrent_reads = Integer.parseInt(rawReaders);
}
String rawWriters = xmlUtils.getNodeValue("/Storage/ConcurrentWrites");
if (rawWriters != null)
{
conf.concurrent_writes = Integer.parseInt(rawWriters);
}
String rawSlicedBuffer = xmlUtils.getNodeValue("/Storage/SlicedBufferSizeInKB");
if (rawSlicedBuffer != null)
{
conf.sliced_buffer_size_in_kb = Integer.parseInt(rawSlicedBuffer);
}
String bmtThresh = xmlUtils.getNodeValue("/Storage/BinaryMemtableThroughputInMB");
if (bmtThresh != null)
{
conf.binary_memtable_throughput_in_mb = Integer.parseInt(bmtThresh);
}
/* TCP port on which the storage system listens */
String port = xmlUtils.getNodeValue("/Storage/StoragePort");
if ( port != null )
conf.storage_port = Integer.parseInt(port);
/* Local IP or hostname to bind services to */
conf.listen_address = xmlUtils.getNodeValue("/Storage/ListenAddress");
conf.rpc_address = xmlUtils.getNodeValue("/Storage/RPCAddress");
port = xmlUtils.getNodeValue("/Storage/RPCPort");
if (port != null)
conf.rpc_port = Integer.parseInt(port);
String framedRaw = xmlUtils.getNodeValue("/Storage/ThriftFramedTransport");
if (framedRaw != null && !Boolean.valueOf(framedRaw))
{
System.out.println("WARN : Cassandra uses a Thrift framed Transport by default in 0.7! Clients will need to match.");
}
conf.thrift_framed_transport_size_in_mb = 15;
String sbc = xmlUtils.getNodeValue("/Storage/SnapshotBeforeCompaction");
if (sbc != null)
{
conf.snapshot_before_compaction = Boolean.valueOf(sbc);
}
String autoBootstr = xmlUtils.getNodeValue("/Storage/AutoBootstrap");
if (autoBootstr != null)
{
conf.auto_bootstrap = Boolean.valueOf(autoBootstr);
}
String columnIndexSize = xmlUtils.getNodeValue("/Storage/ColumnIndexSizeInKB");
if(columnIndexSize != null)
{
conf.column_index_size_in_kb = Integer.parseInt(columnIndexSize);
}
conf.data_file_directories = xmlUtils.getNodeValues("/Storage/DataFileDirectories/DataFileDirectory");
conf.commitlog_directory = xmlUtils.getNodeValue("/Storage/CommitLogDirectory");
conf.saved_caches_directory = xmlUtils.getNodeValue("/Storage/SavedCachesDirectory");
String value = xmlUtils.getNodeValue("/Storage/CommitLogRotationThresholdInMB");
if ( value != null)
conf.commitlog_rotation_threshold_in_mb = Integer.parseInt(value);
conf.seeds = xmlUtils.getNodeValues("/Storage/Seeds/Seed");
conf.keyspaces = readTablesFromXml(xmlUtils);
}
catch (ParserConfigurationException e) {
System.out.println("Parser error during previous config load.");
throw new ConfigurationException("Parser error during previous config load.");
} catch (SAXException e) {
System.out.println("SAX error during previous config load.");
throw new ConfigurationException("SAX error during previous config load.");
} catch (IOException e) {
System.out.println("File I/O error during previous config load.");
throw new ConfigurationException("File I/O error during previous config load.");
} catch (XPathExpressionException e) {
System.out.println("XPath error during previous config load.");
throw new ConfigurationException("XPath error during previous config load.");
}
}
private static void dumpConfig(String outfile) throws IOException
{
DumperOptions options = new DumperOptions();
/* Use a block YAML arrangement */
options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
SkipNullRepresenter representer = new SkipNullRepresenter();
/* Use Tag.MAP to avoid the class name being included as global tag */
representer.addClassTag(Config.class, Tag.MAP);
representer.addClassTag(RawColumnFamily.class, Tag.MAP);
Dumper dumper = new Dumper(representer, options);
Yaml yaml = new Yaml(dumper);
String output = yaml.dump(conf);
/* Write to output file */
BufferedWriter out = new BufferedWriter(new FileWriter(outfile));
out.write("# Cassandra YAML generated from previous config\n");
out.write("# Configuration wiki: http://wiki.apache.org/cassandra/StorageConfiguration\n");
out.write(output);
out.close();
}
public static void main(String[] args) throws Exception
{
if (args.length != 2)
{
throw new IllegalArgumentException("usage: config-converter oldfile newfile");
}
String oldConfigName = args[0];
String newConfigName = args[1];
if (!new File(oldConfigName).exists())
throw new IllegalArgumentException(String.format("%s does not exist", oldConfigName));
loadPreviousConfig(oldConfigName);
dumpConfig(newConfigName);
}
}