package com.tesora.dve.tools;
/*
* #%L
* Tesora Inc.
* Database Virtualization Engine
* %%
* Copyright (C) 2011 - 2014 Tesora Inc.
* %%
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
import java.io.File;
import java.io.FilenameFilter;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.Scanner;
import java.util.Set;
import javax.management.MBeanAttributeInfo;
import javax.management.MBeanInfo;
import javax.management.MBeanOperationInfo;
import javax.management.MBeanParameterInfo;
import javax.management.ObjectName;
import com.tesora.dve.server.bootstrap.Host;
import com.tesora.dve.server.global.HostService;
import com.tesora.dve.singleton.Singletons;
import org.apache.commons.lang.StringEscapeUtils;
import org.apache.commons.lang.StringUtils;
import com.tesora.dve.common.DBHelper;
import com.tesora.dve.common.PEConstants;
import com.tesora.dve.common.PECryptoUtils;
import com.tesora.dve.common.PEFileUtils;
import com.tesora.dve.common.PEUrl;
import com.tesora.dve.common.PEXmlUtils;
import com.tesora.dve.common.SiteInstanceStatus;
import com.tesora.dve.common.catalog.DynamicPolicy;
import com.tesora.dve.common.catalog.ExternalService;
import com.tesora.dve.common.catalog.PersistentGroup;
import com.tesora.dve.common.catalog.PersistentSite;
import com.tesora.dve.common.catalog.Provider;
import com.tesora.dve.common.catalog.SiteInstance;
import com.tesora.dve.common.catalog.VariableConfig;
import com.tesora.dve.exceptions.PEException;
import com.tesora.dve.groupmanager.HazelcastCoordinationServices;
import com.tesora.dve.groupmanager.LocalhostCoordinationServices;
import com.tesora.dve.siteprovider.onpremise.jaxb.OnPremiseSiteProviderConfig;
import com.tesora.dve.sql.schema.VariableScopeKind;
import com.tesora.dve.sql.template.jaxb.Template;
import com.tesora.dve.sql.transexec.CatalogHelper;
import com.tesora.dve.standalone.Main;
import com.tesora.dve.tools.field.FieldAction;
import com.tesora.dve.tools.field.FieldActionManager;
import com.tesora.dve.tools.jaxb.persistent.PersistentConfig;
import com.tesora.dve.tools.jaxb.persistent.PersistentGroupCfgList;
import com.tesora.dve.tools.jaxb.persistent.PersistentSiteCfgList;
import com.tesora.dve.tools.jaxb.policy.PolicyConfig;
import com.tesora.dve.upgrade.CatalogSchemaVersion;
import com.tesora.dve.upgrade.CatalogVersions;
import com.tesora.dve.variable.VariableConstants;
public class DVEConfigCLI extends CLIBuilder {
private static final String PERSISTENT_SCHEMA = "persistent.xsd";
private static final String POLICY_SCHEMA = "policy.xsd";
private static final String ONPREMISE_SITE_PROVIDER_SCHEMA = "onPremiseSiteProvider.xsd";
private static final String TEMPLATE_SCHEMA = "template.xsd";
private static final String DEFAULT_JMX_HOST = "127.0.0.1";
private static final Integer DEFAULT_JMX_PORT = 1234;
private static final FilenameFilter TEMPLATE_FILE_NAME_FILTER = new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
return name.toLowerCase().endsWith(DVEAnalyzerCLI.TEMPLATE_FILE_EXTENSION);
}
};
private CatalogHelper catHelper;
private DBHelper dbHelper;
public DVEConfigCLI(String[] args) throws PEException {
super(args, "Configuration Tool");
registerCommand(new CommandType(new String[] { "locate", "catalog" }, "<url> <user> <password> [<database>]",
"Specify the location of the DVE catalog and the user / password to use when connecting."));
registerCommand(new CommandType(new String[] { "encrypt" }, "<text>", "Internal - Encrypt the specified text.",
true));
registerCommand(new CommandType(new String[] { "setup", "catalog" },
"<persistent file> <dynamic file> <policy file>",
"Create an initial catalog required to run the server using the specified files for configuration."));
registerCommand(new CommandType(new String[] { "setup", "localhost", "catalog" },
"[<num persistent site>] [<num dynamic sites>]",
"Create an initial catalog required to run the server with the specified number of sites on localhost."));
registerCommand(new CommandType(new String[] { "delete", "catalog" }, "Delete the DVE catalog."));
registerCommand(new CommandType(new String[] { "upgrade", "catalog" }, "Upgrade the catalog."));
registerCommand(new CommandType(new String[] { "check", "catalog", "version" }, "Check the catalog version."));
registerCommand(new CommandType(new String[] { "truncate", "servers" },
"Remove all registered servers from the catalog."));
registerCommand(new CommandType(new String[] { "set", "root", "user" }, "<user> <password>",
"Set the username and password for the root user"));
registerCommand(new CommandType(new String[] { "set", "default", "pg" }, "<name>",
"Set the default persistent group."));
registerCommand(new CommandType(new String[] { "show", "pg" }, "<name>",
"Show the contents of the specified persistent group."));
registerCommand(new CommandType(new String[] { "add", "provider" }, "<name> <plugin> [<filename>]",
"Add a new provider with the specified plugin and configuration."));
registerCommand(new CommandType(new String[] { "configure", "provider" }, "<name> <filename>",
"Configure the specified dynamic site provider using the contents of the file."));
registerCommand(new CommandType(new String[] { "show", "provider" }, "<name>",
"Show the configuration for the specified dynamic site provider."));
registerCommand(new CommandType(new String[] { "remove", "provider" }, "<name>",
"Remove the specified dynamic site provider."));
registerCommand(new CommandType(new String[] { "add", "policy" }, "<filename>",
"Add a new dynamic policy with the specifed configuration."));
registerCommand(new CommandType(new String[] { "configure", "policy" }, "<filename>",
"Configure the specified dynamic policy using the contents of the file."));
registerCommand(new CommandType(new String[] { "show", "policy" }, "<name>",
"Show the configuration for the specified dynamic policy."));
registerCommand(new CommandType(new String[] { "set", "default", "policy" }, "<name>", "Set the default policy"));
registerCommand(new CommandType(new String[] { "set", "groupService" }, "<Hazelcast | Localhost>",
"Set the group service."));
registerCommand(new CommandType(new String[] { "add", "es" },
"<name> <plugin> <use data store> [<connect user>] [<filename>]",
"Add a new external service with the specified plugin and configuration."));
registerCommand(new CommandType(new String[] { "configure", "es" }, "<name> <filename>",
"Configure the specified external service using the contents of the file."));
registerCommand(new CommandType(new String[] { "show", "es" }, "<name>",
"Show the configuration for the specified external service."));
registerCommand(new CommandType(new String[] { "remove", "es" }, "<name>",
"Remove the specified external service."));
registerCommand(new CommandType(new String[] { "dump" }, "[verbose]", "Display the contents of the catalog."));
registerCommand(new CommandType(new String[] { "action", "list" }, "List all available action modules.", true));
registerCommand(new CommandType(new String[] { "action", "report" }, "<name>",
"Run the specified action report.", true));
registerCommand(new CommandType(new String[] { "action", "run" }, "<name>", "Run the specified action process.",
true));
// The following commands require an active connection to the DVE server.
registerCommand(new CommandType(new String[] { "connect" }, "[<url> <user> <password>]",
"Connect to a DVE server (default=" + PEConstants.MYSQL_URL + " " + PEConstants.ROOT + " "
+ PEConstants.PASSWORD + ")"));
registerCommand(new CommandType(new String[] { "disconnect" }, "Disconnect from a DVE server."));
registerCommand(new CommandType(new String[] { "add", "template" }, "<template file> [<match>]",
"Add a new template to the server using the specified file."));
registerCommand(new CommandType(new String[] { "add", "templates" }, "<templates folder>",
"Add a new set of templates to the server from the specified folder."));
registerCommand(new CommandType(new String[] { "update", "template" }, "<template file> [<match>]",
"Alter an existing template using the specified file."));
registerCommand(new CommandType(new String[] { "update", "templates" }, "<template folder>",
"Alter a set of existing template from the specified folder."));
registerJMXCommands();
try {
catHelper = new CatalogHelper(Main.class);
} catch (final Exception e) {
throw new PEException("Failed to create catalog helper - " + e.getMessage(), e);
}
}
protected CatalogHelper getCatalogHelper() {
return catHelper;
}
@Override
public void close() {
if (catHelper != null) {
catHelper.close();
catHelper = null;
}
super.close();
}
public void cmd_dump(Scanner scanner) throws PEException {
final boolean verbose = StringUtils.equalsIgnoreCase("VERBOSE", scan(scanner));
printlnDots("Generating " + (verbose ? "verbose " : "") + "dump of catalog");
try (final StringWriter sw = new StringWriter(); final PrintWriter pw = new PrintWriter(sw)) {
catHelper.dumpCatalogInfo(pw, verbose);
println(sw.toString());
} catch (final PEException e) {
throw e;
} catch (final Exception e) {
throw new PEException("Failed to generate dump - " + e.getMessage(), e);
}
}
public void cmd_locate_catalog(Scanner scanner) throws PEException {
if (scanner.hasNext()) {
final String url = buildValidCatalogUrlFromLocation(scan(scanner, "url"));
final String user = scan(scanner, "user");
final String password = scan(scanner, "password");
final String database = scan(scanner);
final DBHelper dbHelper = new DBHelper(url, user, password);
try {
dbHelper.connect();
} catch (final Exception e) {
throw new PEException("Failed to connect to DVE catalog database at '" + url + "' - "
+ e.getMessage(), e);
} finally {
dbHelper.disconnect();
}
try {
final Properties serverProps = PEFileUtils.loadPropertiesFromClasspath(Main.class,
PEConstants.SERVER_FILE_NAME);
serverProps.setProperty(DBHelper.CONN_URL, url);
serverProps.setProperty(DBHelper.CONN_USER, user);
serverProps.setProperty(DBHelper.CONN_PASSWORD, password);
serverProps.setProperty(DBHelper.CONN_DBNAME, (database != null) ? database : PEConstants.CATALOG);
PEFileUtils.savePropertiesToClasspath(Main.class, PEConstants.SERVER_FILE_NAME, serverProps);
printlnDots("Updating properties file at: "
+ PEFileUtils.convert(PEFileUtils.getClasspathURL(Main.class, PEConstants.SERVER_FILE_NAME)));
} catch (final Exception e) {
throw new PEException("Failed to update '" + PEConstants.SERVER_FILE_NAME + "' - " + e.getMessage(), e);
}
try {
catHelper.close();
catHelper = new CatalogHelper(Main.class);
} catch (final Exception e) {
throw new PEException("Failed to re-create catalog helper - " + e.getMessage(), e);
}
}
final Properties props = catHelper.getProperties();
printlnDots("DVE catalog location settings are:");
printlnIndent("URL = " + props.getProperty(DBHelper.CONN_URL, ""));
printlnIndent("User = " + props.getProperty(DBHelper.CONN_USER, ""));
printlnIndent("Password = " + props.getProperty(DBHelper.CONN_PASSWORD, ""));
printlnIndent("Database = " + props.getProperty(DBHelper.CONN_DBNAME, ""));
}
private String buildValidCatalogUrlFromLocation(final String catalogLocation) throws PEException {
PEUrl.isValidUrlWithPort(catalogLocation);
final PEUrl urlBuilder = PEUrl.fromUrlString(catalogLocation);
urlBuilder.setQueryOption("useUnicode", "true");
urlBuilder.setQueryOption("characterEncoding", "utf8");
urlBuilder.setQueryOption("connectionCollation", "utf8_general_ci");
return urlBuilder.getURL();
}
public void cmd_encrypt(Scanner scanner) throws PEException {
final String value = scan(scanner, "text");
printlnDots("encrypt '" + value + "' as '" + PECryptoUtils.encrypt(value));
}
public void cmd_delete_catalog() throws PEException {
final Properties props = catHelper.getProperties();
final String catalogLocation = props.getProperty(DBHelper.CONN_URL, "");
if (StringUtils.isBlank(catalogLocation)) {
throw new PEException("Catalog location not set. Use the locate command to specify catalog location first.");
}
println("You are about to completely remove the DVE catalog located at " + catalogLocation);
askQuestion("Do you want to continue [y/N]? ", new QuestionCallback() {
@Override
public void answer(String line) throws PEException {
if ((line != null) && (line.equalsIgnoreCase("y") || line.equalsIgnoreCase("yes"))) {
catHelper.deleteCatalog();
printlnDots("DVE catalog deleted from '" + catalogLocation + "'");
}
}
});
}
public void cmd_set_root_user(Scanner scanner) throws PEException {
final String user = scan(scanner, "user");
final String password = scan(scanner, "password");
catHelper.setRootCredentials(user, password);
}
public void cmd_setup_localhost_catalog(Scanner scanner) throws PEException {
Integer persistent = scanInteger(scanner);
Integer dynamic = scanInteger(scanner);
if (persistent == null) {
persistent = 1;
}
if (dynamic == null) {
dynamic = 1;
}
printlnDots("Generating localhost catalog with " + persistent + " persistent site(s) and " + dynamic
+ " dynamic site(s)");
catHelper.createLocalhostCatalog("OnPremise", PEConstants.DEFAULT_GROUP_NAME, persistent, dynamic);
printlnDots("DVE catalog created at '" + catHelper.getCatalogDatabaseLocation() + "'");
}
public void cmd_setup_catalog(Scanner scanner) throws PEException {
final File persistentFile = scanFile(scanner, "persistent sites");
final File dynamicFile = scanFile(scanner, "dynamic sites");
final File policyFile = scanFile(scanner, "policy");
final String persistentContent = PEFileUtils.readToString(persistentFile);
final String dynamicContent = PEFileUtils.readToString(dynamicFile);
final String policyContent = PEFileUtils.readToString(policyFile);
// Unmarshall XML for persistent site configuration.
final PersistentConfig persistentConfig = PEXmlUtils.unmarshalJAXB(persistentContent,
PersistentConfig.class, PEFileUtils.getClasspathURL(PersistentConfig.class, PERSISTENT_SCHEMA));
// Create in memory representations of sites and groups.
final List<PersistentSite> sites = createPersistentSites(persistentConfig);
final List<PersistentGroup> groups = createPersistentGroups(persistentConfig, sites);
// Determine default Persistent Group to set.
PersistentGroup defaultGroup = null;
if ((persistentConfig.getPersistentGroups() != null)
&& (persistentConfig.getPersistentGroups().getDefaultGroup() != null)) {
// Make sure it exists in the group list we created above.
for (final PersistentGroup group : groups) {
if (persistentConfig.getPersistentGroups().getDefaultGroup().equals(group.getName())) {
defaultGroup = group;
break;
}
}
if (defaultGroup == null) {
throw new PEException("Persistent group specified a default group that doesn't exist");
}
} else {
defaultGroup = groups.get(0);
}
// Unmarshall XML for policy configuration.
final DynamicPolicy policy = parsePolicyConfig(PEXmlUtils.unmarshalJAXB(policyContent, PolicyConfig.class,
PEFileUtils.getClasspathURL(PolicyConfig.class, POLICY_SCHEMA)));
OnPremiseSiteProviderConfig dynamic = PEXmlUtils.unmarshalJAXB(dynamicContent, OnPremiseSiteProviderConfig.class,
PEFileUtils.getClasspathURL(OnPremiseSiteProviderConfig.class, ONPREMISE_SITE_PROVIDER_SCHEMA));
// Encrypt any passwords.
dynamic = CatalogHelper.encryptProviderPasswords(dynamic);
printlnDots("Generating DVE catalog");
catHelper.createStandardCatalog(sites, groups, defaultGroup, dynamic, policy);
printlnDots("DVE catalog created at '" + catHelper.getCatalogDatabaseLocation() + "'");
}
private List<PersistentSite> createPersistentSites(PersistentConfig config) throws PEException {
final List<PersistentSite> sites = new ArrayList<PersistentSite>();
if ((config.getPersistentSites() == null) || (config.getPersistentSites().getSite() == null)
|| config.getPersistentSites().getSite().isEmpty()) {
throw new PEException("Persistent site list needs to contain at least one persistent site");
}
for (final PersistentSiteCfgList.Site siteCfg : config.getPersistentSites().getSite()) {
if (!PersistentSite.isValidHAType(siteCfg.getHaMode())) {
throw new PEException("Persistent site contains an invalid haMode '" + siteCfg.getHaMode() + "'");
}
final PersistentSite site = new PersistentSite(siteCfg.getName(), siteCfg.getHaMode());
// Create all of the site instances.
SiteInstance master = null;
for (final PersistentSiteCfgList.Site.SiteInstance inst : siteCfg.getSiteInstance()) {
// Initially we will create all site as non-masters.
final SiteInstance i = new SiteInstance(inst.getName(), inst.getUrl(), inst.getUser(), inst.getPassword(),
false, inst.isEnabled() ? SiteInstanceStatus.ONLINE.name() : SiteInstanceStatus.OFFLINE.name());
site.addInstance(i);
// If the configuration specifies a master then remember it.
if ((inst.isMaster() != null) && inst.isMaster()) {
if (!inst.isEnabled()) {
throw new PEException("Can't specify a disabled site instance as master for site '"
+ site.getName() + "'");
}
// Unless we already have a master specified.
if (master != null) {
throw new PEException("Multiple instances specified as master for site '" + site.getName()
+ "'");
}
master = i;
}
}
// If we don't have a master defined then simply use the first
// available.
if (master == null) {
for (final SiteInstance inst : site.getSiteInstances()) {
if (inst.isEnabled() && inst.isMaster()) {
master = inst;
break;
}
}
}
// If master is still null then we don't have a viable master.
if (master == null) {
throw new PEException("No master site instance available for site '" + site.getName() + "'");
}
site.setMasterInstance(master);
if (siteCfg.getHaMode().equalsIgnoreCase("single")) {
if (site.getSiteInstances().size() != 1) {
throw new PEException("Persistent site of haMode 'Single' should have exactly one site instance");
}
} else if (siteCfg.getHaMode().equalsIgnoreCase("mastermaster")) {
if (site.getSiteInstances().size() != 2) {
throw new PEException(
"Persistent site of haMode 'MasterMaster' should have exactly two site instances");
}
}
sites.add(site);
}
return sites;
}
private List<PersistentGroup> createPersistentGroups(PersistentConfig config, List<PersistentSite> sites)
throws PEException {
final List<PersistentGroup> groups = new ArrayList<PersistentGroup>();
if ((config.getPersistentGroups() == null) || (config.getPersistentGroups().getGroup() == null)
|| config.getPersistentGroups().getGroup().isEmpty()) {
// We don't have a groups declaration therefore we are going to
// create a default group containing all of the sites we know about.
final PersistentGroup group = new PersistentGroup(PEConstants.DEFAULT_GROUP_NAME);
group.addAllSites(sites);
groups.add(group);
} else {
for (final PersistentGroupCfgList.Group groupCfg : config.getPersistentGroups().getGroup()) {
final PersistentGroup group = new PersistentGroup(groupCfg.getName());
if (groupCfg.getSite().isEmpty()) {
throw new PEException("Persistent group contains a group with no sites");
}
for (final String siteName : groupCfg.getSite()) {
// Iterate over available PersistentSites looking for the
// one we want to add to the group.
boolean found = false;
for (final PersistentSite site : sites) {
if (siteName.equals(site.getName())) {
group.addStorageSite(site);
found = true;
break;
}
}
if (!found) {
throw new PEException("Persistent group contains site '" + siteName + "' that doesn't exist");
}
}
groups.add(group);
}
}
return groups;
}
public void cmd_set_default_pg(Scanner scanner) throws PEException {
final String name = scan(scanner, "name");
catHelper.setDefaultStorageGroup(name);
printlnDots("Default persistent group set to '" + name + "'");
}
public void cmd_show_pg(Scanner scanner) throws PEException {
final String name = scan(scanner, "name");
final PersistentGroup pg = catHelper.getStorageGroup(name);
final List<PersistentSite> sites = pg.getStorageSites();
printlnDots("Persistent group '" + pg.getName() + "' size=" + sites.size());
println("Sites:");
for (final PersistentSite site : sites) {
final StringBuffer siteInstances = new StringBuffer();
for (final SiteInstance siteInstance : site.getSiteInstances()) {
if (!siteInstance.isMaster()) {
if (siteInstances.length() > 0) {
siteInstances.append(", ");
}
siteInstances.append(siteInstance.getInstanceURL());
if (!siteInstance.isEnabled()) {
siteInstances.append("(disabled)");
}
}
}
final String instText = (siteInstances.length() > 0) ? ", [" + siteInstances.toString() + "]" : "";
println(site.getName() + ", " + site.getHAType() + ", " + site.getMasterUrl() + instText);
}
}
public void cmd_add_provider(Scanner scanner) throws PEException {
final String name = scan(scanner, "name");
final String plugin = scan(scanner, "plugin");
final File providerFile = scanFile(scanner);
final Provider provider = catHelper.createSiteProvider(name, plugin,
(providerFile == null) ? null : PEFileUtils.readToString(providerFile));
printlnDots("Site provider '" + provider.getName() + "' created");
}
public void cmd_configure_provider(Scanner scanner) throws PEException {
final String name = scan(scanner, "name");
final File providerFile = scanFile(scanner, "filename");
final Provider provider = catHelper.setSiteProviderConfig(name, PEFileUtils.readToString(providerFile));
printlnDots("Site provider '" + provider.getName() + "' configuration complete");
}
public void cmd_show_provider(Scanner scanner) throws PEException {
final String name = scan(scanner, "name");
final Provider provider = catHelper.getSiteProvider(name);
printlnDots("Site provider '" + provider.getName() + "' plugin='" + provider.getPlugin() + "' enabled="
+ provider.isEnabled());
println("Configuration:");
println(provider.getConfig());
}
public void cmd_remove_provider(Scanner scanner) throws PEException {
final String name = scan(scanner, "name");
catHelper.removeSiteProvider(name);
printlnDots("Site provider '" + name + "' removed");
}
public void cmd_add_policy(Scanner scanner) throws PEException {
final File policyFile = scanFile(scanner, "filename");
final String policyContent = PEFileUtils.readToString(policyFile);
final PolicyConfig policyConfig = PEXmlUtils.unmarshalJAXB(policyContent, PolicyConfig.class,
PEFileUtils.getClasspathURL(PolicyConfig.class, POLICY_SCHEMA));
final DynamicPolicy policy = parsePolicyConfig(policyConfig);
catHelper.createDynamicPolicy(policy);
printlnDots("Dynamic policy '" + policy.getName() + "' created");
}
public void cmd_configure_policy(Scanner scanner) throws PEException {
final File policyFile = scanFile(scanner, "filename");
final String policyContent = PEFileUtils.readToString(policyFile);
final PolicyConfig policyConfig = PEXmlUtils.unmarshalJAXB(policyContent, PolicyConfig.class,
PEFileUtils.getClasspathURL(PolicyConfig.class, POLICY_SCHEMA));
final DynamicPolicy policy = parsePolicyConfig(policyConfig);
catHelper.setDynamicPolicyConfig(policy);
printlnDots("Dynamic policy '" + policy.getName() + "' configuration complete");
}
public void cmd_show_policy(Scanner scanner) throws PEException {
final String name = scan(scanner, "name");
final DynamicPolicy policy = catHelper.getDynamicPolicy(name);
printlnDots("Dynamic policy '" + policy.getName() + "'");
println("Configuration:");
println("Aggregation : " + policy.getAggregationClass().toString());
println("Small : " + policy.getSmallClass().toString());
println("Medium : " + policy.getMediumClass().toString());
println("Large : " + policy.getLargeClass().toString());
}
public void cmd_set_default_policy(Scanner scanner) throws PEException {
final String name = scan(scanner, "name");
catHelper.setDefaultDynamicPolicy(name);
printlnDots("Default dynamic policy set to '" + name + "'");
}
public void cmd_set_groupService(Scanner scanner) throws PEException {
final String name = scan(scanner, "groupService");
String type = null;
if (name.equalsIgnoreCase(LocalhostCoordinationServices.TYPE)) {
type = LocalhostCoordinationServices.TYPE;
} else if (name.equalsIgnoreCase(HazelcastCoordinationServices.TYPE)) {
type = HazelcastCoordinationServices.TYPE;
}
if (type == null) {
throw new PEException("'" + name + "' isn't a recognized type for group service");
}
catHelper.setVariable(VariableConstants.GROUP_SERVICE_NAME, type, true);
}
@SuppressWarnings("unused")
public void cmd_show_variables(Scanner scanner) throws PEException {
final List<VariableConfig> variables = catHelper.getAllVariables();
printlnDots("Variables size: " + variables.size());
for (final VariableConfig variable : variables) {
if (variable.getScopes().indexOf(VariableScopeKind.GLOBAL.name()) > -1)
println(variable.getName() + " = " + variable.getValue());
}
}
public void cmd_set_variable(Scanner scanner) throws PEException {
final String variable = scan(scanner, "variable");
final int index = variable.indexOf('=');
if (index == -1) {
throw new PEException("Variable should be in the form <key>=<value>");
}
final String key = variable.substring(0, index);
final String value = variable.substring(index + 1);
catHelper.setVariable(key, value, true);
printlnDots("Successfully set variable '" + key + "' to value '" + value + "'");
}
public void cmd_add_es(Scanner scanner) throws PEException {
final String name = scan(scanner, "name");
final String plugin = scan(scanner, "plugin");
final boolean usesDataStore = scanBoolean(scanner, "use data source");
String connectUser = scan(scanner);
if ((connectUser != null) && connectUser.equals("null")) {
connectUser = null;
}
final File esFile = scanFile(scanner);
final ExternalService externalService = catHelper.createExternalService(name, plugin, connectUser, usesDataStore,
(esFile == null) ? null : PEFileUtils.readToString(esFile));
printlnDots("External service '" + externalService.getName() + "' created");
}
public void cmd_configure_es(Scanner scanner) throws PEException {
final String name = scan(scanner, "name");
final File esFile = scanFile(scanner, "filename");
final ExternalService externalService = catHelper.setExternalServiceConfig(name, PEFileUtils.readToString(esFile));
printlnDots("External service '" + externalService.getName() + "' configuration complete");
}
public void cmd_show_es(Scanner scanner) throws PEException {
final String name = scan(scanner, "name");
final ExternalService externalService = catHelper.getExternalService(name);
printlnDots("External service '" + externalService.getName() + "' plugin='" + externalService.getPlugin()
+ "' auto start=" + externalService.isAutoStart() + "' uses datastore="
+ externalService.usesDataStore() + " 'connect user='" + externalService.getConnectUser());
println("Configuration:");
println(externalService.getConfig());
}
public void cmd_remove_es(Scanner scanner) throws PEException {
final String name = scan(scanner, "name");
catHelper.removeExternalService(name);
printlnDots("External service '" + name + "' removed");
}
public void cmd_upgrade_catalog() throws PEException {
catHelper.checkURLAvailable();
final Properties props = catHelper.getProperties();
final String catalogLocation = props.getProperty(DBHelper.CONN_URL, "");
boolean upgradeRequired = false;
try {
CatalogVersions.catalogVersionCheck(props);
} catch (final PEException e) {
upgradeRequired = true;
}
if (!upgradeRequired) {
throw new PEException("DVE catalog at '" + catalogLocation
+ "' is latest version. Upgrade not required.");
}
println("You are about to upgrade your DVE catalog located at '" + catalogLocation + "'");
askQuestion("Do you want to continue [y/N]? ", new QuestionCallback() {
@Override
public void answer(String line) throws PEException {
if ((line != null) && (line.equalsIgnoreCase("y") || line.equalsIgnoreCase("yes"))) {
printlnDots("Starting DVE catalog upgrade at '" + catalogLocation + "'");
//this Host initialization was pulled out of CatalogVersions to break a dependency cycle between Host and CatalogVersions.
if (Singletons.lookup(HostService.class) == null){
props.put(DBHelper.CONN_DRIVER_CLASS, PEConstants.MYSQL_DRIVER_CLASS);
// we can't start the catalog here - otherwise we would modify it while sitting on it
new Host(props, false /* startCatalog */); //TODO: the startCatalog flag has a bad smell. HostService needs further decomposition. -sgossard
}
CatalogVersions.upgradeToLatest(props,DVEConfigCLI.this);
printlnDots("Catalog upgrade complete");
}
}
});
}
public void cmd_check_catalog_version() throws PEException {
catHelper.checkURLAvailable();
final Properties props = catHelper.getProperties();
final String catalogLocation = props.getProperty(DBHelper.CONN_URL, "");
printlnDots("Checking version of DVE catalog at '" + catalogLocation + "'");
final CatalogSchemaVersion version = CatalogVersions.catalogVersionCheck(props);
printlnDots("DVE catalog at " + catalogLocation + " is at latest version (" + version.getSchemaVersion()
+ ")");
}
public void cmd_truncate_servers() {
println("You are about to completely remove all registered servers from the DVE catalog");
askQuestion("Do you want to continue [y/N]? ", new QuestionCallback() {
@Override
public void answer(String line) throws PEException {
if ((line != null) && (line.equalsIgnoreCase("y") || line.equalsIgnoreCase("yes"))) {
catHelper.deleteAllServerRegistration();
printlnDots("All registered servers removed from the DVE catalog");
}
}
});
}
public void cmd_action_list() {
printlnDots("Available actions are:");
for (final FieldAction action : FieldActionManager.getAllActions()) {
println(action.getName() + " - " + action.getDescription());
}
}
public void cmd_action_report(Scanner scanner) throws PEException {
final String name = scan(scanner, "name");
final FieldAction action = getActionOnCurrentCatalog(name);
action.report(catHelper.getProperties(), getPrintStream());
}
public void cmd_action_run(Scanner scanner) throws PEException {
final String name = scan(scanner, "name");
final FieldAction action = getActionOnCurrentCatalog(name);
action.execute(catHelper.getProperties(), getPrintStream());
}
private FieldAction getActionOnCurrentCatalog(final String name) throws PEException {
final FieldAction action = FieldActionManager.getAction(name);
if (!action.isValid(CatalogVersions.getCurrentVersion())) {
throw new PEException("Action '" + name + "' isn't available for the current catalog version");
}
return action;
}
private DynamicPolicy parsePolicyConfig(PolicyConfig config) {
return new DynamicPolicy(config.getName(), config.isStrict(), config.getAggregation().getProvider(), config
.getAggregation().getPool(), config.getAggregation().getCount(), config.getSmall().getProvider(),
config.getSmall().getPool(), config.getSmall().getCount(), config.getMedium().getProvider(), config
.getMedium().getPool(), config.getMedium().getCount(), config.getLarge().getProvider(), config
.getLarge().getPool(), config.getLarge().getCount());
}
public void cmd_connect(Scanner scanner) throws PEException {
cmd_disconnect();
String url = PEConstants.MYSQL_URL;
String user = PEConstants.ROOT;
String password = PEConstants.PASSWORD;
if (scanner.hasNext()) {
url = scan(scanner, "url");
user = scan(scanner, "user");
password = scan(scanner, "password");
}
url = PEUrl.fromUrlString(url).getURL();
dbHelper = new DBHelper(url, user, password).connect();
printlnDots("You are now connected to the DVE server at '" + url + "'");
}
public void cmd_disconnect() {
if (dbHelper != null) {
dbHelper.disconnect();
printlnDots("You are now disconnected from the DVE server");
}
dbHelper = null;
}
public void cmd_add_template(Scanner scanner) throws PEException {
final File templateFile = scanFile(scanner, "filename");
final String match = scan(scanner);
addTemplate(templateFile, false, match);
printlnDots("Template '" + templateFile.getAbsolutePath() + "' added to catalog");
}
public void cmd_add_templates(Scanner scanner) throws PEException {
final File folder = scanFile(scanner, "folder");
pushTemplatesToCatalog(folder, false);
}
public void cmd_update_template(Scanner scanner) throws PEException {
final File templateFile = scanFile(scanner, "filename");
final String match = scan(scanner);
addTemplate(templateFile, true, match);
printlnDots("Template '" + templateFile + "' updated in catalog");
}
public void cmd_update_templates(Scanner scanner) throws PEException {
final File folder = scanFile(scanner, "folder");
pushTemplatesToCatalog(folder, true);
}
private void pushTemplatesToCatalog(final File folder, final boolean updateExisting) throws PEException {
if (!folder.isDirectory()) {
throw new PEException("'" + folder + "' isn't a valid directory");
}
final File[] files = folder.listFiles(TEMPLATE_FILE_NAME_FILTER);
for (final File file : files) {
try {
addTemplate(file, updateExisting, null);
printlnDots("Template '" + file.getPath() + "' added to catalog");
} catch (final Exception e) {
printlnDots("Could not add template '" + file.getAbsolutePath() + "' - " + e.getMessage());
}
}
}
private String addTemplate(File templateFile, boolean update, String match) throws PEException {
final String templateStr = PEFileUtils.readToString(templateFile);
final Template template = PEXmlUtils.unmarshalJAXB(templateStr, Template.class, PEFileUtils.getClasspathURL(Template.class, TEMPLATE_SCHEMA));
final String useName = template.getName();
final String useComment = template.getComment();
final String useMatch = StringUtils.isBlank(match) ? template.getMatch() : match;
try {
if (dbHelper != null) {
addTemplateToServer(useName, templateStr, update, useMatch, useComment);
} else {
addTemplateToCatalog(useName, templateStr, update, useMatch, useComment);
}
return useName;
} catch (final Exception e) {
if (update) {
throw new PEException("Failed to update template '" + template + "' in catalog - " + e.getMessage(), e);
} else {
throw new PEException("Failed to add template '" + template + "' to catalog - " + e.getMessage(), e);
}
}
}
private void addTemplateToServer(String name, String templateStr, boolean update, String match, String comment)
throws Exception {
final StringBuilder query = new StringBuilder();
if (update) {
query.append("ALTER TEMPLATE `").append(name).append("` SET XML='")
.append(StringEscapeUtils.escapeSql(templateStr)).append("'");
} else {
query.append("CREATE TEMPLATE `").append(name).append("` XML='")
.append(StringEscapeUtils.escapeSql(templateStr)).append("'");
}
if (StringUtils.isNotBlank(match)) {
query.append(" MATCH='").append(match).append("'");
}
if (StringUtils.isNotBlank(comment)) {
query.append(" COMMENT='").append(comment).append("'");
}
dbHelper.executeQuery(query.toString());
}
private void addTemplateToCatalog(String name, String templateStr, boolean update, String match, String comment)
throws Exception {
if (update) {
catHelper.updateTemplate(name, templateStr, match, comment);
} else {
catHelper.createTemplate(name, templateStr, match, comment);
}
}
private JMXMBeanConnector jmxConnector = null;
private String jmxDomain = null;
private ObjectName jmxMBean = null;
private MBeanInfo jmxMBeanInfo = null;
public void registerJMXCommands() {
// The following commands are for JMX mode
final CommandMapType map = createCommandMap("jmx");
map.registerCommand(new CommandType(new String[] { "connect" }, "[<host> <port>]"));
map.registerCommand(new CommandType(new String[] { "list", "domains" }, ""));
map.registerCommand(new CommandType(new String[] { "domain" }, "<domain>"));
map.registerCommand(new CommandType(new String[] { "list", "mbeans" }, ""));
map.registerCommand(new CommandType(new String[] { "mbean" }, "<mbean>"));
map.registerCommand(new CommandType(new String[] { "get", "attribute" }, "<attribute>"));
map.registerCommand(new CommandType(new String[] { "get", "attributes" }, ""));
map.registerCommand(new CommandType(new String[] { "list", "operations" }, ""));
map.registerCommand(new CommandType(new String[] { "invoke" }, "[<arg 1> ... <arg n>]"));
}
public void cmd_jmx_connect(Scanner scanner) throws PEException {
String host = scan(scanner);
Integer port = scanInteger(scanner);
if (host == null) {
host = DEFAULT_JMX_HOST;
}
if (port == null)
{
port = DEFAULT_JMX_PORT;
}
jmxConnector = new JMXMBeanConnector(host, port);
printlnDots("You are now connected to the MBean server at '" + jmxConnector.serviceUrl + "'");
cmd_jmx_list_domains();
}
public void cmd_jmx_disconnect() throws PEException {
checkJMXConnected();
jmxConnector.close();
this.jmxConnector = null;
this.jmxDomain = null;
this.jmxMBean = null;
this.jmxMBeanInfo = null;
}
public void cmd_jmx_list_domains() throws PEException {
checkJMXConnected();
final String[] domains = jmxConnector.getBeanDomains();
printlnDots("Available MBean domain names are:");
for (final String domain : domains) {
println(domain);
}
}
public void cmd_jmx_domain(Scanner scanner) throws PEException {
checkJMXConnected();
final String domain = scan(scanner, "domain");
jmxConnector.getBeanNames(domain);
jmxDomain = domain;
jmxMBean = null;
jmxMBeanInfo = null;
printlnDots("Domain '" + jmxDomain + "' selected");
cmd_jmx_list_mbeans();
}
public void cmd_jmx_list_mbeans() throws PEException {
checkJMXConnected();
checkJMXDomain();
final Set<ObjectName> beans = jmxConnector.getBeanNames(jmxDomain);
printlnDots("Available MBean(s) in domain '" + jmxDomain + "' are:");
for (final ObjectName bean : beans) {
println(bean.getCanonicalKeyPropertyListString());
}
}
public void cmd_jmx_mbean(Scanner scanner) throws PEException {
checkJMXConnected();
checkJMXDomain();
final String mbean = scan(scanner, "mbean");
final ObjectName on = jmxConnector.makeObjectName(jmxDomain, mbean);
final MBeanInfo info = jmxConnector.getBeanInfo(on);
jmxMBean = on;
jmxMBeanInfo = info;
printlnDots("MBean '" + jmxMBean + "' selected");
println("Attributes = " + (info.getAttributes() == null ? "N/A" : info.getAttributes().length));
println("Operations = " + (info.getOperations() == null ? "N/A" : info.getOperations().length));
}
public void cmd_jmx_get_attribute(Scanner scanner) throws PEException {
checkJMXConnected();
checkJMXDomain();
checkJMXMBean();
final String attribute = scan(scanner, "attribute");
printlnDots("Attribute value for '" + attribute + "' in mbean '" + jmxMBean + "' is:");
println(attribute + " = " + jmxConnector.getAttribute(jmxMBean, attribute));
}
public void cmd_jmx_get_attributes() throws PEException {
checkJMXConnected();
checkJMXDomain();
checkJMXMBean();
for (final MBeanAttributeInfo attrib : jmxMBeanInfo.getAttributes()) {
if (attrib.isReadable()) {
println(attrib.getName() + " = " + jmxConnector.getAttribute(jmxMBean, attrib.getName()));
}
}
}
public void cmd_jmx_list_operations() throws PEException {
checkJMXConnected();
checkJMXDomain();
checkJMXMBean();
for (final MBeanOperationInfo operation : jmxMBeanInfo.getOperations()) {
final String name = operation.getName();
final StringBuilder sParam = new StringBuilder();
for (final MBeanParameterInfo param : operation.getSignature()) {
if (sParam.length() > 0) {
sParam.append(", ");
}
sParam.append(formatType(param.getType())).append(" ").append(param.getName());
}
final StringBuilder builder = new StringBuilder();
println(builder.append(name).append("(").append(sParam.toString()).append(")").toString());
}
}
public void cmd_jmx_invoke(Scanner scanner) throws PEException {
checkJMXConnected();
checkJMXDomain();
checkJMXMBean();
final String opName = scan(scanner, "operation");
// First lookup the operation to ensure it exists
MBeanOperationInfo found = null;
for (final MBeanOperationInfo operation : jmxMBeanInfo.getOperations()) {
if (operation.getName().equals(opName)) {
found = operation;
break;
}
}
if (found == null) {
throw new PEException("'" + opName + "' isn't a valid operation on '" + jmxMBean + "'");
}
final MBeanParameterInfo[] params = found.getSignature();
final Object[] paramO = params.length == 0 ? null : new Object[params.length];
final String[] paramT = params.length == 0 ? null : new String[params.length];
for (int i = 0; i < params.length; i++) {
// For each parameter we expect to have a matching item in the
// scanner
final String val = scan(scanner, params[i].getName());
paramO[i] = jmxConnector.stringToObject(val, params[i].getType());
paramT[i] = params[i].getType();
}
jmxConnector.invokeOperation(jmxMBean, found, paramO, paramT);
}
private String formatType(String type) {
if (type.startsWith("[")) {
switch (type.charAt(1)) {
case 'C':
return "char[]";
case 'D':
return "double[]";
case 'F':
return "float[]";
case 'I':
return "int[]";
case 'J':
return "long[]";
case 'S':
return "short[]";
case 'Z':
return "boolean[]";
case 'L':
return type.substring(2, type.indexOf(";"));
}
}
return type;
}
private void checkJMXConnected() throws PEException {
if (jmxConnector == null) {
throw new PEException("Your aren't connected to a JMX MBean server");
}
}
private void checkJMXDomain() throws PEException {
if (jmxDomain == null) {
throw new PEException("Your haven't selected a JMX MBean domain");
}
}
private void checkJMXMBean() throws PEException {
if ((jmxMBean == null) || (jmxMBeanInfo == null)) {
throw new PEException("Your haven't selected a JMX MBean");
}
}
public static void main(String[] args) {
try {
new DVEConfigCLI(args).start();
} catch (final Exception e) {
e.printStackTrace();
}
}
}