package com.sequenceiq.cloudbreak.shell; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; import javax.inject.Inject; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.Banner; import org.springframework.boot.CommandLineRunner; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration; import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.shell.CommandLine; import org.springframework.shell.core.CommandResult; import org.springframework.shell.core.JLineShellComponent; import org.springframework.shell.event.ShellStatus; import org.springframework.shell.event.ShellStatusListener; import com.sequenceiq.cloudbreak.api.model.AccountPreferencesJson; import com.sequenceiq.cloudbreak.api.model.NetworkResponse; import com.sequenceiq.cloudbreak.api.model.PlatformRegionsJson; import com.sequenceiq.cloudbreak.api.model.PlatformVirtualMachinesJson; import com.sequenceiq.cloudbreak.api.model.RDSConfigResponse; import com.sequenceiq.cloudbreak.api.model.SecurityGroupResponse; import com.sequenceiq.cloudbreak.api.model.VmTypeJson; import com.sequenceiq.cloudbreak.client.CloudbreakClient; import com.sequenceiq.cloudbreak.shell.model.Hints; import com.sequenceiq.cloudbreak.shell.model.ShellContext; import com.sequenceiq.cloudbreak.shell.transformer.ResponseTransformer; @Configuration @EnableAutoConfiguration(exclude = {DataSourceAutoConfiguration.class, DataSourceTransactionManagerAutoConfiguration.class, HibernateJpaAutoConfiguration.class}) @ComponentScan(basePackages = "com.sequenceiq.cloudbreak.shell") public class CloudbreakShell implements CommandLineRunner, ShellStatusListener { public static final String DOLLAR = "$"; public static final String SPACE = " "; public static final String EMPTY = ""; public static final String FAILED = "FAILED"; public static final String SUCCESS = "SUCCESS"; @Inject private CommandLine commandLine; @Inject private JLineShellComponent shell; @Inject private ShellContext context; @Inject private CloudbreakClient cloudbreakClient; @Inject private ResponseTransformer responseTransformer; @Value("${sequenceiq.user:}") private String user; @Value("${sequenceiq.password:}") private String password; @Override public void run(String... arg) throws Exception { if ("".equals(user)) { System.out.println("Missing 'sequenceiq.user' parameter!"); return; } if ("".equals(password)) { System.out.println("Missing 'sequenceiq.password' parameter!"); return; } String[] shellCommandsToExecute = commandLine.getShellCommandsToExecute(); if (shellCommandsToExecute != null) { init(); for (String cmd : shellCommandsToExecute) { String replacedCommand = getReplacedString(cmd); CommandResult commandResult = shell.executeCommand(replacedCommand); if (!commandResult.isSuccess()) { String message = Optional.ofNullable(commandResult.getException()).map(Throwable::getMessage).orElse("Unknown error, maybe command not valid"); System.out.println(String.format("%s: [%s] [REASON: %s]", replacedCommand, FAILED, message)); break; } else { System.out.println(String.format("%s: [%s]", replacedCommand, SUCCESS)); } } } else { shell.addShellStatusListener(this); shell.start(); shell.promptLoop(); shell.waitForComplete(); } } private String getReplacedString(String cmd) { String result = cmd; if (result != null) { for (String split : cmd.split(SPACE)) { if (split.startsWith(DOLLAR)) { result = result.replace(split, System.getenv(split.replace(DOLLAR, EMPTY))); } } } return result; } @Override public void onShellStatusChange(ShellStatus oldStatus, ShellStatus newStatus) { if (newStatus.getStatus() == ShellStatus.Status.STARTED) { try { init(); } catch (Exception e) { System.out.println("Can't connect to Cloudbreak"); e.printStackTrace(); shell.executeCommand("quit"); } } } private void init() { //cloudbreak.health(); initResourceAccessibility(); initPlatformVariants(); initEnabledPlatformms(); if (!context.isCredentialAccessible()) { context.setHint(Hints.CREATE_CREDENTIAL); } else { context.setHint(Hints.SELECT_CREDENTIAL); } } public static void main(String[] args) { if ((args.length == 1 && ("--help".equals(args[0]) || "-h".equals(args[0]))) || args.length == 0) { System.out.println( "\nCloudbreak Shell: Interactive command line tool for managing Cloudbreak.\n\n" + "Usage:\n" + " java -jar cloudbreak-shell.jar : Starts Cloudbreak Shell in interactive mode.\n" + " java -jar cloudbreak-shell.jar --cmdfile=<FILE> : Cloudbreak Shell executes commands read from the file.\n\n" + "Options:\n" + " --cloudbreak.address=http[s]://<HOSTNAME>[:PORT] Address of the Cloudbreak Server\n" + " --identity.address=http[s]://<HOSTNAME>[:PORT] Address of the SequenceIQ identity server (not a mandatory parameter)" + " [default: cloudbreak.address + /identity].\n" + " --sequenceiq.user=<USER> Username of the SequenceIQ user.\n" + " --sequenceiq.password=<PASSWORD> Password of the SequenceIQ user.\n" + " --cert.validation=<boolean> Validate SSL certificates, shall be disabled for self signed certificates" + " (not a mandatory parameter) [default: true]." ); } else { if (!VersionedApplication.versionedApplication().showVersionInfo(args)) { try { new SpringApplicationBuilder(CloudbreakShell.class).web(false).bannerMode(Banner.Mode.OFF).run(args); } catch (Exception e) { e.printStackTrace(); System.out.println("Cloudbreak shell cannot be started"); } } } } private void initResourceAccessibility() { initCredentialAccessibility(); initBlueprintAccessibility(); initStackAccessibility(); initRecipeAccessibility(); initSssdConfigAccessibility(); initRdsConfigAccessibility(); Set<NetworkResponse> publics = cloudbreakClient.networkEndpoint().getPublics(); for (NetworkResponse network : publics) { context.putNetwork(network.getId(), network.getCloudPlatform()); } Set<SecurityGroupResponse> securityGroups = cloudbreakClient.securityGroupEndpoint().getPublics(); for (SecurityGroupResponse securityGroup : securityGroups) { context.putSecurityGroup(securityGroup.getId(), securityGroup.getName()); } Set<RDSConfigResponse> rdsConfigResponses = cloudbreakClient.rdsConfigEndpoint().getPublics(); for (RDSConfigResponse rdsConfig: rdsConfigResponses) { context.putRdsConfig(rdsConfig.getId(), rdsConfig.getName()); } } private void initCredentialAccessibility() { if (!cloudbreakClient.credentialEndpoint().getPublics().isEmpty()) { context.setCredentialAccessible(); } } private void initBlueprintAccessibility() { if (!cloudbreakClient.blueprintEndpoint().getPublics().isEmpty()) { context.setBlueprintAccessible(); } } private void initStackAccessibility() { if (!cloudbreakClient.stackEndpoint().getPublics().isEmpty()) { context.setStackAccessible(); } } private void initRecipeAccessibility() { if (!cloudbreakClient.recipeEndpoint().getPublics().isEmpty()) { context.setRecipeAccessible(); } } private void initSssdConfigAccessibility() { if (!cloudbreakClient.sssdConfigEndpoint().getPublics().isEmpty()) { context.setSssdConfigAccessible(); } } private void initRdsConfigAccessibility() { if (!cloudbreakClient.rdsConfigEndpoint().getPublics().isEmpty()) { context.setRdsConfigAccessible(); } } private void initPlatformVariants() { Map<String, Collection<String>> platformToVariants = Collections.emptyMap(); Map<String, Collection<String>> regions = Collections.emptyMap(); Map<String, Collection<String>> volumeTypes = Collections.emptyMap(); Map<String, Map<String, Collection<String>>> availabilityZones = Collections.emptyMap(); Map<String, List<Map<String, String>>> instanceTypes = new HashMap<>(); Map<String, Collection<String>> orchestrators = new HashMap<>(); Map<String, Map<String, Collection<VmTypeJson>>> vmTypesPerZones = new HashMap<>(); Map<String, Map<String, String>> defaultVmTypePerZones = new HashMap<>(); try { platformToVariants = cloudbreakClient.connectorEndpoint().getPlatformVariants().getPlatformToVariants(); PlatformRegionsJson platformRegions = cloudbreakClient.connectorEndpoint().getRegions(); regions = platformRegions.getRegions(); availabilityZones = platformRegions.getAvailabilityZones(); volumeTypes = cloudbreakClient.connectorEndpoint().getDisktypes().getDiskTypes(); orchestrators = cloudbreakClient.connectorEndpoint().getOrchestratortypes().getOrchestrators(); PlatformVirtualMachinesJson platformVirtualMachines = cloudbreakClient.connectorEndpoint().getVmTypes(true); for (Map.Entry<String, Collection<VmTypeJson>> vmCloud : platformVirtualMachines.getVirtualMachines().entrySet()) { List<Map<String, String>> tmp = new ArrayList<>(); for (VmTypeJson vmTypeJson : vmCloud.getValue()) { Map<String, String> map = responseTransformer.transformObjectToStringMap(vmTypeJson); tmp.add(map); } instanceTypes.put(vmCloud.getKey(), tmp); } vmTypesPerZones = platformVirtualMachines.getVmTypesPerZones(); defaultVmTypePerZones = platformVirtualMachines.getDefaultVmTypePerZones(); } catch (Exception e) { System.out.println("Error during retrieving platform variants"); } finally { context.setPlatformToVariantsMap(platformToVariants); context.setRegions(regions); context.setAvailabilityZones(availabilityZones); context.setVmTypesPerZones(vmTypesPerZones); context.setDefaultVmTypePerZones(defaultVmTypePerZones); context.setVolumeTypes(volumeTypes); context.setInstanceTypes(instanceTypes); context.setOrchestrators(orchestrators); } } private void initEnabledPlatformms() { AccountPreferencesJson accountPreferencesJson = cloudbreakClient.accountPreferencesEndpoint().get(); if (accountPreferencesJson.getPlatforms() != null) { String[] values = accountPreferencesJson.getPlatforms().split(","); context.setEnabledPlatforms(new HashSet<>(Arrays.asList(values))); } else { context.setEnabledPlatforms(null); } } }