/* * 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.controller; import java.util.Collections; import java.util.List; import java.util.Map; import javax.persistence.RollbackException; import org.apache.ambari.server.AmbariException; import org.apache.ambari.server.orm.dao.ExtensionDAO; import org.apache.ambari.server.orm.dao.ExtensionLinkDAO; 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.stack.ExtensionHelper; import org.apache.ambari.server.stack.StackManager; import org.apache.ambari.server.state.ExtensionInfo; import org.apache.ambari.server.state.StackInfo; import org.apache.ambari.server.state.stack.ExtensionMetainfoXml; import org.apache.ambari.server.utils.VersionUtils; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.inject.Inject; import com.google.inject.Singleton; @Singleton public class AmbariManagementHelper { private final static Logger LOG = LoggerFactory.getLogger(AmbariManagementHelper.class); private ExtensionLinkDAO linkDAO; private ExtensionDAO extensionDAO; private StackDAO stackDAO; @Inject public AmbariManagementHelper(StackDAO stackDAO, ExtensionDAO extensionDAO, ExtensionLinkDAO linkDAO) { this.stackDAO = stackDAO; this.extensionDAO = extensionDAO; this.linkDAO = linkDAO; } /** * This method will create a link between an extension version and a stack version (Extension Link). * * An extension version is like a stack version but it contains custom services. Linking an extension * version to the current stack version allows the cluster to install the custom services contained in * the extension version. */ public void createExtensionLink(StackManager stackManager, StackInfo stackInfo, ExtensionInfo extensionInfo) throws AmbariException { validateCreateExtensionLinkRequest(stackInfo, extensionInfo); ExtensionHelper.validateCreateLink(stackInfo, extensionInfo); ExtensionLinkEntity linkEntity = createExtensionLinkEntity(stackInfo, extensionInfo); stackManager.linkStackToExtension(stackInfo, extensionInfo); try { linkDAO.create(linkEntity); linkEntity = linkDAO.merge(linkEntity); } catch (RollbackException e) { String message = "Unable to create extension link"; LOG.debug(message, e); String errorMessage = message + ", stackName=" + stackInfo.getName() + ", stackVersion=" + stackInfo.getVersion() + ", extensionName=" + extensionInfo.getName() + ", extensionVersion=" + extensionInfo.getVersion(); LOG.warn(errorMessage); throw new AmbariException(errorMessage, e); } } /** * This method will create a link between an extension version and a stack version (Extension Link). * * An extension version is like a stack version but it contains custom services. Linking an extension * version to the current stack version allows the cluster to install the custom services contained in * the extension version. */ public void createExtensionLinks(StackManager stackManager, List<ExtensionInfo> extensions) throws AmbariException { Map<String, List<StackInfo>> stackMap = stackManager.getStacksByName(); for (List<StackInfo> stacks : stackMap.values()) { Collections.sort(stacks); Collections.reverse(stacks); } Collections.sort(extensions); Collections.reverse(extensions); for (ExtensionInfo extension : extensions) { if (extension.isActive() && extension.isAutoLink()) { LOG.debug("Autolink - looking for matching stack versions for extension:{}/{} ", extension.getName(), extension.getVersion()); for (ExtensionMetainfoXml.Stack supportedStack : extension.getStacks()) { List<StackInfo> stacks = stackMap.get(supportedStack.getName()); for (StackInfo stack : stacks) { // If the stack version is not currently linked to a version of the extension and it meets the minimum stack version then link them if (stack.getExtension(extension.getName()) == null && VersionUtils.compareVersions(stack.getVersion(), supportedStack.getVersion()) > -1) { LOG.debug("Autolink - extension: {}/{} stack: {}/{}", extension.getName(), extension.getVersion(), stack.getName(), stack.getVersion()); createExtensionLink(stackManager, stack, extension); } else { LOG.debug("Autolink - not a match extension: {}/{} stack: {}/{}", extension.getName(), extension.getVersion(), stack.getName(), stack.getVersion()); } } } } else { LOG.debug("Autolink - skipping extension: {}/{}. It is either not active or set to autolink.", extension.getName(), extension.getVersion()); } } } /** * Validates the stackInfo and extensionInfo parameters are valid. * If they are then it confirms that the stack and extension are not already linked. */ private void validateCreateExtensionLinkRequest(StackInfo stackInfo, ExtensionInfo extensionInfo) throws AmbariException { if (stackInfo == null) { throw new IllegalArgumentException("Stack should be provided"); } if (extensionInfo == null) { throw new IllegalArgumentException("Extension should be provided"); } if (StringUtils.isBlank(stackInfo.getName()) || StringUtils.isBlank(stackInfo.getVersion()) || StringUtils.isBlank(extensionInfo.getName()) || StringUtils.isBlank(extensionInfo.getVersion())) { throw new IllegalArgumentException("Stack name, stack version, extension name and extension version should be provided"); } ExtensionLinkEntity entity = linkDAO.findByStackAndExtension(stackInfo.getName(), stackInfo.getVersion(), extensionInfo.getName(), extensionInfo.getVersion()); if (entity != null) { throw new AmbariException("The stack and extension are already linked" + ", stackName=" + stackInfo.getName() + ", stackVersion=" + stackInfo.getVersion() + ", extensionName=" + extensionInfo.getName() + ", extensionVersion=" + extensionInfo.getVersion()); } } private ExtensionLinkEntity createExtensionLinkEntity(StackInfo stackInfo, ExtensionInfo extensionInfo) throws AmbariException { StackEntity stack = stackDAO.find(stackInfo.getName(), stackInfo.getVersion()); ExtensionEntity extension = extensionDAO.find(extensionInfo.getName(), extensionInfo.getVersion()); ExtensionLinkEntity linkEntity = new ExtensionLinkEntity(); linkEntity.setStack(stack); linkEntity.setExtension(extension); return linkEntity; } }