/** * 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.internal; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import org.apache.ambari.server.AmbariException; import org.apache.ambari.server.StaticallyInject; import org.apache.ambari.server.api.resources.OperatingSystemResourceDefinition; import org.apache.ambari.server.api.services.AmbariMetaInfo; import org.apache.ambari.server.controller.AmbariManagementController; import org.apache.ambari.server.controller.predicate.AndPredicate; import org.apache.ambari.server.controller.predicate.EqualsPredicate; import org.apache.ambari.server.controller.predicate.OrPredicate; import org.apache.ambari.server.controller.spi.NoSuchParentResourceException; import org.apache.ambari.server.controller.spi.NoSuchResourceException; import org.apache.ambari.server.controller.spi.Predicate; import org.apache.ambari.server.controller.spi.Request; import org.apache.ambari.server.controller.spi.Resource; import org.apache.ambari.server.controller.spi.Resource.Type; import org.apache.ambari.server.controller.spi.SystemException; import org.apache.ambari.server.controller.spi.UnsupportedPropertyException; import org.apache.ambari.server.controller.utilities.PredicateBuilder; import org.apache.ambari.server.orm.dao.RepositoryVersionDAO; import org.apache.ambari.server.orm.entities.RepositoryVersionEntity; import org.apache.ambari.server.state.ServiceInfo; import org.apache.ambari.server.state.StackId; import org.apache.ambari.server.state.StackInfo; import org.apache.ambari.server.state.repository.ManifestServiceInfo; import org.apache.ambari.server.state.repository.VersionDefinitionXml; import org.apache.ambari.server.state.stack.UpgradePack; import com.google.common.collect.Sets; import com.google.inject.Inject; import com.google.inject.Provider; /** * Resource provider for repository versions resources. */ @StaticallyInject public class CompatibleRepositoryVersionResourceProvider extends ReadOnlyResourceProvider { // ----- Property ID constants --------------------------------------------- public static final String REPOSITORY_VERSION_ID_PROPERTY_ID = "CompatibleRepositoryVersions/id"; public static final String REPOSITORY_VERSION_STACK_NAME_PROPERTY_ID = "CompatibleRepositoryVersions/stack_name"; public static final String REPOSITORY_VERSION_STACK_VERSION_PROPERTY_ID = "CompatibleRepositoryVersions/stack_version"; public static final String REPOSITORY_VERSION_REPOSITORY_VERSION_PROPERTY_ID = "CompatibleRepositoryVersions/repository_version"; public static final String REPOSITORY_VERSION_DISPLAY_NAME_PROPERTY_ID = "CompatibleRepositoryVersions/display_name"; public static final String REPOSITORY_UPGRADES_SUPPORTED_TYPES_ID = "CompatibleRepositoryVersions/upgrade_types"; public static final String REPOSITORY_VERSION_SERVICES = "CompatibleRepositoryVersions/services"; public static final String REPOSITORY_VERSION_STACK_SERVICES = "CompatibleRepositoryVersions/stack_services"; public static final String SUBRESOURCE_OPERATING_SYSTEMS_PROPERTY_ID = new OperatingSystemResourceDefinition().getPluralName(); private static final String REPOSITORY_STACK_VALUE = "stack_value"; private static Set<String> pkPropertyIds = Collections.singleton(REPOSITORY_VERSION_ID_PROPERTY_ID); static Set<String> propertyIds = Sets.newHashSet( REPOSITORY_STACK_VALUE, REPOSITORY_VERSION_ID_PROPERTY_ID, REPOSITORY_VERSION_REPOSITORY_VERSION_PROPERTY_ID, REPOSITORY_VERSION_DISPLAY_NAME_PROPERTY_ID, REPOSITORY_VERSION_STACK_NAME_PROPERTY_ID, REPOSITORY_VERSION_STACK_VERSION_PROPERTY_ID, SUBRESOURCE_OPERATING_SYSTEMS_PROPERTY_ID, REPOSITORY_UPGRADES_SUPPORTED_TYPES_ID, REPOSITORY_VERSION_SERVICES, REPOSITORY_VERSION_STACK_SERVICES); static Map<Type, String> keyPropertyIds = new HashMap<Type, String>() { { put(Type.Stack, REPOSITORY_VERSION_STACK_NAME_PROPERTY_ID); put(Type.StackVersion, REPOSITORY_VERSION_STACK_VERSION_PROPERTY_ID); put(Type.Upgrade, REPOSITORY_UPGRADES_SUPPORTED_TYPES_ID); put(Type.CompatibleRepositoryVersion, REPOSITORY_VERSION_ID_PROPERTY_ID); } }; @Inject private static RepositoryVersionDAO s_repositoryVersionDAO; @Inject private static Provider<AmbariMetaInfo> s_ambariMetaInfo; /** * Create a new resource provider. */ public CompatibleRepositoryVersionResourceProvider(AmbariManagementController amc) { super(propertyIds, keyPropertyIds, amc); } @Override public Set<Resource> getResources(Request request, Predicate predicate) throws SystemException, UnsupportedPropertyException, NoSuchResourceException, NoSuchParentResourceException { final Set<Resource> resources = new HashSet<>(); final Set<String> requestedIds = getRequestPropertyIds(request, predicate); final Set<Map<String, Object>> propertyMaps = getPropertyMaps(predicate); Long currentStackUniqueId = null; Map<Long, CompatibleRepositoryVersion> compatibleRepositoryVersionsMap = new HashMap<>(); StackId stackId = null; // !!! this is the case where the predicate was altered to include all stacks for (Map<String, Object> propertyMap : propertyMaps) { if (propertyMap.containsKey(REPOSITORY_STACK_VALUE)) { stackId = new StackId(propertyMap.get(REPOSITORY_STACK_VALUE).toString()); break; } } if (null == stackId) { if (propertyMaps.size() == 1) { Map<String, Object> propertyMap = propertyMaps.iterator().next(); stackId = getStackInformationFromUrl(propertyMap); } else { LOG.error("Property Maps size is NOT equal to 1. Current 'propertyMaps' size = {}", propertyMaps.size()); } } if (null == stackId) { LOG.error("Could not determine stack to process. Returning empty set."); return resources; } for (RepositoryVersionEntity repositoryVersionEntity : s_repositoryVersionDAO.findByStack(stackId)) { currentStackUniqueId = repositoryVersionEntity.getId(); compatibleRepositoryVersionsMap.put(repositoryVersionEntity.getId(), new CompatibleRepositoryVersion(repositoryVersionEntity)); if (LOG.isDebugEnabled()) { LOG.debug("Added current stack id: {} to map", repositoryVersionEntity.getId()); } } Map<String, UpgradePack> packs = s_ambariMetaInfo.get().getUpgradePacks( stackId.getStackName(), stackId.getStackVersion()); for (UpgradePack up : packs.values()) { if (null != up.getTargetStack()) { StackId targetStackId = new StackId(up.getTargetStack()); List<RepositoryVersionEntity> repositoryVersionEntities = s_repositoryVersionDAO.findByStack(targetStackId); for (RepositoryVersionEntity repositoryVersionEntity : repositoryVersionEntities) { if (compatibleRepositoryVersionsMap.containsKey(repositoryVersionEntity.getId())) { compatibleRepositoryVersionsMap.get(repositoryVersionEntity.getId()).addUpgradePackType(up.getType()); if (LOG.isDebugEnabled()) { LOG.debug("Stack id: {} exists in map. Appended new upgrade type {}" + repositoryVersionEntity.getId(), up.getType()); } } else { CompatibleRepositoryVersion compatibleRepositoryVersionEntity = new CompatibleRepositoryVersion(repositoryVersionEntity); compatibleRepositoryVersionEntity.addUpgradePackType(up.getType()); compatibleRepositoryVersionsMap.put(repositoryVersionEntity.getId(), compatibleRepositoryVersionEntity); if (LOG.isDebugEnabled()) { LOG.debug("Added Stack id: {} to map with upgrade type {}", repositoryVersionEntity.getId(), up.getType()); } } } } else { if (currentStackUniqueId != null) { compatibleRepositoryVersionsMap.get(currentStackUniqueId).addUpgradePackType(up.getType()); if (LOG.isDebugEnabled()) { LOG.debug("Current Stack id: {} retrieved from map. Added upgrade type {}", currentStackUniqueId, up.getType()); } } else { LOG.error("Couldn't retrieve Current stack entry from Map."); } } } for (CompatibleRepositoryVersion entity : compatibleRepositoryVersionsMap.values()) { RepositoryVersionEntity repositoryVersionEntity = entity.getRepositoryVersionEntity(); final Resource resource = new ResourceImpl(Resource.Type.CompatibleRepositoryVersion); setResourceProperty(resource, REPOSITORY_VERSION_ID_PROPERTY_ID, repositoryVersionEntity.getId(), requestedIds); setResourceProperty(resource, REPOSITORY_VERSION_STACK_NAME_PROPERTY_ID, repositoryVersionEntity.getStackName(), requestedIds); setResourceProperty(resource, REPOSITORY_VERSION_STACK_VERSION_PROPERTY_ID, repositoryVersionEntity.getStackVersion(), requestedIds); setResourceProperty(resource, REPOSITORY_VERSION_DISPLAY_NAME_PROPERTY_ID, repositoryVersionEntity.getDisplayName(), requestedIds); setResourceProperty(resource, REPOSITORY_VERSION_REPOSITORY_VERSION_PROPERTY_ID, repositoryVersionEntity.getVersion(), requestedIds); setResourceProperty(resource, REPOSITORY_UPGRADES_SUPPORTED_TYPES_ID, entity.getSupportedTypes(), requestedIds); final VersionDefinitionXml xml; try { xml = repositoryVersionEntity.getRepositoryXml(); } catch (Exception e) { throw new SystemException(String.format("Could not load xml for Repository %s", repositoryVersionEntity.getId()), e); } final StackInfo stack; try { stack = s_ambariMetaInfo.get().getStack(repositoryVersionEntity.getStackName(), repositoryVersionEntity.getStackVersion()); } catch (AmbariException e) { throw new SystemException(String.format("Could not load stack %s for Repository %s", repositoryVersionEntity.getStackId().toString(), repositoryVersionEntity.getId())); } final List<ManifestServiceInfo> stackServices; if (null != xml) { setResourceProperty(resource, REPOSITORY_VERSION_SERVICES, xml.getAvailableServices(stack), requestedIds); stackServices = xml.getStackServices(stack); } else { stackServices = new ArrayList<>(); for (ServiceInfo si : stack.getServices()) { stackServices.add(new ManifestServiceInfo(si.getName(), si.getDisplayName(), si.getComment(), Collections.singleton(si.getVersion()))); } } setResourceProperty(resource, REPOSITORY_VERSION_STACK_SERVICES, stackServices, requestedIds); resources.add(resource); } return resources; } @Override protected Set<String> getPKPropertyIds() { return pkPropertyIds; } /** * Gets the stack id from the request map * * @param propertyMap the request map * @return the StackId, or {@code null} if not found. */ protected StackId getStackInformationFromUrl(Map<String, Object> propertyMap) { if (propertyMap.containsKey(REPOSITORY_VERSION_STACK_NAME_PROPERTY_ID) && propertyMap.containsKey(REPOSITORY_VERSION_STACK_VERSION_PROPERTY_ID)) { return new StackId(propertyMap.get(REPOSITORY_VERSION_STACK_NAME_PROPERTY_ID).toString(), propertyMap.get(REPOSITORY_VERSION_STACK_VERSION_PROPERTY_ID).toString()); } return null; } /** * Identify the case where this is a top-level request, containing ONLY the stack name * and stack version. Determine all other compatible stacks and make a new Predicate * out of them. * * Consider a stack STACK-2.2 that is compatible with 2.3 and 2.4. The result of this * call will be a predicate like so (abbreviated): * <pre> * in -> AndPredicate([stack_name=STACK],[stack_version=2.2]) * out-> OrPredicate( * AndPredicate([stack_name=STACK],[stack_version=2.2]), * AndPredicate([stack_name=STACK],[stack_version=2.3]), * AndPredicate([stack_name=STACK],[stack_version=2.4]) ) * </pre> * * Any input predicate that does not conform to ONLY stack_name/stack_version will * revert to the that predicate (return {@code null}). */ @SuppressWarnings("rawtypes") @Override public Predicate amendPredicate(Predicate predicate) { if (!AndPredicate.class.isInstance(predicate)) { return null; } AndPredicate ap = (AndPredicate) predicate; if (2 != ap.getPropertyIds().size()) { return null; } if (!ap.getPropertyIds().contains(REPOSITORY_VERSION_STACK_NAME_PROPERTY_ID) && !ap.getPropertyIds().contains(REPOSITORY_VERSION_STACK_VERSION_PROPERTY_ID)) { return null; } Predicate[] predicates = ap.getPredicates(); if (!EqualsPredicate.class.isInstance(predicates[0]) || !EqualsPredicate.class.isInstance(predicates[1])) { return null; } EqualsPredicate pred1 = (EqualsPredicate) predicates[0]; EqualsPredicate pred2 = (EqualsPredicate) predicates[1]; StackId stackId = null; if (pred1.getPropertyId().equals(REPOSITORY_VERSION_STACK_NAME_PROPERTY_ID)) { stackId = new StackId(pred1.getValue().toString(), pred2.getValue().toString()); } else { stackId = new StackId(pred2.getValue().toString(), pred1.getValue().toString()); } Map<String, UpgradePack> packs = s_ambariMetaInfo.get().getUpgradePacks( stackId.getStackName(), stackId.getStackVersion()); Set<String> stackIds = new HashSet<>(); for (Entry<String, UpgradePack> entry : packs.entrySet()) { UpgradePack pack = entry.getValue(); String packStack = pack.getTargetStack(); if (null == packStack || !packStack.equals(stackId.toString())) { stackIds.add(packStack); } } // !!! use the one passed in, it already includes the one of interest List<Predicate> usable = new ArrayList<>(); usable.add(predicate); // !!! add predicate for each of the compatible stacks as found by the upgrade packs for (String requiredStack : stackIds) { StackId targetStack = new StackId(requiredStack); Predicate p = new PredicateBuilder().property(REPOSITORY_VERSION_STACK_NAME_PROPERTY_ID).equals(targetStack.getStackName()) .and().property(REPOSITORY_VERSION_STACK_VERSION_PROPERTY_ID).equals(targetStack.getStackVersion()).toPredicate(); usable.add(p); } // !!! add the stack that is used to find compatible versions for Predicate p = new PredicateBuilder().property(REPOSITORY_STACK_VALUE).equals(stackId.toString()).toPredicate(); usable.add(p); p = new OrPredicate(usable.toArray(new Predicate[usable.size()])); return p; } }