/******************************************************************************* * Copyright (c) 2011 GigaSpaces Technologies Ltd. All rights reserved * * 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.esc.shell.commands; import java.io.File; import java.io.FileNotFoundException; import java.io.FilenameFilter; import java.io.IOException; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.logging.Level; import java.util.logging.Logger; import org.apache.commons.lang.StringUtils; import org.apache.felix.gogo.commands.Argument; import org.apache.felix.gogo.commands.Command; import org.apache.felix.gogo.commands.Option; import org.cloudifysource.domain.cloud.Cloud; import org.cloudifysource.dsl.internal.ServiceReader; import org.cloudifysource.dsl.utils.RecipePathResolver; import org.cloudifysource.esc.driver.provisioning.jclouds.DefaultProvisioningDriver; import org.cloudifysource.esc.installer.AgentlessInstaller; import org.cloudifysource.esc.shell.installer.CloudGridAgentBootstrapper; import org.cloudifysource.shell.AdminFacade; import org.cloudifysource.shell.Constants; import org.cloudifysource.shell.GigaShellMain; import org.cloudifysource.shell.ShellUtils; import org.cloudifysource.shell.commands.AbstractGSCommand; import org.cloudifysource.shell.exceptions.CLIException; import org.cloudifysource.shell.exceptions.CLIStatusException; /** * Tears down the remote Cloud. * * Optional arguments: * timeout - The number of minutes to wait until the operation is completed (default: 60 minutes) * force - states whether the management machine be shutdown if other applications are installed * terminate-now - triggers immediate termination of all resources, without performing uninstall to applications * * @author barakme, adaml * */ @Command(scope = "cloudify", name = "teardown-cloud", description = "Terminates management machines.") public class TeardownCloud extends AbstractGSCommand { private static final int DEFAULT_TIMEOUT_MINUTES = 60; private static final int POLLING_INTERVAT_SEC = 10; @Argument(required = true, name = "provider", description = "the cloud provider to use") private String cloudProvider; @Option(required = false, name = "-timeout", description = "The number of minutes to wait until the operation is done. ") private int timeoutInMinutes = DEFAULT_TIMEOUT_MINUTES; @Option(required = false, name = "-force", description = "Should management machine be shutdown if other applications are installed") private boolean force = false; @Option(required = false, name = "-terminate-now", description = "Terminate all resources immediately, without performing uninstall to applications") private boolean terminateNow = false; public void setTimeoutInMinutes(final int timeoutInMinutes) { this.timeoutInMinutes = timeoutInMinutes; } public void setForce(final boolean force) { this.force = force; } public void setTerminateNow(final boolean terminateNow) { this.terminateNow = terminateNow; } @Override protected Object doExecute() throws Exception { if (!confirmTeardown()) { return getFormattedMessage("teardown_aborted"); } if (this.adminFacade == null) { adminFacade = (AdminFacade) session.get(Constants.ADMIN_FACADE); } if (adminFacade.isConnected()) { adminFacade.verifyCloudAdmin(); } else { if (!force && !terminateNow) { throw new CLIException("Please connect to the cloud before tearing down"); } } CloudGridAgentBootstrapper installer = new CloudGridAgentBootstrapper(); RecipePathResolver pathResolver = new RecipePathResolver(); File providerDirectory = null; if (pathResolver.resolveCloud(new File(cloudProvider))) { providerDirectory = pathResolver.getResolved(); } else { throw new CLIStatusException("cloud_driver_file_doesnt_exist", StringUtils.join(pathResolver.getPathsLooked().toArray(), ", ")); } // load the cloud file File cloudFile = findCloudFile(providerDirectory); Cloud cloud = ServiceReader.readCloud(cloudFile); installer.setProviderDirectory(providerDirectory); if (this.adminFacade != null) { installer.setAdminFacade(this.adminFacade); } else { installer.setAdminFacade((AdminFacade) session.get(Constants.ADMIN_FACADE)); } installer.setProgressInSeconds(POLLING_INTERVAT_SEC); installer.setVerbose(verbose); installer.setForce(force); installer.setTerminateNow(terminateNow); installer.setCloud(cloud); // Note: The cloud driver may be very verbose. This is EXTEREMELY useful // when debugging ESM // issues, but can also clutter up the CLI display. It makes more sense // to temporarily raise the log level here, // so that all of these // messages will not be displayed on the console. limitLoggingLevel(); try { installer.teardownCloudAndWait(timeoutInMinutes, TimeUnit.MINUTES); session.put(Constants.ACTIVE_APP, "default"); GigaShellMain.getInstance().setCurrentApplicationName("default"); return getFormattedMessage("cloud_terminated_successfully", cloudProvider); } finally { installer.close(); restoreLoggingLevel(); } } private boolean confirmTeardown() throws IOException { return ShellUtils.promptUser(session, "teardown_cloud_confirmation_question"); } private static final String[] NON_VERBOSE_LOGGERS = { DefaultProvisioningDriver. class.getName(), AgentlessInstaller.class.getName() }; private final Map<String, Level> loggerStates = new HashMap<String, Level>(); private void limitLoggingLevel() { if (!this.verbose) { loggerStates.clear(); for (String loggerName : NON_VERBOSE_LOGGERS) { Logger provisioningLogger = Logger.getLogger(loggerName); Level logLevelBefore = provisioningLogger.getLevel(); provisioningLogger.setLevel(Level.WARNING); loggerStates.put(loggerName, logLevelBefore); } } } private void restoreLoggingLevel() { if (!verbose) { Set<Entry<String, Level>> entries = loggerStates.entrySet(); for (Entry<String, Level> entry : entries) { Logger provisioningLogger = Logger.getLogger(entry.getKey()); provisioningLogger.setLevel(entry.getValue()); } } } private File findCloudFile(final File providerDirectory) throws FileNotFoundException { if (!providerDirectory.exists() || !providerDirectory.isDirectory()) { throw new FileNotFoundException("Could not find cloud provider directory: " + providerDirectory); } File[] cloudFiles = providerDirectory.listFiles(new FilenameFilter() { @Override public boolean accept(final File dir, final String name) { return name.endsWith("-cloud.groovy"); } }); if (cloudFiles.length == 0) { throw new FileNotFoundException("Could not find a cloud definition file in: " + providerDirectory + ". Definitions file must end with the suffix '-cloud.groovy'"); } else if (cloudFiles.length > 1) { throw new IllegalArgumentException("Found multiple cloud definition files in: " + providerDirectory + ". Only one file may end with the suffix '-cloud.groovy'"); } return cloudFiles[0]; } }