com.sun.enterprise.admin.cli.cluster.CreateLocalInstanceCommand Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of payara-micro Show documentation
Show all versions of payara-micro Show documentation
Micro Distribution of the Payara Project
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 1997-2013 Oracle and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can
* obtain a copy of the License at
* https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
* or packager/legal/LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at packager/legal/LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
// Portions Copyright [2016-2020] [Payara Foundation and/or its affiliates]
package com.sun.enterprise.admin.cli.cluster;
import com.sun.enterprise.admin.cli.CLIConstants;
import com.sun.enterprise.admin.cli.remote.RemoteCLICommand;
import com.sun.enterprise.admin.servermgmt.KeystoreManager;
import com.sun.enterprise.admin.util.CommandModelData.ParamModelData;
import com.sun.enterprise.security.store.PasswordAdapter;
import com.sun.enterprise.universal.glassfish.TokenResolver;
import com.sun.enterprise.util.OS;
import com.sun.enterprise.util.StringUtils;
import com.sun.enterprise.util.SystemPropertyConstants;
import com.sun.enterprise.util.io.FileUtils;
import fish.payara.admin.cli.cluster.NamingHelper;
import fish.payara.util.cluster.PayaraServerNameGenerator;
import java.io.File;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.logging.Level;
import org.glassfish.api.ActionReport;
import org.glassfish.api.I18n;
import org.glassfish.api.Param;
import org.glassfish.api.admin.CommandException;
import org.glassfish.api.admin.CommandValidationException;
import org.glassfish.hk2.api.PerLookup;
import org.glassfish.security.common.FileProtectionUtility;
import org.jvnet.hk2.annotations.Service;
import static com.sun.enterprise.admin.servermgmt.domain.DomainConstants.MASTERPASSWORD_FILE;
/**
* This is a local command that calls the primitive remote _register-instance to add the
* entries in domain.xml and then the primitive local command _create-instance-filesystem
* to create the empty directory structure and das.properties
*
*/
@Service(name = "create-local-instance")
@PerLookup
@I18n("create.local.instance")
public final class CreateLocalInstanceCommand extends CreateLocalInstanceFilesystemCommand {
private static final String CONFIG = "config";
private static final String CLUSTER = "cluster";
private static final String DEPLOYMENT_GROUP = "deploymentGroup";
@Param(name = CONFIG, optional = true)
private String configName;
@Param(name = CLUSTER, optional = true)
private String clusterName;
@Param(name = DEPLOYMENT_GROUP, optional = true)
private String deploymentGroup;
@Param(name="lbenabled", optional = true)
private Boolean lbEnabled;
@Param(name = "systemproperties", optional = true, separator = ':')
private String systemProperties; // XXX - should it be a Properties?
@Param(name = "portbase", optional = true)
private String portBase;
@Param(name = "checkports", optional = true, defaultValue = "true")
private boolean checkPorts = true;
@Param(name = "savemasterpassword", optional = true, defaultValue = "false")
private boolean saveMasterPassword = false;
@Param(name = "usemasterpassword", optional = true, defaultValue = "false")
private boolean useMasterPassword = false;
// Add asadmin utility option so that it isn't mandated to be before the command on the command line
// Technically deprecated syntax
@Param(name = "autoname", optional = true, shortName = "a", defaultValue = "false")
private boolean autoName;
@Param(name = "dataGridStartPort", optional = true, alias = "datagridstartport")
private String dataGridStartPort;
// Override for hostname, as getting it from the system can be fragile when comparing against node config
@Param(name = "ip", optional = true)
private String ip;
private String masterPassword = null;
private static final String RENDEZVOUS_PROPERTY_NAME = "rendezvousOccurred";
private String INSTANCE_DOTTED_NAME;
private String RENDEZVOUS_DOTTED_NAME;
private boolean _rendezvousOccurred;
private String _node;
private static final String DEFAULT_MASTER_PASSWORD = KeystoreManager.DEFAULT_MASTER_PASSWORD;
private ParamModelData masterPasswordOption;
@Override
protected void validate() throws CommandException {
echoCommand();
if (configName != null && clusterName != null) {
throw new CommandException(Strings.get("ConfigClusterConflict"));
}
if (lbEnabled != null && clusterName == null) {
throw new CommandException(Strings.get("lbenabledNotForStandaloneInstance"));
}
setDasDefaultsOnly = true; //Issue 12847 - Call super.validate to setDasDefaults only
super.validate(); //so _validate-node uses das host from das.properties. No dirs created.
if (node != null) {
//BugDB 13431949 - If installdir is not specified on node, call _validate-node on DAS to populate installdir.
//If installdir is specified on node, validate installdir locally so we can take advantage of java path processing to
//normalize the installdir from the node.
//If installdir has tokens, call _validate-node on DAS to have DAS resolve the tokens
//If we are on Windows, call _validate-node on DAS instead of relying on the path processing in the local validation.
String nodeInstallDir = getNodeInstallDir();
if (nodeInstallDir == null || nodeInstallDir.isEmpty() || TokenResolver.hasToken(nodeInstallDir) || OS.isWindows()) {
if (dockerNode && StringUtils.ok(ip)) {
validateNode(node, getProductRootPath(), ip);
} else {
validateNode(node, getProductRootPath(), getInstanceHostName(true));
}
} else {
validateNodeInstallDirLocal(nodeInstallDir, getProductRootPath());
if (dockerNode && StringUtils.ok(ip)) {
validateNode(node, null, ip);
} else {
validateNode(node, null, getInstanceHostName(true));
}
}
}
if (!rendezvousWithDAS()) {
throw new CommandException(Strings.get("Instance.rendezvousFailed", DASHost, "" + DASPort));
}
if (instanceName != null && instanceName.equals(SystemPropertyConstants.DAS_SERVER_NAME)) {
throw new CommandException(Strings.get("Instance.alreadyExists", SystemPropertyConstants.DAS_SERVER_NAME));
}
setDomainName();
setDasDefaultsOnly = false;
if (programOpts.isAutoName() || autoName) {
instanceName0 = PayaraServerNameGenerator.validateInstanceNameUnique(instanceName0,
NamingHelper.getAllNamesInUse(programOpts, env));
}
super.validate(); // instanceName is validated and set in super.validate(), directories created
INSTANCE_DOTTED_NAME = "servers.server." + instanceName;
RENDEZVOUS_DOTTED_NAME = INSTANCE_DOTTED_NAME + ".property." + RENDEZVOUS_PROPERTY_NAME;
_rendezvousOccurred = rendezvousOccurred();
if (_rendezvousOccurred) {
throw new CommandException(
Strings.get("Instance.rendezvousAlready", instanceName));
}
}
/**
*/
@Override
protected int executeCommand()
throws CommandException, CommandValidationException {
int exitCode = -1;
if (node == null) {
if(nodeDirChild == null)
throw new CommandException(Strings.get("internal.error",
"nodeDirChild was null. The Base Class is supposed to "
+ "guarantee that this won't happen"));
_node = nodeDirChild.getName();
String nodeHost = getInstanceHostName(true);
createNodeImplicit(_node, getProductRootPath(), nodeHost);
} else {
_node = node;
}
if (isRegisteredToDAS()) {
if (!_rendezvousOccurred) {
setRendezvousOccurred("true");
_rendezvousOccurred = true;
}
} else {
validateInstanceDirUnique();
try {
registerToDAS();
_rendezvousOccurred = true;
} catch (CommandException ce) {
FileUtils.deleteFileNowOrLater(instanceDir);
throw ce;
}
}
bootstrapSecureAdminFiles();
try {
exitCode = super.executeCommand();
if (exitCode == SUCCESS) {
saveMasterPassword();
if (programOpts.isExtraTerse() || extraTerse) {
logger.info(instanceName);
}
}
} catch (CommandException ce) {
String msg = "Something went wrong in creating the local filesystem for instance " + instanceName;
if (ce.getLocalizedMessage() != null) {
msg = msg + ": " + ce.getLocalizedMessage();
}
logger.severe(msg);
setRendezvousOccurred("false");
_rendezvousOccurred = false;
throw new CommandException(msg, ce);
}
if (StringUtils.ok(dataGridStartPort)) {
try {
RemoteCLICommand rc = new RemoteCLICommand("set-hazelcast-configuration", this.programOpts, this.env);
rc.execute("set-hazelcast-configuration", "--configSpecificDataGridStartPort", dataGridStartPort, "--target", instanceName);
} catch (CommandException cex) {
String msg = "Something went wrong when setting config specific Data Grid start port for instance " + instanceName;
if (cex.getLocalizedMessage() != null) {
msg = msg + ": " + cex.getLocalizedMessage();
}
logger.severe(msg);
throw new CommandException(msg, cex);
}
}
return exitCode;
}
private void validateInstanceDirUnique() throws CommandException {
RemoteCLICommand rc = new RemoteCLICommand("list-instances", this.programOpts, this.env);
String returnOutput =
rc.executeAndReturnOutput("list-instances", "--nostatus", _node);
if (returnOutput == null)
return;
String[] registeredInstanceNamesOnThisNode = returnOutput.split("\r?\n");
for (String registeredInstanceName : registeredInstanceNamesOnThisNode) {
File instanceListDir = new File(nodeDirChild, registeredInstanceName);
if (instanceName != null && registeredInstanceName.equalsIgnoreCase(instanceName)) {
if (instanceDir != null && instanceListDir.equals(instanceDir)){
throw new CommandException(
Strings.get("Instance.duplicateInstanceDir",
instanceName, registeredInstanceName));
}
}
}
}
private int bootstrapSecureAdminFiles() throws CommandException {
RemoteCLICommand rc = new RemoteCLICommand("_bootstrap-secure-admin", this.programOpts, this.env);
rc.setFileOutputDirectory(instanceDir);
return rc.execute(new String[] {"_bootstrap-secure-admin"});
}
/**
* If --savemasterpassword=true,
* then --usemasterpassword is set to true also
* If AS_ADMIN_MASTERPASSWORD from --passwordfile exists that is used.
* If it does not exist, the user is asked to enter the master password.
* The password is validated against the keystore if it exists. If successful, master-password
* is saved to the server instance directory /nodes//master-password.
* If the password entered does not match the keystore, master-password is not
* saved and a warning is displayed. The command is still successful.
* The default value of --usemasterpassword is false.
*
* When savemasterpassword is false, the keystore is encrypted with a well-known password
* that is built into the system, thus affording no additional security.
* The master password must be the same for all instances in a domain.
* @throws CommandException
*/
private void saveMasterPassword() throws CommandException {
masterPasswordOption = new ParamModelData(CLIConstants.MASTER_PASSWORD,
String.class, false, null);
masterPasswordOption.prompt = Strings.get("MasterPassword");
masterPasswordOption.promptAgain = Strings.get("MasterPasswordAgain");
masterPasswordOption.param._password = true;
if (saveMasterPassword)
useMasterPassword = true;
if (useMasterPassword)
masterPassword = getPassword(masterPasswordOption,
DEFAULT_MASTER_PASSWORD, true) != null ? new String(getPassword(masterPasswordOption,
DEFAULT_MASTER_PASSWORD, true)) : null;
if (masterPassword == null)
masterPassword = DEFAULT_MASTER_PASSWORD;
if (saveMasterPassword) {
File mp = new File(new File(getServerDirs().getServerDir(), "config"), "keystore.p12");
if (mp.canRead()) {
if (verifyMasterPassword(masterPassword)) {
createMasterPasswordFile(masterPassword);
} else {
logger.info(Strings.get("masterPasswordIncorrect"));
}
} else {
createMasterPasswordFile(masterPassword);
}
}
}
/**
* Create the master password keystore. This routine can also modify the master password
* if the keystore already exists
* @param masterPassword
* @throws CommandException
*/
protected void createMasterPasswordFile(String masterPassword) throws CommandException {
final File pwdFile = new File(this.getServerDirs().getAgentDir(), MASTERPASSWORD_FILE);
try {
PasswordAdapter p = new PasswordAdapter(pwdFile.getAbsolutePath(), MASTERPASSWORD_FILE.toCharArray());
p.setPasswordForAlias(MASTERPASSWORD_FILE, masterPassword.getBytes());
FileProtectionUtility.chmod0600(pwdFile);
} catch (Exception ex) {
throw new CommandException(Strings.get("masterPasswordFileNotCreated", pwdFile),
ex);
}
}
private boolean rendezvousWithDAS() {
try {
getUptime();
if (!programOpts.isExtraTerse() || !extraTerse) {
logger.info(Strings.get("Instance.rendezvousSuccess", DASHost, "" + DASPort));
}
return true;
} catch (CommandException ex) {
return false;
}
}
private int registerToDAS() throws CommandException {
ArrayList argsList = new ArrayList();
argsList.add(0, "_register-instance");
if (clusterName != null) {
argsList.add("--cluster");
argsList.add(clusterName);
}
if (deploymentGroup != null) {
argsList.add("--deploymentgroup");
argsList.add(deploymentGroup);
}
if (lbEnabled != null) {
argsList.add("--lbenabled");
argsList.add(lbEnabled.toString());
}
if (configName != null) {
argsList.add("--config");
argsList.add(configName);
}
if (_node != null) {
argsList.add("--node");
argsList.add(_node);
}
argsList.add("--checkports");
argsList.add(String.valueOf(checkPorts));
if (portBase != null) {
argsList.add("--portbase");
argsList.add(portBase);
}
if (systemProperties != null) {
argsList.add("--systemproperties");
argsList.add(systemProperties);
}
argsList.add("--properties");
argsList.add(RENDEZVOUS_PROPERTY_NAME+"=true");
argsList.add(this.instanceName);
String[] argsArray = new String[argsList.size()];
argsArray = argsList.toArray(argsArray);
RemoteCLICommand rc = new RemoteCLICommand("_register-instance", this.programOpts, this.env);
return rc.execute(argsArray);
}
private boolean isRegisteredToDAS() {
boolean isRegistered = false;
try {
RemoteCLICommand rc = new RemoteCLICommand("get", this.programOpts, this.env);
rc.executeAndReturnOutput("get", INSTANCE_DOTTED_NAME);
isRegistered = true;
} catch (CommandException ex) {
logger.log(Level.FINER, "{0} is not yet registered to DAS.", instanceName);
isRegistered=false;
}
return isRegistered;
}
private boolean rendezvousOccurred() {
boolean rendezvousOccurred = false;
RemoteCLICommand rc = null;
try {
rc = new RemoteCLICommand("get", this.programOpts, this.env);
String s = rc.executeAndReturnOutput("get", RENDEZVOUS_DOTTED_NAME);
String val = s.substring(s.indexOf('=') + 1);
rendezvousOccurred = Boolean.parseBoolean(val);
logger.log(Level.FINER, "rendezvousOccurred = {0} for instance {1}", new Object[]{val, instanceName});
} catch (CommandException ce) {
logger.log(Level.FINER,RENDEZVOUS_PROPERTY_NAME + " property may not be set yet on {0}", instanceName);
}
return rendezvousOccurred;
}
private void setRendezvousOccurred(String rendezVal) throws CommandException {
String dottedName = RENDEZVOUS_DOTTED_NAME + "=" + rendezVal;
RemoteCLICommand rc = new RemoteCLICommand("set", this.programOpts, this.env);
logger.log(Level.FINER, "Setting rendezvousOccurred to {0} for instance {1}", new Object[]{rendezVal, instanceName});
rc.executeAndReturnOutput("set", dottedName);
}
/* installdir is product install dir (parent of glassfish install root) */
private int createNodeImplicit(String name, String installdir, String nodeHost) throws CommandException {
ArrayList argsList = new ArrayList();
argsList.add(0, "_create-node-implicit");
if (name != null) {
argsList.add("--name");
argsList.add(name);
}
if (nodeDir != null) {
argsList.add("--nodedir");
argsList.add(nodeDir);
}
if (installdir != null) {
argsList.add("--installdir");
argsList.add(installdir);
}
argsList.add(nodeHost);
String[] argsArray = new String[argsList.size()];
argsArray = argsList.toArray(argsArray);
RemoteCLICommand rc = new RemoteCLICommand("_create-node-implicit", this.programOpts, this.env);
return rc.execute(argsArray);
}
/* installdir is product install dir (parent of glassfish install root) */
private int validateNode(String name, String installdir, String nodeHost) throws CommandException {
ArrayList argsList = new ArrayList();
argsList.add(0, "_validate-node");
if (nodeDir != null) {
argsList.add("--nodedir");
argsList.add(nodeDir);
}
if (nodeHost != null) {
argsList.add("--nodehost");
argsList.add(nodeHost);
}
if (installdir != null) {
argsList.add("--installdir");
argsList.add(installdir);
}
argsList.add(name);
String[] argsArray = new String[argsList.size()];
argsArray = argsList.toArray(argsArray);
RemoteCLICommand rc = new RemoteCLICommand("_validate-node", this.programOpts, this.env);
return rc.execute(argsArray);
}
private void validateNodeInstallDirLocal(String nodeInstallDir, String installDir) throws CommandValidationException {
String canonicalNodeInstallDir = FileUtils.safeGetCanonicalPath(new File(nodeInstallDir));
String canonicalInstallDir = FileUtils.safeGetCanonicalPath(new File(installDir));
if (canonicalNodeInstallDir == null || canonicalInstallDir == null) {
throw new CommandValidationException(
Strings.get("Instance.installdir.null", node, canonicalInstallDir, canonicalNodeInstallDir));
}
if ( !canonicalInstallDir.equals(canonicalNodeInstallDir) ) {
throw new CommandValidationException(
Strings.get("Instance.installdir.mismatch", node, canonicalInstallDir, canonicalNodeInstallDir));
}
}
private String getInstanceHostName(boolean isCanonical) throws CommandException {
String instanceHostName = null;
InetAddress localHost = null;
try {
localHost = InetAddress.getLocalHost();
} catch (UnknownHostException ex) {
throw new CommandException(Strings.get("cantGetHostName", ex));
}
if (localHost != null) {
if (isCanonical) {
instanceHostName = localHost.getCanonicalHostName();
} else {
instanceHostName = localHost.getHostName();
}
}
return instanceHostName;
}
private void setDomainName() throws CommandException {
RemoteCLICommand rc = new RemoteCLICommand("_get-runtime-info", this.programOpts, this.env);
ActionReport report = rc.executeAndReturnActionReport("_get-runtime-info", "--target", "server");
this.domainName = report.findProperty("domain_name");
}
@Override
protected boolean mkdirs(File f) {
if (setDasDefaultsOnly) {
return true;
} else {
return f.mkdirs();
}
}
@Override
protected boolean isDirectory(File f) {
if (setDasDefaultsOnly) {
return true;
} else {
return f.isDirectory();
}
}
@Override
protected boolean setServerDirs() {
return !setDasDefaultsOnly;
}
private void echoCommand() {
if (this.programOpts.isEcho()) {
logger.info(this.toString());
programOpts.setEcho(false); // only echo this command
}
}
}