/** * Copyright (C) 2008 Progress Software, Inc. All rights reserved. * http://fusesource.com * * The software in this package is published under the terms of the AGPL license * a copy of which has been included with this distribution in the license.txt file. */ package org.fusesource.cloudmix.controller.provisioning; 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 java.util.concurrent.Callable; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.fusesource.cloudmix.agent.AgentPoller; import org.fusesource.cloudmix.common.controller.AgentController; import org.fusesource.cloudmix.common.controller.FeatureController; import org.fusesource.cloudmix.common.controller.ProfileController; import org.fusesource.cloudmix.common.dto.AgentCfgUpdate; import org.fusesource.cloudmix.common.dto.ConfigurationUpdate; import org.fusesource.cloudmix.common.dto.Constants; import org.fusesource.cloudmix.common.dto.Dependency; import org.fusesource.cloudmix.common.dto.FeatureDetails; import org.fusesource.cloudmix.common.dto.ProvisioningAction; import org.fusesource.cloudmix.common.dto.ProvisioningHistory; import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.InitializingBean; /** * @version $Revision$ */ public class ProvisioningGridController extends DefaultGridController implements InitializingBean, DisposableBean, Callable<Object> { private static final transient Log LOG = LogFactory.getLog(ProvisioningGridController.class); AgentPoller poller; private long startupProvisioningDelay = 5000L; @Override public String toString() { return "ProvisioningGridController[agentTimout: " + getAgentTimeout() + "]"; } public void afterPropertiesSet() throws Exception { poller = new AgentPoller(this); poller.setInitialPollingDelay(getStartupProvisioningDelay()); poller.afterPropertiesSet(); } public void destroy() throws Exception { if (LOG.isDebugEnabled()) { LOG.debug("in destroy()"); } if (poller != null) { poller.destroy(); } } /** * Lets poll to see if there are any new features we can provision */ public Object call() throws Exception { if (LOG.isDebugEnabled()) { LOG.debug("in call()"); } List<ProvisioningAction> answer = new ArrayList<ProvisioningAction>(); // cleaning up all features from de-activated agents for (AgentController ac : agentTrackers()) { if (ac.isDeActivated() && (ac.getFeatures() != null) && (ac.getFeatures().size() > 0)) { String agentId = ac.getDetails().getId(); for (String fid : ac.getFeatures().toArray(new String[ac.getFeatures().size()])) { removeAgentFromFeature(fid, agentId); ProvisioningHistory history = ac.getHistory(); history.addCfgUpdate(new AgentCfgUpdate(AgentCfgUpdate.PROPERTY_AGENT_FORCE_REGISTER, "true")); } } } for (ProfileController profile : profileControllers()) { String profileID = decodeURL(profile.getDetails().getId()); // the profile was modified so re-deploy everything // TODO maybe something less drastic would do, like only uninstalling the features for which the // cfg overrides were modified if (profile.hasChanged()) { LOG.info("profile '" + profile.getDetails().getId() + "' was updated: initiating redeploy..."); List<String> toRemove = new ArrayList<String>(); for (Dependency dep : profile.getDetails().getFeatures()) { if (dep.hasChanged()) { toRemove.add(dep.getFeatureId()); dep.setChanged(false); } } for (AgentController ac : agentTrackers(profileID)) { for (String featureToRemove : toRemove) { removeAgentFromFeature(featureToRemove, ac.getDetails().getId()); } } profile.setChanged(false); } List<FeatureController> deployableFeatures = profile.getDeployableFeatures(); for (FeatureController fc : deployableFeatures) { String featureId = decodeURL(fc.getId()); Collection<AgentController> agentTrackers = agentTrackers(); AgentController agent = fc.selectAgentForDeployment(profileID, agentTrackers); if (agent == null) { LOG.debug("for feature: " + featureId + " no agent selected from possible agents " + agentTrackers.size()); } else { LOG.debug("for feature: " + featureId + " found adequate agent: " + agent.getDetails()); Map<String, String> cfgOverridesProps = getFeatureConfigurationOverrides(profile, featureId); List<ProvisioningAction> list = addAgentToFeature(agent, fc.getId(), cfgOverridesProps); answer.addAll(list); } } // cleaning up redundant features from agents that still have a profile assigned // (either because we switched profile or because the profile was updated) List<String> featureIds = new ArrayList<String>(); for (Dependency featureDependency : profile.getDetails().getFeatures()) { featureIds.add(featureDependency.getFeatureId()); } for (AgentController ac : agentTrackers(profileID)) { Set<String> featuresToRemove = new HashSet<String>(ac.getFeatures()); featuresToRemove.removeAll(featureIds); for (String fid : featuresToRemove) { removeAgentFromFeature(fid, ac.getDetails().getId()); } } } // cleaning up all features from agents that that do not have a profile assigned anymore // (... or only an unpublished one...) or for (AgentController ac : agentTrackers()) { String assignedProfile = ac.getDetails().getProfile(); boolean agentProfileGone = assignedProfile == null || hasProfileGone(assignedProfile) && !assignedProfile.equals(Constants.WILDCARD_PROFILE_NAME); if (ac.getFeatures() != null && !ac.getFeatures().isEmpty()) { String agentId = ac.getDetails().getId(); String[] featuresCopy = ac.getFeatures().toArray(new String[ac.getFeatures().size()]); if (agentProfileGone) { for (String fid : featuresCopy) { removeAgentFromFeature(fid, agentId); } } else { for (String fid : featuresCopy) { FeatureController featureController = getFeatureController(fid); // if the feature controller has gone, then the feature has gone // either by being deleted itself, or due to the profile going boolean deleteFeature = true; if (featureController != null) { deleteFeature = false; FeatureDetails details = featureController.getDetails(); if (details != null && details.getOwnedByProfileId() != null && hasProfileGone(details.getOwnedByProfileId())) { deleteFeature = true; } } if (deleteFeature) { removeAgentFromFeature(fid, agentId); } } } } } return answer; } /** * Returns true if the given profile ID has been destroyed */ protected boolean hasProfileGone(String assignedProfile) { return getProfileController(assignedProfile) == null; } private Map<String, String> getFeatureConfigurationOverrides(ProfileController profile, String featureId) { Map<String, String> cfgOverridesProps = null; LOG.debug("getFeatureConfigurationOverrides, relevant feature id: " + featureId); LOG.debug("getFeatureConfigurationOverrides, features: " + profile.getDetails().getFeatures().size()); for (Dependency dep : profile.getDetails().getFeatures()) { LOG.debug("getFeatureConfigurationOverrides, dep id: " + dep.getFeatureId()); LOG.debug("getFeatureConfigurationOverrides, dep overrides: " + (dep.getCfgUpdates() == null ? 0 : dep.getCfgUpdates().size())); if (featureId.equals(decodeURL(dep.getFeatureId())) && dep.getCfgUpdates() != null) { cfgOverridesProps = new HashMap<String, String>(dep.getCfgUpdates().size()); for (ConfigurationUpdate cfgUpdate : dep.getCfgUpdates()) { cfgOverridesProps.put(cfgUpdate.getProperty(), cfgUpdate.getValue()); } } } return cfgOverridesProps; } // Properties //------------------------------------------------------------------------- public long getStartupProvisioningDelay() { return startupProvisioningDelay; } public void setStartupProvisioningDelay(long startupProvisioningDelay) { this.startupProvisioningDelay = startupProvisioningDelay; } }