blob: fc49a24b182b090da68ffe5d560af2c44c24a150 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.ambari.server.stack;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.annotation.Nullable;
import javax.xml.XMLConstants;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;
import org.apache.ambari.server.AmbariException;
import org.apache.ambari.server.configuration.Configuration;
import org.apache.ambari.server.controller.AmbariManagementHelper;
import org.apache.ambari.server.metadata.ActionMetadata;
import org.apache.ambari.server.orm.dao.ExtensionDAO;
import org.apache.ambari.server.orm.dao.ExtensionLinkDAO;
import org.apache.ambari.server.orm.dao.MetainfoDAO;
import org.apache.ambari.server.orm.dao.StackDAO;
import org.apache.ambari.server.orm.entities.ExtensionEntity;
import org.apache.ambari.server.orm.entities.ExtensionLinkEntity;
import org.apache.ambari.server.orm.entities.StackEntity;
import org.apache.ambari.server.state.ExtensionInfo;
import org.apache.ambari.server.state.ServiceInfo;
import org.apache.ambari.server.state.StackInfo;
import org.apache.ambari.server.state.stack.OsFamily;
import org.apache.ambari.server.state.stack.ServiceMetainfoXml;
import org.apache.ambari.server.utils.ResourceFilesKeeperHelper;
import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xml.sax.SAXException;
import com.google.inject.assistedinject.Assisted;
import com.google.inject.assistedinject.AssistedInject;
/**
* Manages all stack related behavior including parsing of stacks and providing access to
* stack information.
*/
public class StackManager {
public static final String PROPERTY_SCHEMA_PATH = "configuration-schema.xsd";
private static final String CUSTOM_ACTIONS_DIR="custom_actions";
private static final String HOST_SCRIPTS_DIR="host_scripts";
private static final String DASHBOARDS_DIR="dashboards";
/**
* Delimiter used for parent path string
* Example:
* HDP/2.0.6/HDFS
* common-services/HDFS/2.1.0.2.0
*/
public static String PATH_DELIMITER = "/";
/**
* Prefix used for common services parent path string
*/
public static final String COMMON_SERVICES = "common-services";
/**
* Prefix used for extension services parent path string
*/
public static final String EXTENSIONS = "extensions";
public static final String METAINFO_FILE_NAME = "metainfo.xml";
/**
* Provides access to non-stack server functionality
*/
private StackContext stackContext;
/**
* Logger
*/
private final static Logger LOG = LoggerFactory.getLogger(StackManager.class);
/**
* Map of stack id to stack info
*/
protected Map<String, StackInfo> stackMap = new HashMap<>();
protected Map<String, ServiceModule> commonServiceModules;
protected Map<String, StackModule> stackModules;
protected Map<String, ExtensionModule> extensionModules;
/**
* Map of extension id to extension info
*/
private Map<String, ExtensionInfo> extensionMap = new HashMap<>();
private AmbariManagementHelper helper;
/**
* Constructor. Initialize stack manager.
*
* @param resourcesRoot
* resources root directory
* @param stackRoot
* stack root directory
* @param commonServicesRoot
* common services root directory
* @param extensionRoot
* extensions root directory
* @param osFamily
* the OS family read from resources
* @param validate
* validate all stack and service definitions
* @param refreshArchives
* refresh archive.zip and .hash
* @param metaInfoDAO
* metainfo DAO automatically injected
* @param actionMetadata
* action meta data automatically injected
* @param stackDao
* stack DAO automatically injected
* @param extensionDao
* extension DAO automatically injected
* @param linkDao
* extension link DAO automatically injected
* @param helper
* Ambari management helper automatically injected
*
* @throws AmbariException
* if an exception occurs while processing the stacks
*/
@AssistedInject
public StackManager(@Assisted("resourcesRoot") File resourcesRoot,
@Assisted("stackRoot") File stackRoot,
@Assisted("commonServicesRoot") @Nullable File commonServicesRoot,
@Assisted("extensionRoot") @Nullable File extensionRoot,
@Assisted OsFamily osFamily, @Assisted("validate") boolean validate,
@Assisted("refreshArchives") boolean refreshArchives,
MetainfoDAO metaInfoDAO, ActionMetadata actionMetadata, StackDAO stackDao,
ExtensionDAO extensionDao, ExtensionLinkDAO linkDao, AmbariManagementHelper helper)
throws AmbariException {
LOG.info("Initializing the stack manager...");
if (validate) {
validateStackDirectory(stackRoot);
validateCommonServicesDirectory(commonServicesRoot);
validateExtensionDirectory(extensionRoot);
}
stackMap = new HashMap<>();
stackContext = new StackContext(metaInfoDAO, actionMetadata, osFamily);
extensionMap = new HashMap<>();
this.helper = helper;
parseDirectories(stackRoot, commonServicesRoot, extensionRoot);
//Read the extension links from the DB
for (StackModule module : stackModules.values()) {
StackInfo stack = module.getModuleInfo();
List<ExtensionLinkEntity> entities = linkDao.findByStack(stack.getName(), stack.getVersion());
for (ExtensionLinkEntity entity : entities) {
String name = entity.getExtension().getExtensionName();
String version = entity.getExtension().getExtensionVersion();
String key = name + StackManager.PATH_DELIMITER + version;
ExtensionModule extensionModule = extensionModules.get(key);
if (extensionModule != null) {
LOG.info("Adding extension to stack/version: " + stack.getName() + "/" + stack.getVersion() +
" extension/version: " + name + "/" + version);
//Add the extension to the stack
module.getExtensionModules().put(key, extensionModule);
}
}
}
fullyResolveCommonServices(stackModules, commonServiceModules, extensionModules);
fullyResolveExtensions(stackModules, commonServiceModules, extensionModules);
fullyResolveStacks(stackModules, commonServiceModules, extensionModules);
if(refreshArchives) {
updateArchives(resourcesRoot, stackRoot, stackModules, commonServiceModules, extensionModules);
}
populateDB(stackDao, extensionDao);
}
/***
* Constructor. Initialize StackManager for merging service definitions and creating management packs
* @param stackRoot
* @param commonServicesRoot
*/
public StackManager(File stackRoot, File commonServicesRoot, boolean validate) throws AmbariException{
LOG.info("Initializing the stack manager...");
if (validate) {
validateStackDirectory(stackRoot);
validateCommonServicesDirectory(commonServicesRoot);
}
stackMap = new HashMap<>();
parseDirectories(stackRoot, commonServicesRoot, null);
fullyResolveCommonServices(stackModules, commonServiceModules, extensionModules);
fullyResolveExtensions(stackModules, commonServiceModules, extensionModules);
fullyResolveStacks(stackModules, commonServiceModules, extensionModules);
}
protected void updateArchives(
File resourcesRoot, File stackRoot, Map<String, StackModule> stackModules, Map<String, ServiceModule> commonServiceModules,
Map<String, ExtensionModule> extensionModules ) throws AmbariException {
LOG.info("Refreshing archives ...");
LOG.debug("Refreshing archives for stacks");
for (StackModule stackModule : stackModules.values()) {
LOG.debug("Refreshing archives for stack : " + stackModule.getId());
for(ServiceModule serviceModule : stackModule.getServiceModules().values()) {
String packageDir = serviceModule.getServiceDirectory().getPackageDir();
if(packageDir != null) {
LOG.debug("Refreshing archive for stack service package directory : " + packageDir);
String packageAbsoluteDir = resourcesRoot.getAbsolutePath() + File.separator + packageDir;
ResourceFilesKeeperHelper.updateDirectoryArchive(packageAbsoluteDir, false);
}
}
}
LOG.debug("Refreshing archives for common services");
for(ServiceModule serviceModule : commonServiceModules.values()) {
String packageDir = serviceModule.getServiceDirectory().getPackageDir();
if(packageDir != null) {
LOG.debug("Refreshing archive for common service package directory : " + packageDir);
String packageAbsoluteDir = resourcesRoot.getAbsolutePath() + File.separator + packageDir;
ResourceFilesKeeperHelper.updateDirectoryArchive(packageAbsoluteDir, false);
}
}
LOG.debug("Refreshing archives for extensions");
for(ExtensionModule extensionModule : extensionModules.values()) {
LOG.debug("Refreshing archives for extension module" + extensionModule.getId());
for(ServiceModule serviceModule : extensionModule.getServiceModules().values()) {
String packageDir = serviceModule.getServiceDirectory().getPackageDir();
if(packageDir != null) {
LOG.debug("Refreshing archive for extension service package directory : " + packageDir);
String packageAbsoluteDir = resourcesRoot.getAbsolutePath() + File.separator + packageDir;
ResourceFilesKeeperHelper.updateDirectoryArchive(packageAbsoluteDir, false);
}
}
}
List<String> miscDirs = new ArrayList<String>() {{
add(CUSTOM_ACTIONS_DIR);
add(HOST_SCRIPTS_DIR);
add(DASHBOARDS_DIR);
}};
LOG.debug("Refreshing archives for misc directories");
for(String miscDir : miscDirs) {
LOG.debug("Refreshing archive for misc directory : " + miscDir);
String miscAbsolutePath = resourcesRoot.getAbsolutePath() + File.separator + miscDir;
ResourceFilesKeeperHelper.updateDirectoryArchive(miscAbsolutePath, false);
}
LOG.info("Refreshing archives finished!");
}
protected void parseDirectories(File stackRoot, File commonServicesRoot, File extensionRoot) throws AmbariException {
commonServiceModules = parseCommonServicesDirectory(commonServicesRoot);
stackModules = parseStackDirectory(stackRoot);
LOG.info("About to parse extension directories");
extensionModules = parseExtensionDirectory(extensionRoot);
}
private void populateDB(StackDAO stackDao, ExtensionDAO extensionDao) throws AmbariException {
// for every stack read in, ensure that we have a database entry for it;
// don't put try/catch logic around this since a failure here will
// cause other things to break down the road
Collection<StackInfo> stacks = getStacks();
for(StackInfo stack : stacks){
String stackName = stack.getName();
String stackVersion = stack.getVersion();
if (stackDao.find(stackName, stackVersion) == null) {
LOG.info("Adding stack {}-{} to the database", stackName, stackVersion);
StackEntity stackEntity = new StackEntity();
stackEntity.setStackName(stackName);
stackEntity.setStackVersion(stackVersion);
stackDao.create(stackEntity);
}
}
// for every extension read in, ensure that we have a database entry for it;
// don't put try/catch logic around this since a failure here will
// cause other things to break down the road
Collection<ExtensionInfo> extensions = getExtensions();
for(ExtensionInfo extension : extensions){
String extensionName = extension.getName();
String extensionVersion = extension.getVersion();
if (extensionDao.find(extensionName, extensionVersion) == null) {
LOG.info("Adding extension {}-{} to the database", extensionName, extensionVersion);
ExtensionEntity extensionEntity = new ExtensionEntity();
extensionEntity.setExtensionName(extensionName);
extensionEntity.setExtensionVersion(extensionVersion);
extensionDao.create(extensionEntity);
}
}
createLinks();
}
/**
* Attempts to automatically create links between extension versions and stack versions.
* This is limited to 'active' extensions that have the 'autolink' attribute set (in the metainfo.xml).
* Stack versions are selected based on the minimum stack versions that the extension supports.
* The extension and stack versions are processed in order of most recent to oldest.
* In this manner, the newest extension version will be autolinked before older extension versions.
* If a different version of the same extension is already linked to a stack version then that stack version
* will be skipped.
*/
private void createLinks() {
LOG.info("Creating links");
Collection<ExtensionInfo> extensions = getExtensions();
Set<String> names = new HashSet<>();
for(ExtensionInfo extension : extensions){
names.add(extension.getName());
}
for(String name : names) {
createLinksForExtension(name);
}
}
/**
* Attempts to automatically create links between versions of a particular extension and stack versions they support.
* This is limited to 'active' extensions that have the 'autolink' attribute set (in the metainfo.xml).
* Stack versions are selected based on the minimum stack versions that the extension supports.
* The extension and stack versions are processed in order of most recent to oldest.
* In this manner, the newest extension version will be autolinked before older extension versions.
* If a different version of the same extension is already linked to a stack version then that stack version
* will be skipped.
*/
private void createLinksForExtension(String name) {
Collection<ExtensionInfo> collection = getExtensions(name);
List<ExtensionInfo> extensions = new ArrayList<>(collection.size());
extensions.addAll(collection);
try {
helper.createExtensionLinks(this, extensions);
}
catch (AmbariException e) {
String msg = String.format("Failed to create link for extension: %s with exception: %s", name, e.getMessage());
LOG.error(msg);
}
}
/**
* Obtain the stack info specified by name and version.
*
* @param name name of the stack
* @param version version of the stack
* @return The stack corresponding to the specified name and version.
* If no matching stack exists, null is returned.
*/
public StackInfo getStack(String name, String version) {
return stackMap.get(name + StackManager.PATH_DELIMITER + version);
}
/**
* Obtain all stacks for the given name.
*
* @param name stack name
* @return A collection of all stacks with the given name.
* If no stacks match the specified name, an empty collection is returned.
*/
public Collection<StackInfo> getStacks(String name) {
Collection<StackInfo> stacks = new HashSet<>();
for (StackInfo stack: stackMap.values()) {
if (stack.getName().equals(name)) {
stacks.add(stack);
}
}
return stacks;
}
/**
* Obtain all a map of all stacks by name.
*
* @return A map of all stacks with the name as the key.
*/
public Map<String, List<StackInfo>> getStacksByName() {
Map<String, List<StackInfo>> stacks = new HashMap<>();
for (StackInfo stack: stackMap.values()) {
List<StackInfo> list = stacks.get(stack.getName());
if (list == null) {
list = new ArrayList<>();
stacks.put(stack.getName(), list);
}
list.add(stack);
}
return stacks;
}
/**
* Obtain all stacks.
*
* @return collection of all stacks
*/
public Collection<StackInfo> getStacks() {
return stackMap.values();
}
/**
* Obtain the extension info specified by name and version.
*
* @param name name of the extension
* @param version version of the extension
* @return The extension corresponding to the specified name and version.
* If no matching stack exists, null is returned.
*/
public ExtensionInfo getExtension(String name, String version) {
return extensionMap.get(name + StackManager.PATH_DELIMITER + version);
}
/**
* Obtain all extensions for the given name.
*
* @param name extension name
* @return A collection of all extensions with the given name.
* If no extensions match the specified name, an empty collection is returned.
*/
public Collection<ExtensionInfo> getExtensions(String name) {
Collection<ExtensionInfo> extensions = new HashSet<>();
for (ExtensionInfo extension: extensionMap.values()) {
if (extension.getName().equals(name)) {
extensions.add(extension);
}
}
return extensions;
}
/**
* Obtain all extensions.
*
* @return collection of all extensions
*/
public Collection<ExtensionInfo> getExtensions() {
return extensionMap.values();
}
/**
* Determine if all tasks which update stack repo urls have completed.
*
* @return true if all of the repo update tasks have completed; false otherwise
*/
public boolean haveAllRepoUrlsBeenResolved() {
if(stackContext == null) {
return true;
}
return stackContext.haveAllRepoTasksCompleted();
}
/**
* Fully resolve all stacks.
*
* @param stackModules map of stack id which contains name and version to stack module.
* @param commonServiceModules map of common service id which contains name and version to stack module.
* @throws AmbariException if unable to resolve all stacks
*/
private void fullyResolveStacks(
Map<String, StackModule> stackModules, Map<String, ServiceModule> commonServiceModules, Map<String, ExtensionModule> extensions)
throws AmbariException {
// Resolve all stacks without finalizing the stacks.
for (StackModule stack : stackModules.values()) {
if (stack.getModuleState() == ModuleState.INIT) {
stack.resolve(null, stackModules, commonServiceModules, extensions);
}
}
// Finalize the common services and stacks to remove sub-modules marked for deletion.
// Finalizing the stacks AFTER all stacks are resolved ensures that the sub-modules marked for deletion are
// inherited into the child module when explicit parent is defined and thereby ensuring all modules from parent module
// are inlined into the child module even if the module is marked for deletion.
for(ServiceModule commonService : commonServiceModules.values()) {
commonService.finalizeModule();
}
for (ExtensionModule extension : extensions.values()) {
extension.finalizeModule();
}
for (StackModule stack : stackModules.values()) {
stack.finalizeModule();
}
// Execute all of the repo tasks in a single thread executor
if(stackContext != null) {
stackContext.executeRepoTasks();
}
}
/**
* Fully resolve common services.
*
* @param stackModules map of stack id which contains name and version to stack module.
* @param commonServiceModules map of common service id which contains name and version to common service module.
* @throws AmbariException if unable to resolve all common services
*/
private void fullyResolveCommonServices(
Map<String, StackModule> stackModules, Map<String, ServiceModule> commonServiceModules, Map<String, ExtensionModule> extensions)
throws AmbariException {
for(ServiceModule commonService : commonServiceModules.values()) {
if (commonService.getModuleState() == ModuleState.INIT) {
commonService.resolveCommonService(stackModules, commonServiceModules, extensions);
}
}
}
/**
* Fully resolve extensions.
*
* @param extensionModules map of extension id which contains name and version to extension module.
* @param stackModules map of stack id which contains name and version to stack module.
* @param commonServiceModules map of common service id which contains name and version to common service module.
* @throws AmbariException if unable to resolve all extensions
*/
private void fullyResolveExtensions(Map<String, StackModule> stackModules, Map<String, ServiceModule> commonServiceModules,
Map<String, ExtensionModule> extensionModules)
throws AmbariException {
for(ExtensionModule extensionModule : extensionModules.values()) {
if (extensionModule.getModuleState() == ModuleState.INIT) {
extensionModule.resolve(null, stackModules, commonServiceModules, extensionModules);
}
}
}
/**
* Validate that the specified common services root is a valid directory.
*
* @param commonServicesRoot the common services root directory to validate
* @throws AmbariException if the specified common services root directory is invalid
*/
private void validateCommonServicesDirectory(File commonServicesRoot) throws AmbariException {
if(commonServicesRoot != null) {
LOG.info("Validating common services directory {} ...",
commonServicesRoot);
String commonServicesRootAbsolutePath = commonServicesRoot.getAbsolutePath();
if (LOG.isDebugEnabled()) {
LOG.debug("Loading common services information, commonServicesRoot = {}", commonServicesRootAbsolutePath);
}
if (!commonServicesRoot.isDirectory() && !commonServicesRoot.exists()) {
throw new AmbariException("" + Configuration.COMMON_SERVICES_DIR_PATH
+ " should be a directory with common services"
+ ", commonServicesRoot = " + commonServicesRootAbsolutePath);
}
}
}
/**
* Validate that the specified stack root is a valid directory.
*
* @param stackRoot the stack root directory to validate
* @throws AmbariException if the specified stack root directory is invalid
*/
private void validateStackDirectory(File stackRoot) throws AmbariException {
LOG.info("Validating stack directory {} ...", stackRoot);
String stackRootAbsPath = stackRoot.getAbsolutePath();
if (LOG.isDebugEnabled()) {
LOG.debug("Loading stack information, stackRoot = {}", stackRootAbsPath);
}
if (!stackRoot.isDirectory() && !stackRoot.exists()) {
throw new AmbariException("" + Configuration.METADATA_DIR_PATH
+ " should be a directory with stack"
+ ", stackRoot = " + stackRootAbsPath);
}
Validator validator = getPropertySchemaValidator();
validateAllPropertyXmlsInFolderRecursively(stackRoot, validator);
}
public static Validator getPropertySchemaValidator() throws AmbariException {
SchemaFactory factory =
SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema schema;
ClassLoader classLoader = StackManager.class.getClassLoader();
try {
schema = factory.newSchema(classLoader.getResource(PROPERTY_SCHEMA_PATH));
} catch (SAXException e) {
throw new AmbariException(String.format("Failed to parse property schema file %s", PROPERTY_SCHEMA_PATH), e);
}
return schema.newValidator();
}
public static void validateAllPropertyXmlsInFolderRecursively(File stackRoot, Validator validator) throws AmbariException {
Collection<File> files = FileUtils.listFiles(stackRoot, new String[]{"xml"}, true);
for (File file : files) {
try {
if (file.getParentFile().getName().contains("configuration")) {
validator.validate(new StreamSource(file));
}
} catch (Exception e) {
String msg = String.format("File %s didn't pass the validation. Error message is : %s", file.getAbsolutePath(), e.getMessage());
LOG.error(msg);
throw new AmbariException(msg);
}
}
}
/**
* Validate that the specified extension root is a valid directory.
*
* @param extensionRoot the extension root directory to validate
* @throws AmbariException if the specified extension root directory is invalid
*/
private void validateExtensionDirectory(File extensionRoot) throws AmbariException {
LOG.info("Validating extension directory {} ...", extensionRoot);
if (extensionRoot == null)
return;
String extensionRootAbsPath = extensionRoot.getAbsolutePath();
if (LOG.isDebugEnabled()) {
LOG.debug("Loading extension information, extensionRoot = {}", extensionRootAbsPath);
}
//For backwards compatibility extension directory may not exist
if (extensionRoot.exists() && !extensionRoot.isDirectory()) {
throw new AmbariException("" + Configuration.METADATA_DIR_PATH
+ " should be a directory"
+ ", extensionRoot = " + extensionRootAbsPath);
}
}
/**
* Parse the specified common services root directory
*
* @param commonServicesRoot the common services root directory to parse
* @return map of common service id which contains name and version to common service module.
* @throws AmbariException if unable to parse all common services
*/
private Map<String, ServiceModule> parseCommonServicesDirectory(File commonServicesRoot) throws AmbariException {
Map<String, ServiceModule> commonServiceModules = new HashMap<>();
if(commonServicesRoot != null) {
File[] commonServiceFiles = commonServicesRoot.listFiles(StackDirectory.FILENAME_FILTER);
for (File commonService : commonServiceFiles) {
if (commonService.isFile()) {
continue;
}
for (File serviceFolder : commonService.listFiles(StackDirectory.FILENAME_FILTER)) {
String serviceName = serviceFolder.getParentFile().getName();
String serviceVersion = serviceFolder.getName();
ServiceDirectory serviceDirectory = new CommonServiceDirectory(serviceFolder.getPath());
ServiceMetainfoXml metaInfoXml = serviceDirectory.getMetaInfoFile();
if (metaInfoXml != null) {
if (metaInfoXml.isValid()) {
for (ServiceInfo serviceInfo : metaInfoXml.getServices()) {
ServiceModule serviceModule = new ServiceModule(stackContext, serviceInfo, serviceDirectory, true);
String commonServiceKey = serviceInfo.getName() + StackManager.PATH_DELIMITER + serviceInfo.getVersion();
commonServiceModules.put(commonServiceKey, serviceModule);
}
} else {
ServiceModule serviceModule = new ServiceModule(stackContext, new ServiceInfo(), serviceDirectory, true);
serviceModule.setValid(false);
serviceModule.addErrors(metaInfoXml.getErrors());
commonServiceModules.put(metaInfoXml.getSchemaVersion(), serviceModule);
metaInfoXml.setSchemaVersion(null);
}
}
}
}
}
return commonServiceModules;
}
/**
* Parse the specified stack root directory
*
* @param stackRoot the stack root directory to parse
* @return map of stack id which contains name and version to stack module.
* @throws AmbariException if unable to parse all stacks
*/
private Map<String, StackModule> parseStackDirectory(File stackRoot) throws AmbariException {
Map<String, StackModule> stackModules = new HashMap<>();
File[] stackFiles = stackRoot.listFiles(StackDirectory.FILENAME_FILTER);
for (File stack : stackFiles) {
if (stack.isFile()) {
continue;
}
for (File stackFolder : stack.listFiles(StackDirectory.FILENAME_FILTER)) {
if (stackFolder.isFile()) {
continue;
}
String stackName = stackFolder.getParentFile().getName();
String stackVersion = stackFolder.getName();
StackModule stackModule = new StackModule(new StackDirectory(stackFolder.getPath()), stackContext);
String stackKey = stackName + StackManager.PATH_DELIMITER + stackVersion;
stackModules.put(stackKey, stackModule);
stackMap.put(stackKey, stackModule.getModuleInfo());
}
}
if (stackMap.isEmpty()) {
throw new AmbariException("Unable to find stack definitions under " +
"stackRoot = " + stackRoot.getAbsolutePath());
}
return stackModules;
}
public void linkStackToExtension(StackInfo stack, ExtensionInfo extension) throws AmbariException {
stack.addExtension(extension);
}
public void unlinkStackAndExtension(StackInfo stack, ExtensionInfo extension) throws AmbariException {
stack.removeExtension(extension);
}
/**
* Parse the specified extension root directory
*
* @param extensionRoot the extension root directory to parse
* @return map of extension id which contains name and version to extension module.
* @throws AmbariException if unable to parse all extensions
*/
private Map<String, ExtensionModule> parseExtensionDirectory(File extensionRoot) throws AmbariException {
Map<String, ExtensionModule> extensionModules = new HashMap<>();
if (extensionRoot == null || !extensionRoot.exists())
return extensionModules;
File[] extensionFiles = extensionRoot.listFiles(StackDirectory.FILENAME_FILTER);
for (File extensionNameFolder : extensionFiles) {
if (extensionNameFolder.isFile()) {
continue;
}
for (File extensionVersionFolder : extensionNameFolder.listFiles(StackDirectory.FILENAME_FILTER)) {
if (extensionVersionFolder.isFile()) {
continue;
}
String extensionName = extensionNameFolder.getName();
String extensionVersion = extensionVersionFolder.getName();
ExtensionModule extensionModule = new ExtensionModule(new ExtensionDirectory(extensionVersionFolder.getPath()), stackContext);
String extensionKey = extensionName + StackManager.PATH_DELIMITER + extensionVersion;
extensionModules.put(extensionKey, extensionModule);
extensionMap.put(extensionKey, extensionModule.getModuleInfo());
}
}
if (stackMap.isEmpty()) {
throw new AmbariException("Unable to find extension definitions under " +
"extensionRoot = " + extensionRoot.getAbsolutePath());
}
return extensionModules;
}
public void removeStack(StackEntity stackEntity) {
String stackKey = stackEntity.getStackName() + StackManager.PATH_DELIMITER + stackEntity.getStackVersion();
stackMap.remove(stackKey);
}
}