package com.sequenceiq.cloudbreak.service.cluster.flow; import static com.sequenceiq.cloudbreak.common.type.OrchestratorConstants.SALT; import static com.sequenceiq.cloudbreak.common.type.OrchestratorConstants.SWARM; import java.io.IOException; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; import javax.annotation.Resource; import javax.inject.Inject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import com.google.common.collect.Sets; import com.sequenceiq.cloudbreak.api.model.ExecutionType; import com.sequenceiq.cloudbreak.api.model.FileSystemConfiguration; import com.sequenceiq.cloudbreak.api.model.FileSystemType; import com.sequenceiq.cloudbreak.core.CloudbreakException; import com.sequenceiq.cloudbreak.core.CloudbreakSecuritySetupException; import com.sequenceiq.cloudbreak.core.bootstrap.service.OrchestratorTypeResolver; import com.sequenceiq.cloudbreak.domain.Cluster; import com.sequenceiq.cloudbreak.domain.FileSystem; import com.sequenceiq.cloudbreak.domain.HostGroup; import com.sequenceiq.cloudbreak.domain.HostMetadata; import com.sequenceiq.cloudbreak.domain.Orchestrator; import com.sequenceiq.cloudbreak.domain.Recipe; import com.sequenceiq.cloudbreak.domain.SssdConfig; import com.sequenceiq.cloudbreak.domain.Stack; import com.sequenceiq.cloudbreak.service.TlsSecurityService; import com.sequenceiq.cloudbreak.service.cluster.flow.blueprint.BlueprintProcessor; import com.sequenceiq.cloudbreak.service.cluster.flow.blueprint.SmartSenseConfigProvider; import com.sequenceiq.cloudbreak.service.cluster.flow.filesystem.FileSystemConfigurator; import com.sequenceiq.cloudbreak.util.FileReaderUtils; import com.sequenceiq.cloudbreak.util.JsonUtil; @Component public class RecipeEngine { public static final Set<String> DEFAULT_RECIPES = Sets.newHashSet("hdfs-home", "smartsense-capture-schedule"); private static final String SSSD_CONFIG = "sssd-config-"; private static final Logger LOGGER = LoggerFactory.getLogger(RecipeEngine.class); @Inject private TlsSecurityService tlsSecurityService; @Inject private OrchestratorTypeResolver orchestratorTypeResolver; @Resource private Map<FileSystemType, FileSystemConfigurator> fileSystemConfigurators; @Inject private RecipeBuilder recipeBuilder; @Inject private OrchestratorRecipeExecutor orchestratorRecipeExecutor; @Inject private BlueprintProcessor blueprintProcessor; @Inject private SmartSenseConfigProvider smartSenseConfigProvider; public void executePreInstall(Stack stack, Set<HostGroup> hostGroups) throws CloudbreakException { Orchestrator orchestrator = stack.getOrchestrator(); if (recipesSupportedOnOrchestrator(orchestrator)) { configureSssd(stack, null); addFsRecipes(stack, hostGroups); addSmartSenseRecipe(stack, hostGroups); boolean recipesFound = recipesFound(hostGroups); if (recipesFound) { orchestratorRecipeExecutor.uploadRecipes(stack, hostGroups); orchestratorRecipeExecutor.preInstall(stack); } } } public void executeUpscalePreInstall(Stack stack, HostGroup hostGroup, Set<HostMetadata> metaData) throws CloudbreakException { Orchestrator orchestrator = stack.getOrchestrator(); if (recipesSupportedOnOrchestrator(orchestrator)) { Set<HostGroup> hostGroups = Collections.singleton(hostGroup); configureSssd(stack, metaData); addFsRecipes(stack, hostGroups); boolean recipesFound = recipesFound(hostGroups); if (recipesFound) { orchestratorRecipeExecutor.preInstall(stack); } } } public void executePostInstall(Stack stack) throws CloudbreakException { Orchestrator orchestrator = stack.getOrchestrator(); if (recipesSupportedOnOrchestrator(orchestrator)) { orchestratorRecipeExecutor.postInstall(stack); } } public void executeUpscalePostInstall(Stack stack, Set<HostMetadata> hostMetadata) throws CloudbreakException { Orchestrator orchestrator = stack.getOrchestrator(); if (recipesSupportedOnOrchestrator(orchestrator)) { orchestratorRecipeExecutor.postInstall(stack); } } private void addFsRecipes(Stack stack, Set<HostGroup> hostGroups) throws CloudbreakException { String orchestrator = stack.getOrchestrator().getType(); if (SWARM.equals(orchestrator) || SALT.equals(orchestrator)) { Cluster cluster = stack.getCluster(); String blueprintText = cluster.getBlueprint().getBlueprintText(); FileSystem fs = cluster.getFileSystem(); if (fs != null) { try { addFsRecipesToHostGroups(hostGroups, blueprintText, fs); } catch (IOException e) { throw new CloudbreakException("can not add FS recipes to host groups", e); } } addHDFSRecipe(cluster, blueprintText, hostGroups); } } private void addFsRecipesToHostGroups(Set<HostGroup> hostGroups, String blueprintText, FileSystem fs) throws IOException { String scriptName = fs.getType().toLowerCase(); FileSystemConfigurator fsConfigurator = fileSystemConfigurators.get(FileSystemType.valueOf(fs.getType())); FileSystemConfiguration fsConfiguration = getFileSystemConfiguration(fs); List<RecipeScript> recipeScripts = fsConfigurator.getScripts(fsConfiguration); List<Recipe> fsRecipes = recipeBuilder.buildRecipes(scriptName, recipeScripts); for (int i = 0; i < fsRecipes.size(); i++) { RecipeScript recipeScript = recipeScripts.get(i); Recipe recipe = fsRecipes.get(i); for (HostGroup hostGroup : hostGroups) { if (ExecutionType.ALL_NODES == recipeScript.getExecutionType()) { hostGroup.addRecipe(recipe); } else if (ExecutionType.ONE_NODE == recipeScript.getExecutionType() && isComponentPresent(blueprintText, "NAMENODE", hostGroup)) { hostGroup.addRecipe(recipe); break; } } } } private FileSystemConfiguration getFileSystemConfiguration(FileSystem fs) throws IOException { String json = JsonUtil.writeValueAsString(fs.getProperties()); return (FileSystemConfiguration) JsonUtil.readValue(json, FileSystemType.valueOf(fs.getType()).getClazz()); } private void configureSssd(Stack stack, Set<HostMetadata> hostMetadata) throws CloudbreakException { if (stack.getCluster().getSssdConfig() != null) { List<String> sssdPayload = generateSssdRecipePayload(stack); throw new IllegalStateException("implementation required"); } } private List<String> generateSssdRecipePayload(Stack stack) throws CloudbreakSecuritySetupException { SssdConfig config = stack.getCluster().getSssdConfig(); List<String> payload; if (config.getConfiguration() != null) { String configName = SSSD_CONFIG + config.getId(); payload = Collections.singletonList(configName); } else { payload = Arrays.asList("-", config.getProviderType().getType(), config.getUrl(), config.getSchema().getRepresentation(), config.getBaseSearch(), config.getTlsReqcert().getRepresentation(), config.getAdServer(), config.getKerberosServer(), config.getKerberosRealm()); } return payload; } private boolean recipesFound(Set<HostGroup> hostGroups) { for (HostGroup hostGroup : hostGroups) { if (!hostGroup.getRecipes().isEmpty()) { return true; } } return false; } private void addHDFSRecipe(Cluster cluster, String blueprintText, Set<HostGroup> hostGroups) { try { for (HostGroup hostGroup : hostGroups) { if (isComponentPresent(blueprintText, "NAMENODE", hostGroup)) { String script = FileReaderUtils.readFileFromClasspath("scripts/hdfs-home.sh").replaceAll("\\$USER", cluster.getUserName()); RecipeScript recipeScript = new RecipeScript(script, ClusterLifecycleEvent.POST_INSTALL); Recipe recipe = recipeBuilder.buildRecipes("hdfs-home", Collections.singletonList(recipeScript)).get(0); hostGroup.addRecipe(recipe); break; } } } catch (IOException e) { LOGGER.warn("Cannot create HDFS home dir recipe", e); } } private void addSmartSenseRecipe(Stack stack, Set<HostGroup> hostGroups) { try { Cluster cluster = stack.getCluster(); String blueprintText = cluster.getBlueprint().getBlueprintText(); if (smartSenseConfigProvider.smartSenseIsConfigurable(blueprintText)) { for (HostGroup hostGroup : hostGroups) { if (isComponentPresent(blueprintText, "HST_AGENT", hostGroup)) { String script = FileReaderUtils.readFileFromClasspath("scripts/smartsense-capture-schedule.sh"); RecipeScript recipeScript = new RecipeScript(script, ClusterLifecycleEvent.POST_INSTALL); Recipe recipe = recipeBuilder.buildRecipes("smartsense-capture-schedule", Collections.singletonList(recipeScript)).get(0); hostGroup.addRecipe(recipe); break; } } } } catch (IOException e) { LOGGER.warn("Cannot create SmartSense caputre schedule setter recipe", e); } } private boolean isComponentPresent(String blueprint, String component, HostGroup hostGroup) { return isComponentPresent(blueprint, component, Sets.newHashSet(hostGroup)); } private boolean isComponentPresent(String blueprint, String component, Set<HostGroup> hostGroups) { for (HostGroup hostGroup : hostGroups) { Set<String> components = blueprintProcessor.getComponentsInHostGroup(blueprint, hostGroup.getName()); if (components.contains(component)) { return true; } } return false; } private boolean recipesSupportedOnOrchestrator(Orchestrator orchestrator) throws CloudbreakException { return !orchestratorTypeResolver.resolveType(orchestrator).containerOrchestrator(); } }