/*******************************************************************************
* Copyright 2011 GigaSpaces Technologies Ltd
*
* Licensed 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.cloudifysource.shell.commands;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.apache.felix.gogo.commands.Argument;
import org.apache.felix.gogo.commands.Command;
import org.apache.felix.gogo.commands.CompleterValues;
import org.openspaces.cloud.installer.AgentlessInstaller;
import org.openspaces.cloud.installer.InstallationDetails;
import org.openspaces.cloud.installer.InstallerException;
import org.cloudifysource.shell.rest.ErrorStatusException;
/**
* @author barakme
* @since 2.0.0
*/
@Command(scope = "cloudify", name = "add-machine", description = "Sets up a new host preconfigured for the service zone")
public class AddBareMetalInstance extends AdminAwareCommand {
private static final String NODES_PROPERTIES_FILE_NAME = "nodes.properties";
@Argument(index = 0, required = true, name = "service", description = "The service name to add instance to. Press tab to see the list of currently running services")
private String serviceName;
@CompleterValues(index = 0)
public Collection<String> getComponentList() {
try {
return adminFacade.getServicesList(getCurrentApplicationName());
} catch (final ErrorStatusException e) {
logger.warning("Could not get list of services: " + e.getReasonCode());
return null;
}
}
private Properties getProperties() throws IOException {
FileReader reader = null;
try {
reader = new FileReader(NODES_PROPERTIES_FILE_NAME);
final Properties props = new Properties();
props.load(reader);
return props;
} catch (final FileNotFoundException e) {
try {
createDefaultPropertiesFile();
} catch (final IOException ioe) {
throw new IOException("File " + NODES_PROPERTIES_FILE_NAME
+ " was not found. Attempted to create default file, but failed: " + e.getMessage(), ioe);
}
throw new FileNotFoundException(
"File: "
+ NODES_PROPERTIES_FILE_NAME
+ " was not found. A default file was generated in the local directory. " +
"Please set the required values before trying again");
} finally {
if (reader != null) {
reader.close();
}
}
}
private void createDefaultPropertiesFile() throws IOException {
InputStream is = null;
FileOutputStream os = null;
try {
is = this.getClass().getClassLoader().getResourceAsStream("META-INF/nodes/default_nodes_config.properties");
if (is == null) {
throw new IOException(
"Failed to create properties file as default properties file was not found on the classpath");
}
os = new FileOutputStream(NODES_PROPERTIES_FILE_NAME);
while (true) {
final byte[] buff = new byte[10 * 1024];
final int howmany = is.read(buff);
os.write(buff, 0, howmany);
if (howmany < buff.length) {
return;
}
}
} finally {
if (is != null) {
is.close();
}
if (os != null) {
os.close();
}
}
}
@Override
protected Object doExecute() throws ErrorStatusException, IOException {
final Properties props = getProperties();
logger.info("Scanning for machine");
final String machine = chooseMachine(props);
if (machine == null) {
return "No machine available for scale out";
}
logger.info("Selected machine: " + machine);
final AgentlessInstaller installer = new AgentlessInstaller();
final InstallationDetails details = createInstallationDetails(props, machine);
try {
installer.installOnMachineWithIP(details, 5, TimeUnit.MINUTES);
} catch (final TimeoutException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (final InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (final InstallerException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return "Machine " + machine + " started up successfully in zone: " + this.serviceName;
}
protected InstallationDetails createInstallationDetails(final Properties props, final String machine) {
final InstallationDetails details = new InstallationDetails();
details.setLocalDir(props.getProperty("localDir", "C:/docBaseBare"));
details.setLocator(props.getProperty("lookupGroups", System.getenv("LOOKUPGROUPS")));
details.setLus(false);
details.setPassword(props.getProperty("password"));
details.setPrivateIp(machine);
details.setRemoteDir(props.getProperty("remoteDir", "/tmp/gs-files"));
details.setTargetIP(machine);
details.setUsername(props.getProperty("username"));
details.setZones(this.serviceName);
return details;
}
private String chooseMachine(final Properties props) throws UnknownHostException, ErrorStatusException {
final String nodesString = props.getProperty("nodes", "");
final String[] nodes = nodesString.split(",");
final Set<String> activeNodes = getAllActiveNodes();
for (final String node : nodes) {
if (node.trim().length() > 0) {
if (isMachineAvailable(node, activeNodes)) {
return node;
}
}
}
return null;
}
private boolean isMachineAvailable(final String node, final Set<String> activeNodes) throws UnknownHostException,
ErrorStatusException {
final Set<String> nodeNames = new HashSet<String>();
nodeNames.add(node);
final InetAddress address = InetAddress.getByName(node);
nodeNames.add(address.getHostAddress());
// nodeNames.add(address.getCanonicalHostName());
nodeNames.add(address.getHostName());
final int sizeBefore = nodeNames.size();
nodeNames.removeAll(activeNodes);
final int sizeAfter = nodeNames.size();
if (sizeAfter != sizeBefore) {
return false;
}
// check for management machine
// if (AgentlessInstaller.checkConnection(node, 4166, 1)) {
// return false;
// }
// check for ssh connection
try {
AgentlessInstaller.checkConnection(node, 22, 10, TimeUnit.SECONDS);
} catch (final Exception e) {
logger.info("Failed connection test on port 22 for machine: " + node);
return false;
}
return true;
}
protected Set<String> getAllActiveNodes() throws ErrorStatusException {
final List<String> machines = this.adminFacade.getMachines();
final HashSet<String> res = new HashSet<String>();
res.addAll(machines);
return res;
// final Set<Object> activeNodes = new HashSet<Object>();
// final List<String> apps = this.adminFacade.getApplicationsList();
// for (final String app : apps) {
// final List<String> services = this.adminFacade.getServicesList(app);
// for (final String service : services) {
// final Map<String, Object> instances =
// this.adminFacade.getInstanceList(app, service);
// final Set<Entry<String, Object>> entries = instances.entrySet();
// for (final Entry<String, Object> entry : entries) {
// activeNodes.add(entry.getValue());
// }
//
// }
// }
// return activeNodes;
}
}