AMBARI-8458. Add support for "add hosts" specifying host name, blueprint name and host group name AMBARI-8437. Fix regression that prvented cluster creation via blueprints
Project: http://git-wip-us.apache.org/repos/asf/ambari/repo Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/058dc168 Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/058dc168 Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/058dc168 Branch: refs/heads/trunk Commit: 058dc168e6173f4206837a1db87d066b372b07b1 Parents: 501785f Author: John Speidel <jspei...@hortonworks.com> Authored: Wed Nov 26 13:35:45 2014 -0500 Committer: John Speidel <jspei...@hortonworks.com> Committed: Wed Nov 26 17:56:02 2014 -0500 ---------------------------------------------------------------------- .../controller/AmbariManagementController.java | 18 - .../AmbariManagementControllerImpl.java | 336 +----------- .../ambari/server/controller/HostRequest.java | 31 +- .../internal/AbstractResourceProvider.java | 2 +- .../internal/BaseBlueprintProcessor.java | 81 +++ .../internal/ClusterControllerImpl.java | 13 +- .../internal/ClusterResourceProvider.java | 46 +- .../internal/ConfigGroupResourceProvider.java | 31 +- .../internal/HostComponentResourceProvider.java | 544 ++++++++++++++++++- .../internal/HostResourceProvider.java | 324 ++++++++--- .../org/apache/ambari/server/state/Cluster.java | 7 +- .../server/state/cluster/ClusterImpl.java | 2 +- .../AmbariManagementControllerTest.java | 145 ++--- .../internal/ClusterResourceProviderTest.java | 9 +- .../HostComponentResourceProviderTest.java | 220 +++++++- .../internal/HostResourceProviderTest.java | 18 +- 16 files changed, 1239 insertions(+), 588 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ambari/blob/058dc168/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementController.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementController.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementController.java index 5af88a8..a1ece2c 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementController.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementController.java @@ -235,24 +235,6 @@ public interface AmbariManagementController { throws AmbariException; /** - * Update the host component identified by the given request object with the - * values carried by the given request object. - * - * - * - * @param requests the request object which defines which host component to - * update and the values to set - * @param requestProperties the request properties - * @param runSmokeTest indicates whether or not to run a smoke test - * - * @return a track action response - * - * @throws AmbariException thrown if the resource cannot be updated - */ - public RequestStatusResponse updateHostComponents( - Set<ServiceComponentHostRequest> requests, Map<String, String> requestProperties, boolean runSmokeTest) throws AmbariException; - - /** * Updates the users specified. * * @param requests the users to modify http://git-wip-us.apache.org/repos/asf/ambari/blob/058dc168/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java index 7d08a7b..0e65a1d 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java @@ -44,7 +44,6 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.EnumMap; -import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashSet; @@ -120,11 +119,8 @@ import org.apache.ambari.server.state.StackId; import org.apache.ambari.server.state.StackInfo; import org.apache.ambari.server.state.State; import org.apache.ambari.server.state.configgroup.ConfigGroupFactory; -import org.apache.ambari.server.state.fsm.InvalidStateTransitionException; import org.apache.ambari.server.state.scheduler.RequestExecutionFactory; -import org.apache.ambari.server.state.svccomphost.ServiceComponentHostDisableEvent; import org.apache.ambari.server.state.svccomphost.ServiceComponentHostInstallEvent; -import org.apache.ambari.server.state.svccomphost.ServiceComponentHostRestoreEvent; import org.apache.ambari.server.state.svccomphost.ServiceComponentHostStartEvent; import org.apache.ambari.server.state.svccomphost.ServiceComponentHostStopEvent; import org.apache.ambari.server.state.svccomphost.ServiceComponentHostUpgradeEvent; @@ -975,9 +971,8 @@ public class AmbariManagementControllerImpl implements AmbariManagementControlle try { if (serviceComponentHostMap == null || !serviceComponentHostMap.containsKey(request.getHostname())) { - ServiceComponentHostNotFoundException e = new ServiceComponentHostNotFoundException(cluster.getClusterName(), + throw new ServiceComponentHostNotFoundException(cluster.getClusterName(), s.getName(), sc.getName(), request.getHostname()); - throw e; } ServiceComponentHost sch = serviceComponentHostMap.get(request.getHostname()); @@ -1309,8 +1304,8 @@ public class AmbariManagementControllerImpl implements AmbariManagementControlle /** * Save cluster update results to retrieve later - * @param clusterRequest - * @param clusterResponse + * @param clusterRequest cluster request info + * @param clusterResponse cluster response info */ public void saveClusterUpdate(ClusterRequest clusterRequest, ClusterResponse clusterResponse) { clusterUpdateCache.put(clusterRequest, clusterResponse); @@ -1705,7 +1700,7 @@ public class AmbariManagementControllerImpl implements AmbariManagementControlle private List<ServiceOsSpecific> getOSSpecificsByFamily(Map<String, ServiceOsSpecific> osSpecifics, String osFamily) { List<ServiceOsSpecific> foundedOSSpecifics = new ArrayList<ServiceOsSpecific>(); for (Entry<String, ServiceOsSpecific> osSpecific : osSpecifics.entrySet()) { - if (osSpecific.getKey().indexOf(osFamily) != -1) { + if (osSpecific.getKey().contains(osFamily)) { foundedOSSpecifics.add(osSpecific.getValue()); } } @@ -1833,8 +1828,8 @@ public class AmbariManagementControllerImpl implements AmbariManagementControlle nowTimestamp, scHost.getDesiredStackVersion().getStackId()); } else if (oldSchState == State.STARTED -// TODO: oldSchState == State.INSTALLED is always false, looks like a bug -// || oldSchState == State.INSTALLED + // TODO: oldSchState == State.INSTALLED is always false, looks like a bug + //|| oldSchState == State.INSTALLED || oldSchState == State.STOPPING) { roleCommand = RoleCommand.STOP; event = new ServiceComponentHostStopEvent( @@ -2105,272 +2100,9 @@ public class AmbariManagementControllerImpl implements AmbariManagementControlle return requestStages; } - @Override - public synchronized RequestStatusResponse updateHostComponents(Set<ServiceComponentHostRequest> requests, - Map<String, String> requestProperties, boolean runSmokeTest) - throws AmbariException { - - if (requests.isEmpty()) { - LOG.warn("Received an empty requests set"); - return null; - } - - Map<String, Map<State, List<ServiceComponentHost>>> changedScHosts = - new HashMap<String, Map<State, List<ServiceComponentHost>>>(); - Collection<ServiceComponentHost> ignoredScHosts = - new ArrayList<ServiceComponentHost>(); - - Set<String> clusterNames = new HashSet<String>(); - Map<String, Map<String, Map<String, Set<String>>>> hostComponentNames = - new HashMap<String, Map<String, Map<String, Set<String>>>>(); - Set<State> seenNewStates = new HashSet<State>(); - Map<ServiceComponentHost, State> directTransitionScHosts = new HashMap<ServiceComponentHost, State>(); - - // We don't expect batch requests for different clusters, that's why - // nothing bad should happen if value is overwritten few times - String maintenanceCluster = null; - - // Determine operation level - Resource.Type reqOpLvl; - if (requestProperties.containsKey(RequestOperationLevel.OPERATION_LEVEL_ID)) { - RequestOperationLevel operationLevel = new RequestOperationLevel(requestProperties); - reqOpLvl = operationLevel.getLevel(); - } else { - String message = "Can not determine request operation level. " + - "Operation level property should " + - "be specified for this request."; - LOG.warn(message); - reqOpLvl = Resource.Type.Cluster; - } - - for (ServiceComponentHostRequest request : requests) { - validateServiceComponentHostRequest(request); - - Cluster cluster = clusters.getCluster(request.getClusterName()); - - if (StringUtils.isEmpty(request.getServiceName())) { - request.setServiceName(findServiceName(cluster, request.getComponentName())); - } - - LOG.info("Received a updateHostComponent request" - + ", clusterName=" + request.getClusterName() - + ", serviceName=" + request.getServiceName() - + ", componentName=" + request.getComponentName() - + ", hostname=" + request.getHostname() - + ", request=" + request); - - clusterNames.add(request.getClusterName()); - - if (clusterNames.size() > 1) { - throw new IllegalArgumentException("Updates to multiple clusters is not" - + " supported"); - } - - if (!hostComponentNames.containsKey(request.getClusterName())) { - hostComponentNames.put(request.getClusterName(), - new HashMap<String, Map<String, Set<String>>>()); - } - if (!hostComponentNames.get(request.getClusterName()) - .containsKey(request.getServiceName())) { - hostComponentNames.get(request.getClusterName()).put( - request.getServiceName(), new HashMap<String, Set<String>>()); - } - if (!hostComponentNames.get(request.getClusterName()) - .get(request.getServiceName()) - .containsKey(request.getComponentName())) { - hostComponentNames.get(request.getClusterName()) - .get(request.getServiceName()).put(request.getComponentName(), - new HashSet<String>()); - } - if (hostComponentNames.get(request.getClusterName()) - .get(request.getServiceName()).get(request.getComponentName()) - .contains(request.getHostname())) { - throw new IllegalArgumentException("Invalid request contains duplicate" - + " hostcomponents"); - } - hostComponentNames.get(request.getClusterName()) - .get(request.getServiceName()).get(request.getComponentName()) - .add(request.getHostname()); - - Service s = cluster.getService(request.getServiceName()); - ServiceComponent sc = s.getServiceComponent( - request.getComponentName()); - ServiceComponentHost sch = sc.getServiceComponentHost( - request.getHostname()); - State oldState = sch.getState(); - State newState = null; - if (request.getDesiredState() != null) { - newState = State.valueOf(request.getDesiredState()); - if (!newState.isValidDesiredState()) { - throw new IllegalArgumentException("Invalid arguments, invalid" - + " desired state, desiredState=" + newState.toString()); - } - } - - // Setting Maintenance state for host component - if (null != request.getMaintenanceState()) { - MaintenanceStateHelper psh = injector.getInstance(MaintenanceStateHelper.class); - - MaintenanceState newMaint = MaintenanceState.valueOf(request.getMaintenanceState()); - MaintenanceState oldMaint = psh.getEffectiveState(sch); - - if (newMaint != oldMaint) { - if (sc.isClientComponent()) { - throw new IllegalArgumentException("Invalid arguments, cannot set " + - "maintenance state on a client component"); - } else if (newMaint.equals(MaintenanceState.IMPLIED_FROM_HOST) - || newMaint.equals(MaintenanceState.IMPLIED_FROM_SERVICE)) { - throw new IllegalArgumentException("Invalid arguments, can only set " + - "maintenance state to one of " + EnumSet.of(MaintenanceState.OFF, MaintenanceState.ON)); - } else { - sch.setMaintenanceState(newMaint); - maintenanceCluster = sch.getClusterName(); - } - } - } - - if (newState == null) { - if (LOG.isDebugEnabled()) { - LOG.debug("Nothing to do for new updateServiceComponentHost request" - + ", clusterName=" + request.getClusterName() - + ", serviceName=" + request.getServiceName() - + ", componentName=" + request.getComponentName() - + ", hostname=" + request.getHostname() - + ", oldState=" + oldState - + ", newDesiredState=null"); - } - continue; - } - - if (sc.isClientComponent() && - !newState.isValidClientComponentState()) { - throw new IllegalArgumentException("Invalid desired state for a client" - + " component"); - } - - seenNewStates.add(newState); - - State oldSchState = sch.getState(); - // Client component reinstall allowed - if (newState == oldSchState && - !sc.isClientComponent() && - !requestProperties.containsKey(sch.getServiceComponentName().toLowerCase())) { - - ignoredScHosts.add(sch); - if (LOG.isDebugEnabled()) { - LOG.debug("Ignoring ServiceComponentHost" - + ", clusterName=" + request.getClusterName() - + ", serviceName=" + s.getName() - + ", componentName=" + sc.getName() - + ", hostname=" + sch.getHostName() - + ", currentState=" + oldSchState - + ", newDesiredState=" + newState); - } - continue; - } - - if (! maintenanceStateHelper.isOperationAllowed(reqOpLvl, sch)) { - ignoredScHosts.add(sch); - if (LOG.isDebugEnabled()) { - LOG.debug("Ignoring ServiceComponentHost" - + ", clusterName=" + request.getClusterName() - + ", serviceName=" + s.getName() - + ", componentName=" + sc.getName() - + ", hostname=" + sch.getHostName()); - } - continue; - } - - if (!State.isValidStateTransition(oldSchState, newState)) { - throw new AmbariException("Invalid transition for" - + " servicecomponenthost" - + ", clusterName=" + cluster.getClusterName() - + ", clusterId=" + cluster.getClusterId() - + ", serviceName=" + sch.getServiceName() - + ", componentName=" + sch.getServiceComponentName() - + ", hostname=" + sch.getHostName() - + ", currentState=" + oldSchState - + ", newDesiredState=" + newState); - } - - if (isDirectTransition(oldSchState, newState)) { - if (LOG.isDebugEnabled()) { - LOG.debug("Handling direct transition update to ServiceComponentHost" - + ", clusterName=" + request.getClusterName() - + ", serviceName=" + s.getName() - + ", componentName=" + sc.getName() - + ", hostname=" + sch.getHostName() - + ", currentState=" + oldSchState - + ", newDesiredState=" + newState); - } - directTransitionScHosts.put(sch, newState); - } else { - if (!changedScHosts.containsKey(sc.getName())) { - changedScHosts.put(sc.getName(), - new EnumMap<State, List<ServiceComponentHost>>(State.class)); - } - if (!changedScHosts.get(sc.getName()).containsKey(newState)) { - changedScHosts.get(sc.getName()).put(newState, - new ArrayList<ServiceComponentHost>()); - } - if (LOG.isDebugEnabled()) { - LOG.debug("Handling update to ServiceComponentHost" - + ", clusterName=" + request.getClusterName() - + ", serviceName=" + s.getName() - + ", componentName=" + sc.getName() - + ", hostname=" + sch.getHostName() - + ", currentState=" + oldSchState - + ", newDesiredState=" + newState); - } - changedScHosts.get(sc.getName()).get(newState).add(sch); - } - } - - if (seenNewStates.size() > 1) { - // FIXME should we handle this scenario - throw new IllegalArgumentException("Cannot handle different desired" - + " state changes for a set of service components at the same time"); - } - - // Perform direct transitions (without task generation) - for (Entry<ServiceComponentHost, State> entry : directTransitionScHosts.entrySet()) { - ServiceComponentHost componentHost = entry.getKey(); - State newState = entry.getValue(); - long timestamp = System.currentTimeMillis(); - ServiceComponentHostEvent event; - componentHost.setDesiredState(newState); - switch (newState) { - case DISABLED: - event = new ServiceComponentHostDisableEvent( - componentHost.getServiceComponentName(), - componentHost.getHostName(), - timestamp); - break; - case INSTALLED: - event = new ServiceComponentHostRestoreEvent( - componentHost.getServiceComponentName(), - componentHost.getHostName(), - timestamp); - break; - default: - throw new AmbariException("Direct transition from " + componentHost.getState() + " to " + newState + " not supported"); - } - try { - componentHost.handleEvent(event); - } catch (InvalidStateTransitionException e) { - //Should not occur, must be covered by previous checks - throw new AmbariException("Internal error - not supported transition", e); - } - } - - Cluster cluster = clusters.getCluster(clusterNames.iterator().next()); - - return createAndPersistStages(cluster, requestProperties, null, null, null, - changedScHosts, ignoredScHosts, runSmokeTest, false); - } - - - private void validateServiceComponentHostRequest(ServiceComponentHostRequest request) { + //todo: for now made this public since is is still used by createHostComponents + //todo: delete after all host component logic is in HostComponentResourceProvider + public void validateServiceComponentHostRequest(ServiceComponentHostRequest request) { if (request.getClusterName() == null || request.getClusterName().isEmpty() || request.getComponentName() == null @@ -2435,31 +2167,6 @@ public class AmbariManagementControllerImpl implements AmbariManagementControlle return serviceName; } - - /** - * Checks if assigning new state does not require performing - * any additional actions - */ - private boolean isDirectTransition(State oldState, State newState) { - switch (newState) { - case INSTALLED: - if (oldState == State.DISABLED) { - return true; - } - break; - case DISABLED: - if (oldState == State.INSTALLED || - oldState == State.INSTALL_FAILED || - oldState == State.UNKNOWN) { - return true; - } - break; - default: - break; - } - return false; - } - @Override public synchronized void updateUsers(Set<UserRequest> requests) throws AmbariException { for (UserRequest request : requests) { @@ -2935,13 +2642,7 @@ public class AmbariManagementControllerImpl implements AmbariManagementControlle @Override public void updateGroups(Set<GroupRequest> requests) throws AmbariException { - for (GroupRequest request: requests) { - final Group group = users.getGroup(request.getGroupName()); - if (group == null) { - continue; - } - // currently no group updates are supported - } + // currently no group updates are supported } protected String getClientHostForRunningAction(Cluster cluster, Service service, ServiceComponent serviceComponent) @@ -3239,16 +2940,16 @@ public class AmbariManagementControllerImpl implements AmbariManagementControlle String errorMessage = null; String[] suffixes = configs.getRepoValidationSuffixes(rr.getOsType()); - for (int i = 0; i < suffixes.length; i++) { - String suffix = String.format(suffixes[i], repoName); + for (String suffix : suffixes) { + String formatted_suffix = String.format(suffix, repoName); String spec = rr.getBaseUrl(); - if (spec.charAt(spec.length()-1) != '/' && suffix.charAt(0) != '/') { - spec = rr.getBaseUrl() + "/" + suffix; - } else if (spec.charAt(spec.length()-1) == '/' && suffix.charAt(0) == '/') { - spec = rr.getBaseUrl() + suffix.substring(1); + if (spec.charAt(spec.length() - 1) != '/' && formatted_suffix.charAt(0) != '/') { + spec = rr.getBaseUrl() + "/" + formatted_suffix; + } else if (spec.charAt(spec.length() - 1) == '/' && formatted_suffix.charAt(0) == '/') { + spec = rr.getBaseUrl() + formatted_suffix.substring(1); } else { - spec = rr.getBaseUrl() + suffix; + spec = rr.getBaseUrl() + formatted_suffix; } try { @@ -3257,8 +2958,7 @@ public class AmbariManagementControllerImpl implements AmbariManagementControlle errorMessage = "Could not access base url . " + rr.getBaseUrl() + " . "; if (LOG.isDebugEnabled()) { errorMessage += ioe; - } - else { + } else { errorMessage += ioe.getMessage(); } bFound = false; http://git-wip-us.apache.org/repos/asf/ambari/blob/058dc168/ambari-server/src/main/java/org/apache/ambari/server/controller/HostRequest.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/HostRequest.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/HostRequest.java index b577bb0..a616839 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/controller/HostRequest.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/HostRequest.java @@ -31,6 +31,9 @@ public class HostRequest { private String rackInfo; private List<ConfigurationRequest> desiredConfigs; // UPDATE private String maintenanceState; // UPDATE + private String blueprint; + private String hostgroup; + private String hostToClone; public HostRequest(String hostname, String clusterName, Map<String, String> hostAttributes) { this.hostname = hostname; @@ -94,6 +97,30 @@ public class HostRequest { return maintenanceState; } + public void setBlueprintName(String blueprintName) { + blueprint = blueprintName; + } + + public String getBlueprintName() { + return blueprint; + } + + public void setHostGroupName(String hostgroupName) { + hostgroup = hostgroupName; + } + + public String getHostGroupName() { + return hostgroup; + } + + public void setHostToClone(String hostname) { + hostToClone = hostname; + } + + public String getHostToClone() { + return hostToClone; + } + public String toString() { StringBuilder sb = new StringBuilder(); sb.append("{ hostname=").append(hostname).append(", clusterName=").append(clusterName); @@ -105,7 +132,9 @@ public class HostRequest { sb.append(","); } ++i; - sb.append(attr.getKey() + "=" + attr.getValue()); + sb.append(attr.getKey()); + sb.append("="); + sb.append(attr.getValue()); } sb.append(']'); } http://git-wip-us.apache.org/repos/asf/ambari/blob/058dc168/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AbstractResourceProvider.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AbstractResourceProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AbstractResourceProvider.java index 2c9179d..69d13b5 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AbstractResourceProvider.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AbstractResourceProvider.java @@ -380,7 +380,7 @@ public abstract class AbstractResourceProvider extends BaseProvider implements R String absCategory = PropertyHelper.getPropertyCategory(entry.getKey()); String propName = PropertyHelper.getPropertyName(entry.getKey()); - if (absCategory.startsWith(desiredConfigKey)) { + if (absCategory != null && absCategory.startsWith(desiredConfigKey)) { config = (null == config) ? new ConfigurationRequest() : config; parseProperties(config, absCategory, propName, entry.getValue().toString()); http://git-wip-us.apache.org/repos/asf/ambari/blob/058dc168/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/BaseBlueprintProcessor.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/BaseBlueprintProcessor.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/BaseBlueprintProcessor.java index 9be73cc..9cfb635 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/BaseBlueprintProcessor.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/BaseBlueprintProcessor.java @@ -24,8 +24,12 @@ import org.apache.ambari.server.AmbariException; import org.apache.ambari.server.StackAccessException; import org.apache.ambari.server.api.services.AmbariMetaInfo; import org.apache.ambari.server.controller.AmbariManagementController; +import org.apache.ambari.server.controller.spi.NoSuchParentResourceException; import org.apache.ambari.server.controller.spi.Resource; +import org.apache.ambari.server.controller.spi.ResourceAlreadyExistsException; +import org.apache.ambari.server.controller.spi.ResourceProvider; import org.apache.ambari.server.controller.spi.SystemException; +import org.apache.ambari.server.controller.spi.UnsupportedPropertyException; import org.apache.ambari.server.orm.dao.BlueprintDAO; import org.apache.ambari.server.orm.entities.BlueprintConfigEntity; import org.apache.ambari.server.orm.entities.BlueprintEntity; @@ -37,6 +41,7 @@ import org.apache.ambari.server.state.ConfigHelper; import org.apache.ambari.server.state.DependencyInfo; import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Map; @@ -442,6 +447,82 @@ public abstract class BaseBlueprintProcessor extends AbstractControllerResourceP throw new IllegalArgumentException(msg); } + /** + * Create host and host_component resources. + * + * @param blueprintHostGroups host groups specified in blueprint + * @param clusterName cluster name + * + * @throws SystemException an unexpected exception occurred + * @throws UnsupportedPropertyException an invalid property was specified + * @throws ResourceAlreadyExistsException attempt to create a host or host_component which already exists + * @throws NoSuchParentResourceException a required parent resource is missing + */ + protected void createHostAndComponentResources(Map<String, HostGroupImpl> blueprintHostGroups, String clusterName) + throws SystemException, UnsupportedPropertyException, ResourceAlreadyExistsException, NoSuchParentResourceException { + + createHostAndComponentResources(blueprintHostGroups, clusterName, getResourceProvider(Resource.Type.Host)); + } + + /** + * Create host and host_component resources via the specified host resource provider. + * + * @param blueprintHostGroups host groups specified in blueprint + * @param clusterName cluster name + * @param hostProvider host resource provider + * + * @throws SystemException an unexpected exception occurred + * @throws UnsupportedPropertyException an invalid property was specified + * @throws ResourceAlreadyExistsException attempt to create a host or host_component which already exists + * @throws NoSuchParentResourceException a required parent resource is missing + */ + protected void createHostAndComponentResources(Map<String, HostGroupImpl> blueprintHostGroups, + String clusterName, + ResourceProvider hostProvider) + throws SystemException, + UnsupportedPropertyException, + ResourceAlreadyExistsException, + NoSuchParentResourceException { + + ResourceProvider hostComponentProvider = getResourceProvider(Resource.Type.HostComponent); + for (HostGroupImpl group : blueprintHostGroups.values()) { + for (String host : group.getHostInfo()) { + Map<String, Object> hostProperties = new HashMap<String, Object>(); + hostProperties.put("Hosts/cluster_name", clusterName); + hostProperties.put("Hosts/host_name", host); + + hostProvider.createResources(new RequestImpl( + null, Collections.singleton(hostProperties), null, null)); + + // create clusters/hosts/host_components + Set<Map<String, Object>> setHostComponentRequestProps = new HashSet<Map<String, Object>>(); + for (String hostComponent : group.getComponents()) { + // AMBARI_SERVER is not recognized by Ambari as a component + if (! hostComponent.equals("AMBARI_SERVER")) { + Map<String, Object> hostComponentProperties = new HashMap<String, Object>(); + hostComponentProperties.put("HostRoles/cluster_name", clusterName); + hostComponentProperties.put("HostRoles/host_name", host); + hostComponentProperties.put("HostRoles/component_name", hostComponent); + setHostComponentRequestProps.add(hostComponentProperties); + } + } + hostComponentProvider.createResources(new RequestImpl( + null, setHostComponentRequestProps, null, null)); + } + } + } + + /** + * Get a config group name based on a bp and host group. + * + * @param bpName blueprint name + * @param hostGroupName host group name + * @return config group name + */ + protected String getConfigurationGroupName(String bpName, String hostGroupName) { + return String.format("%s:%s", bpName, hostGroupName); + } + // ----- Inner Classes ----------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ambari/blob/058dc168/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ClusterControllerImpl.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ClusterControllerImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ClusterControllerImpl.java index 352e0ad..b075d25 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ClusterControllerImpl.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ClusterControllerImpl.java @@ -339,9 +339,20 @@ public class ClusterControllerImpl implements ClusterController { return null; } + + /** + * Provides a non-wrapped resource provider.. + * + * @param type type of resource provider to obtain + * @return a non-wrapped resource provider + */ @Override public ResourceProvider ensureResourceProvider(Type type) { - return ensureResourceProviderWrapper(type); + //todo: in some cases it is necessary to down cast the returned resource provider + //todo: to a concrete type. Perhaps we can provided a 'T getDelegate()' method + //todo: on the wrapper so no casting would be necessary. + ExtendedResourceProviderWrapper providerWrapper = ensureResourceProviderWrapper(type); + return providerWrapper == null ? null : providerWrapper.resourceProvider; } http://git-wip-us.apache.org/repos/asf/ambari/blob/058dc168/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ClusterResourceProvider.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ClusterResourceProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ClusterResourceProvider.java index bb6a39c..15cd8de 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ClusterResourceProvider.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ClusterResourceProvider.java @@ -599,49 +599,6 @@ public class ClusterResourceProvider extends BaseBlueprintProcessor { } /** - * Create host and host_component resources. - * - * @param blueprintHostGroups host groups specified in blueprint - * @param clusterName cluster name - * - * @throws SystemException an unexpected exception occurred - * @throws UnsupportedPropertyException an invalid property was specified - * @throws ResourceAlreadyExistsException attempt to create a host or host_component which already exists - * @throws NoSuchParentResourceException a required parent resource is missing - */ - private void createHostAndComponentResources(Map<String, HostGroupImpl> blueprintHostGroups, String clusterName) - throws SystemException, UnsupportedPropertyException, ResourceAlreadyExistsException, NoSuchParentResourceException { - - ResourceProvider hostProvider = getResourceProvider(Resource.Type.Host); - ResourceProvider hostComponentProvider = getResourceProvider(Resource.Type.HostComponent); - for (HostGroupImpl group : blueprintHostGroups.values()) { - for (String host : group.getHostInfo()) { - Map<String, Object> hostProperties = new HashMap<String, Object>(); - hostProperties.put("Hosts/cluster_name", clusterName); - hostProperties.put("Hosts/host_name", host); - - hostProvider.createResources(new RequestImpl( - null, Collections.singleton(hostProperties), null, null)); - - // create clusters/hosts/host_components - Set<Map<String, Object>> setHostComponentRequestProps = new HashSet<Map<String, Object>>(); - for (String hostComponent : group.getComponents()) { - // AMBARI_SERVER is not recognized by Ambari as a component - if (! hostComponent.equals("AMBARI_SERVER")) { - Map<String, Object> hostComponentProperties = new HashMap<String, Object>(); - hostComponentProperties.put("HostRoles/cluster_name", clusterName); - hostComponentProperties.put("HostRoles/host_name", host); - hostComponentProperties.put("HostRoles/component_name", hostComponent); - setHostComponentRequestProps.add(hostComponentProperties); - } - } - hostComponentProvider.createResources(new RequestImpl( - null, setHostComponentRequestProps, null, null)); - } - } - } - - /** * Create component resources. * * @param blueprintHostGroups host groups specified in blueprint @@ -1158,8 +1115,9 @@ public class ClusterResourceProvider extends BaseBlueprintProcessor { for (Map.Entry<String, Map<String, Config>> entry : groupConfigs.entrySet()) { String service = entry.getKey(); Map<String, Config> serviceConfigs = entry.getValue(); + String hostGroupName = getConfigurationGroupName(entity.getBlueprintName(), entity.getName()); ConfigGroupRequest request = new ConfigGroupRequest( - null, clusterName, entity.getName(), service, "Host Group Configuration", + null, clusterName, hostGroupName, service, "Host Group Configuration", new HashSet<String>(group.getHostInfo()), serviceConfigs); ((ConfigGroupResourceProvider) getResourceProvider(Resource.Type.ConfigGroup)). http://git-wip-us.apache.org/repos/asf/ambari/blob/058dc168/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ConfigGroupResourceProvider.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ConfigGroupResourceProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ConfigGroupResourceProvider.java index c73a8d4..3fcb84b 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ConfigGroupResourceProvider.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ConfigGroupResourceProvider.java @@ -17,8 +17,6 @@ */ package org.apache.ambari.server.controller.internal; -import com.google.common.collect.MapDifference; -import com.google.common.collect.Maps; import org.apache.ambari.server.AmbariException; import org.apache.ambari.server.ClusterNotFoundException; import org.apache.ambari.server.ConfigGroupNotFoundException; @@ -180,19 +178,13 @@ public class ConfigGroupResourceProvider extends for (Map<String, Object> propertyMap : getPropertyMaps(iterator.next(), predicate)) { requests.add(getConfigGroupRequest(propertyMap)); } - - modifyResources(new Command<Void>() { - @Override - public Void invoke() throws AmbariException { - updateConfigGroups(requests); - return null; - } - }); } + RequestStatus status = updateResources(requests); + notifyUpdate(Resource.Type.ConfigGroup, request, predicate); - return getRequestStatus(null); + return status; } @Override @@ -265,6 +257,23 @@ public class ConfigGroupResourceProvider extends return getRequestStatus(null, associatedResources); } + public RequestStatus updateResources(final Set<ConfigGroupRequest> requests) + throws SystemException, + UnsupportedPropertyException, + NoSuchResourceException, + NoSuchParentResourceException { + + modifyResources(new Command<Void>() { + @Override + public Void invoke() throws AmbariException { + updateConfigGroups(requests); + return null; + } + }); + + return getRequestStatus(null); + } + private synchronized Set<ConfigGroupResponse> getConfigGroups (Set<ConfigGroupRequest> requests) throws AmbariException { Set<ConfigGroupResponse> responses = new HashSet<ConfigGroupResponse>(); http://git-wip-us.apache.org/repos/asf/ambari/blob/058dc168/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/HostComponentResourceProvider.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/HostComponentResourceProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/HostComponentResourceProvider.java index 4600538..47d3f70 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/HostComponentResourceProvider.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/HostComponentResourceProvider.java @@ -17,20 +17,31 @@ */ package org.apache.ambari.server.controller.internal; +import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.EnumMap; +import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; -import java.util.Iterator; +import java.util.List; import java.util.Map; import java.util.Set; +import com.google.inject.Inject; import com.google.inject.Injector; import org.apache.ambari.server.AmbariException; import org.apache.ambari.server.configuration.ComponentSSLConfiguration; import org.apache.ambari.server.controller.AmbariManagementController; +import org.apache.ambari.server.controller.MaintenanceStateHelper; import org.apache.ambari.server.controller.RequestStatusResponse; import org.apache.ambari.server.controller.ServiceComponentHostRequest; import org.apache.ambari.server.controller.ServiceComponentHostResponse; +import org.apache.ambari.server.controller.predicate.AndPredicate; +import org.apache.ambari.server.controller.predicate.EqualsPredicate; +import org.apache.ambari.server.controller.predicate.NotPredicate; +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; @@ -45,7 +56,17 @@ import org.apache.ambari.server.controller.utilities.PropertyHelper; import com.google.inject.assistedinject.Assisted; import com.google.inject.assistedinject.AssistedInject; -import com.google.inject.persist.Transactional; +import org.apache.ambari.server.state.Cluster; +import org.apache.ambari.server.state.Clusters; +import org.apache.ambari.server.state.MaintenanceState; +import org.apache.ambari.server.state.ServiceComponent; +import org.apache.ambari.server.state.ServiceComponentHost; +import org.apache.ambari.server.state.ServiceComponentHostEvent; +import org.apache.ambari.server.state.State; +import org.apache.ambari.server.state.fsm.InvalidStateTransitionException; +import org.apache.ambari.server.state.svccomphost.ServiceComponentHostDisableEvent; +import org.apache.ambari.server.state.svccomphost.ServiceComponentHostRestoreEvent; +import org.apache.commons.lang.StringUtils; /** * Resource provider for host component resources. @@ -79,7 +100,7 @@ public class HostComponentResourceProvider extends AbstractControllerResourcePro = PropertyHelper.getPropertyId("HostRoles", "desired_admin_state"); protected static final String HOST_COMPONENT_MAINTENANCE_STATE_PROPERTY_ID = "HostRoles/maintenance_state"; - + //Component name mappings private final Map<String, PropertyProvider> HOST_COMPONENT_PROPERTIES_PROVIDER = new HashMap<String, PropertyProvider>(); private static final int HOST_COMPONENT_HTTP_PROPERTY_REQUEST_CONNECT_TIMEOUT = 1500; //milliseconds @@ -95,6 +116,13 @@ public class HostComponentResourceProvider extends AbstractControllerResourcePro HOST_COMPONENT_COMPONENT_NAME_PROPERTY_ID, HOST_COMPONENT_HOST_NAME_PROPERTY_ID})); + /** + * maintenance state helper + */ + @Inject + private MaintenanceStateHelper maintenanceStateHelper; + + // ----- Constructors ---------------------------------------------------- /** @@ -153,7 +181,6 @@ public class HostComponentResourceProvider extends AbstractControllerResourcePro } @Override - @Transactional public Set<Resource> getResources(Request request, Predicate predicate) throws SystemException, UnsupportedPropertyException, NoSuchResourceException, NoSuchParentResourceException { @@ -194,7 +221,7 @@ public class HostComponentResourceProvider extends AbstractControllerResourcePro setResourceProperty(resource, HOST_COMPONENT_ACTUAL_CONFIGS_PROPERTY_ID, response.getActualConfigs(), requestedIds); setResourceProperty(resource, HOST_COMPONENT_STALE_CONFIGS_PROPERTY_ID, - Boolean.valueOf(response.isStaleConfig()), requestedIds); + response.isStaleConfig(), requestedIds); if (response.getAdminState() != null) { setResourceProperty(resource, HOST_COMPONENT_DESIRED_ADMIN_STATE_PROPERTY_ID, @@ -222,26 +249,24 @@ public class HostComponentResourceProvider extends AbstractControllerResourcePro @Override public RequestStatus updateResources(final Request request, Predicate predicate) throws SystemException, UnsupportedPropertyException, NoSuchResourceException, NoSuchParentResourceException { - final Set<ServiceComponentHostRequest> requests = new HashSet<ServiceComponentHostRequest>(); - RequestStatusResponse response = null; - final boolean runSmokeTest = "true".equals(getQueryParameterValue( - QUERY_PARAMETERS_RUN_SMOKE_TEST_ID, predicate)) ? true : false; + if (request.getProperties().isEmpty()) { + throw new IllegalArgumentException("Received an update request with no properties"); + } - Iterator<Map<String, Object>> iterator = request.getProperties().iterator(); - if (iterator.hasNext()) { - for (Map<String, Object> propertyMap : getPropertyMaps(request.getProperties().iterator().next(), predicate)) { - requests.add(getRequest(propertyMap)); - } - response = modifyResources(new Command<RequestStatusResponse>() { - @Override - public RequestStatusResponse invoke() throws AmbariException { - return getManagementController().updateHostComponents(requests, request.getRequestInfoProperties(), runSmokeTest); - } - }); + RequestStageContainer requestStages = doUpdateResources(null, request, predicate); + RequestStatusResponse response = null; + if (requestStages != null) { + try { + requestStages.persist(); + } catch (AmbariException e) { + throw new SystemException(e.getMessage(), e); + } + response = requestStages.getRequestStatusResponse(); notifyUpdate(Resource.Type.HostComponent, request, predicate); } + return getRequestStatus(response); } @@ -284,14 +309,247 @@ public class HostComponentResourceProvider extends AbstractControllerResourcePro return unsupportedProperties; } + RequestStatusResponse installAndStart(String cluster, Collection<String> hosts) throws SystemException, + UnsupportedPropertyException, NoSuchParentResourceException { - // ----- utility methods ------------------------------------------------- + final RequestStageContainer requestStages; + Map<String, Object> installProperties = new HashMap<String, Object>(); + + installProperties.put(HOST_COMPONENT_STATE_PROPERTY_ID, "INSTALLED"); + Map<String, String> requestInfo = new HashMap<String, String>(); + requestInfo.put("context", "Install and start components on added hosts"); + Request installRequest = PropertyHelper.getUpdateRequest(installProperties, requestInfo); + + Collection<EqualsPredicate> hostPredicates = new ArrayList<EqualsPredicate>(); + for (String host : hosts) { + hostPredicates.add(new EqualsPredicate<String>(HOST_COMPONENT_HOST_NAME_PROPERTY_ID, host)); + } + + Predicate statePredicate = new EqualsPredicate<String>(HOST_COMPONENT_STATE_PROPERTY_ID, "INIT"); + Predicate clusterPredicate = new EqualsPredicate<String>(HOST_COMPONENT_CLUSTER_NAME_PROPERTY_ID, cluster); + Predicate hostPredicate = new OrPredicate(hostPredicates.toArray(new Predicate[hostPredicates.size()])); + Predicate hostAndStatePredicate = new AndPredicate(statePredicate, hostPredicate); + Predicate installPredicate = new AndPredicate(hostAndStatePredicate, clusterPredicate); + + try { + LOG.info("Installing all components on added hosts"); + requestStages = doUpdateResources(null, installRequest, installPredicate); + notifyUpdate(Resource.Type.HostComponent, installRequest, installPredicate); + + Map<String, Object> startProperties = new HashMap<String, Object>(); + startProperties.put(HOST_COMPONENT_STATE_PROPERTY_ID, "STARTED"); + Request startRequest = PropertyHelper.getUpdateRequest(startProperties, requestInfo); + // Important to query against desired_state as this has been updated when install stage was created + // If I query against state, then the getRequest compares predicate prop against desired_state and then when the predicate + // is later applied explicitly, it gets compared to live_state. Since live_state == INSTALLED == INIT at this point and + // desired_state == INSTALLED, we will always get 0 matches since both comparisons can't be true :( + Predicate installedStatePredicate = new EqualsPredicate<String>(HOST_COMPONENT_DESIRED_STATE_PROPERTY_ID, "INSTALLED"); + Predicate notClientPredicate = new NotPredicate(new ClientComponentPredicate()); + Predicate clusterAndClientPredicate = new AndPredicate(clusterPredicate, notClientPredicate); + hostAndStatePredicate = new AndPredicate(installedStatePredicate, hostPredicate); + Predicate startPredicate = new AndPredicate(clusterAndClientPredicate, hostAndStatePredicate); + + LOG.info("Starting all non-client components on added hosts"); + //todo: if a host in in state HEARTBEAT_LOST, no stage will be created, so if this occurs during INSTALL + //todo: then no INSTALL stage will exist which will result in invalid state transition INIT->STARTED + doUpdateResources(requestStages, startRequest, startPredicate); + notifyUpdate(Resource.Type.HostComponent, startRequest, startPredicate); + try { + requestStages.persist(); + } catch (AmbariException e) { + throw new SystemException(e.getMessage(), e); + } + return requestStages.getRequestStatusResponse(); + } catch (NoSuchResourceException e) { + // shouldn't encounter this exception here + throw new SystemException("An unexpected exception occurred while processing add hosts", e); + } + } + + + /** + * Update the host component identified by the given request object with the + * values carried by the given request object. + * + * @param stages stages of the associated request + * @param requests the request object which defines which host component to + * update and the values to set + * @param requestProperties the request properties + * @param runSmokeTest indicates whether or not to run a smoke test + * + * @return a track action response + * + * @throws AmbariException thrown if the resource cannot be updated + */ + //todo: This was moved from AmbariManagementController and needs a lot of refactoring. + //todo: Look into using the predicate instead of Set<ServiceComponentHostRequest> + //todo: change to private access when all AMC tests have been moved. + protected synchronized RequestStageContainer updateHostComponents(RequestStageContainer stages, + Set<ServiceComponentHostRequest> requests, + Map<String, String> requestProperties, + boolean runSmokeTest) throws AmbariException { + + Clusters clusters = getManagementController().getClusters(); + + + Map<String, Map<State, List<ServiceComponentHost>>> changedScHosts = new HashMap<String, Map<State, List<ServiceComponentHost>>>(); + Collection<ServiceComponentHost> ignoredScHosts = new ArrayList<ServiceComponentHost>(); + Set<String> clusterNames = new HashSet<String>(); + Map<String, Map<String, Map<String, Set<String>>>> requestClusters = new HashMap<String, Map<String, Map<String, Set<String>>>>(); + Map<ServiceComponentHost, State> directTransitionScHosts = new HashMap<ServiceComponentHost, State>(); + + Resource.Type reqOpLvl = determineOperationLevel(requestProperties); + + + for (ServiceComponentHostRequest request : requests) { + validateServiceComponentHostRequest(request); + + Cluster cluster = clusters.getCluster(request.getClusterName()); + + if (StringUtils.isEmpty(request.getServiceName())) { + request.setServiceName(getManagementController().findServiceName(cluster, request.getComponentName())); + } + + ServiceComponent sc = getServiceComponent( + request.getClusterName(), request.getServiceName(), request.getComponentName()); + + logRequestInfo("Received a updateHostComponent request", request); + + clusterNames.add(request.getClusterName()); + + if (clusterNames.size() > 1) { + throw new IllegalArgumentException("Updates to multiple clusters is not" + + " supported"); + } + + // maps of cluster->services, services->components, components->hosts + Map<String, Map<String, Set<String>>> clusterServices = requestClusters.get(request.getClusterName()); + if (clusterServices == null) { + clusterServices = new HashMap<String, Map<String, Set<String>>>(); + requestClusters.put(request.getClusterName(), clusterServices); + } + + Map<String, Set<String>> serviceComponents = clusterServices.get(request.getServiceName()); + if (serviceComponents == null) { + serviceComponents = new HashMap<String, Set<String>>(); + clusterServices.put(request.getServiceName(), serviceComponents); + } + + Set<String> componentHosts = serviceComponents.get(request.getComponentName()); + if (componentHosts == null) { + componentHosts = new HashSet<String>(); + serviceComponents.put(request.getComponentName(), componentHosts) ; + } + + if (componentHosts.contains(request.getHostname())) { + throw new IllegalArgumentException("Invalid request contains duplicate hostcomponents"); + } + + componentHosts.add(request.getHostname()); + + + ServiceComponentHost sch = sc.getServiceComponentHost(request.getHostname()); + State oldState = sch.getState(); + State newState = null; + if (request.getDesiredState() != null) { + // set desired state on host component + newState = State.valueOf(request.getDesiredState()); + // throw exception if desired state isn't a valid desired state (static check) + if (!newState.isValidDesiredState()) { + throw new IllegalArgumentException("Invalid arguments, invalid" + + " desired state, desiredState=" + newState.toString()); + } + } + + // Setting Maintenance state for host component + if (null != request.getMaintenanceState()) { + MaintenanceState newMaint = MaintenanceState.valueOf(request.getMaintenanceState()); + MaintenanceState oldMaint = maintenanceStateHelper.getEffectiveState(sch); + + if (newMaint != oldMaint) { + if (sc.isClientComponent()) { + throw new IllegalArgumentException("Invalid arguments, cannot set maintenance state on a client component"); + } else if (newMaint.equals(MaintenanceState.IMPLIED_FROM_HOST) || newMaint.equals(MaintenanceState.IMPLIED_FROM_SERVICE)) { + throw new IllegalArgumentException("Invalid arguments, can only set maintenance state to one of " + + EnumSet.of(MaintenanceState.OFF, MaintenanceState.ON)); + } else { + sch.setMaintenanceState(newMaint); + } + } + } + + if (newState == null) { + logComponentInfo("Nothing to do for new updateServiceComponentHost", request, oldState, null); + continue; + } + + if (sc.isClientComponent() && + !newState.isValidClientComponentState()) { + throw new IllegalArgumentException("Invalid desired state for a client" + + " component"); + } + + State oldSchState = sch.getState(); + // Client component reinstall allowed + if (newState == oldSchState && !sc.isClientComponent() && + !requestProperties.containsKey(sch.getServiceComponentName().toLowerCase())) { + + ignoredScHosts.add(sch); + logComponentInfo("Ignoring ServiceComponentHost", request, oldState, newState); + continue; + } + + if (! maintenanceStateHelper.isOperationAllowed(reqOpLvl, sch)) { + ignoredScHosts.add(sch); + logComponentInfo("Ignoring ServiceComponentHost", request, oldState, newState); + continue; + } + + if (! isValidStateTransition(stages, oldSchState, newState, sch)) { + throw new AmbariException("Invalid state transition for host component" + + ", clusterName=" + cluster.getClusterName() + + ", clusterId=" + cluster.getClusterId() + + ", serviceName=" + sch.getServiceName() + + ", componentName=" + sch.getServiceComponentName() + + ", hostname=" + sch.getHostName() + + ", currentState=" + oldSchState + + ", newDesiredState=" + newState); + } + + if (isDirectTransition(oldSchState, newState)) { + logComponentInfo("Handling direct transition update to host component", request, oldState, newState); + directTransitionScHosts.put(sch, newState); + } else { + if (!changedScHosts.containsKey(sc.getName())) { + changedScHosts.put(sc.getName(), + new EnumMap<State, List<ServiceComponentHost>>(State.class)); + } + if (!changedScHosts.get(sc.getName()).containsKey(newState)) { + changedScHosts.get(sc.getName()).put(newState, + new ArrayList<ServiceComponentHost>()); + } + logComponentInfo("Handling update to host component", request, oldState, newState); + changedScHosts.get(sc.getName()).get(newState).add(sch); + } + } + + doDirectTransitions(directTransitionScHosts); + + // just getting the first cluster + Cluster cluster = clusters.getCluster(clusterNames.iterator().next()); + + return getManagementController().addStages(stages, cluster, requestProperties, null, null, null, + changedScHosts, ignoredScHosts, runSmokeTest, false); + } @Override protected Set<String> getPKPropertyIds() { return pkPropertyIds; } + + // ----- utility methods ------------------------------------------------- + /** * Get a component request object from a map of property values. * @@ -324,4 +582,248 @@ public class HostComponentResourceProvider extends AbstractControllerResourcePro return serviceComponentHostRequest; } + + private RequestStageContainer doUpdateResources(final RequestStageContainer stages, final Request request, Predicate predicate) + throws UnsupportedPropertyException, SystemException, NoSuchResourceException, NoSuchParentResourceException { + + final Set<ServiceComponentHostRequest> requests = new HashSet<ServiceComponentHostRequest>(); + + final boolean runSmokeTest = "true".equals(getQueryParameterValue( + QUERY_PARAMETERS_RUN_SMOKE_TEST_ID, predicate)); + + Set<String> queryIds = Collections.singleton(HOST_COMPONENT_COMPONENT_NAME_PROPERTY_ID); + + Request queryRequest = PropertyHelper.getReadRequest(queryIds); + // will take care of 404 exception + Set<Resource> matchingResources = getResources(queryRequest, predicate); + + for (Resource queryResource : matchingResources) { + if (predicate.evaluate(queryResource)) { + Map<String, Object> updateRequestProperties = new HashMap<String, Object>(); + + // add props from query resource + updateRequestProperties.putAll(PropertyHelper.getProperties(queryResource)); + + // add properties from update request + //todo: should we flag value size > 1? + if (request.getProperties() != null && request.getProperties().size() != 0) { + updateRequestProperties.putAll(request.getProperties().iterator().next()); + } + requests.add(getRequest(updateRequestProperties)); + } + } + + RequestStageContainer requestStages = modifyResources(new Command<RequestStageContainer>() { + @Override + public RequestStageContainer invoke() throws AmbariException { + return updateHostComponents(stages, requests, request.getRequestInfoProperties(), + runSmokeTest); + } + }); + notifyUpdate(Resource.Type.HostComponent, request, predicate); + + return requestStages; + } + + /** + * Determine whether a host component state change is valid. + * Looks at projected state from the current stages associated with the request. + * + * + * @param stages request stages + * @param startState host component start state + * @param desiredState host component desired state + * @param host host where state change is occurring + * + * @return whether the state transition is valid + */ + private boolean isValidStateTransition(RequestStageContainer stages, State startState, + State desiredState, ServiceComponentHost host) { + + if (stages != null) { + State projectedState = stages.getProjectedState(host.getHostName(), host.getServiceComponentName()); + startState = projectedState == null ? startState : projectedState; + } + + return State.isValidStateTransition(startState, desiredState); + } + + /** + * Checks if assigning new state does not require performing + * any additional actions + */ + public boolean isDirectTransition(State oldState, State newState) { + switch (newState) { + case INSTALLED: + if (oldState == State.DISABLED) { + return true; + } + break; + case DISABLED: + if (oldState == State.INSTALLED || + oldState == State.INSTALL_FAILED || + oldState == State.UNKNOWN) { + return true; + } + break; + default: + break; + } + return false; + } + + private ServiceComponent getServiceComponent(String clusterName, String serviceName, String componentName) + throws AmbariException { + + Clusters clusters = getManagementController().getClusters(); + return clusters.getCluster(clusterName).getService(serviceName).getServiceComponent(componentName); + } + + // Perform direct transitions (without task generation) + private void doDirectTransitions(Map<ServiceComponentHost, State> directTransitionScHosts) throws AmbariException { + for (Map.Entry<ServiceComponentHost, State> entry : directTransitionScHosts.entrySet()) { + ServiceComponentHost componentHost = entry.getKey(); + State newState = entry.getValue(); + long timestamp = System.currentTimeMillis(); + ServiceComponentHostEvent event; + componentHost.setDesiredState(newState); + switch (newState) { + case DISABLED: + event = new ServiceComponentHostDisableEvent( + componentHost.getServiceComponentName(), + componentHost.getHostName(), + timestamp); + break; + case INSTALLED: + event = new ServiceComponentHostRestoreEvent( + componentHost.getServiceComponentName(), + componentHost.getHostName(), + timestamp); + break; + default: + throw new AmbariException("Direct transition from " + componentHost.getState() + " to " + newState + " not supported"); + } + try { + componentHost.handleEvent(event); + } catch (InvalidStateTransitionException e) { + //Should not occur, must be covered by previous checks + throw new AmbariException("Internal error - not supported transition", e); + } + } + } + + /** + * Logs request info. + * + * @param msg base log msg + * @param request the request to log + */ + private void logRequestInfo(String msg, ServiceComponentHostRequest request) { + LOG.info("{}, clusterName={}, serviceName={}, componentName={}, hostname={}, request={}", + msg, + request.getClusterName(), + request.getServiceName(), + request.getComponentName(), + request.getHostname(), + request); + } + + /** + * Logs component info. + * + * @param msg base log msg + * @param request the request to log + * @param oldState current state + * @param newDesiredState new desired state + */ + private void logComponentInfo(String msg, ServiceComponentHostRequest request, State oldState, State newDesiredState) { + LOG.debug("{}, clusterName={}, serviceName={}, componentName={}, hostname={}, currentState={}, newDesiredState={}", + msg, + request.getClusterName(), + request.getServiceName(), + request.getComponentName(), + request.getHostname(), + oldState == null ? "null" : oldState, + newDesiredState == null ? "null" : newDesiredState); + } + + /** + * Get the "operation level" from the request. + * + * @param requestProperties request properties + * @return the "operation level" + */ + private Resource.Type determineOperationLevel(Map<String, String> requestProperties) { + // Determine operation level + Resource.Type reqOpLvl; + if (requestProperties.containsKey(RequestOperationLevel.OPERATION_LEVEL_ID)) { + reqOpLvl = new RequestOperationLevel(requestProperties).getLevel(); + } else { + String message = "Can not determine request operation level. " + + "Operation level property should " + + "be specified for this request."; + LOG.warn(message); + reqOpLvl = Resource.Type.Cluster; + } + return reqOpLvl; + } + + /** + * Validate a host component request. + * + * @param request request to validate + * @throws IllegalArgumentException if the request is invalid + */ + private void validateServiceComponentHostRequest(ServiceComponentHostRequest request) { + if (request.getClusterName() == null + || request.getClusterName().isEmpty() + || request.getComponentName() == null + || request.getComponentName().isEmpty() + || request.getHostname() == null + || request.getHostname().isEmpty()) { + throw new IllegalArgumentException("Invalid arguments" + + ", cluster name, component name and host name should be" + + " provided"); + } + + if (request.getAdminState() != null) { + throw new IllegalArgumentException("Property adminState cannot be modified through update. Use service " + + "specific DECOMMISSION action to decommision/recommission components."); + } + } + + + // ----- inner classes --------------------------------------------------- + + /** + * Predicate that identifies client components. + */ + private class ClientComponentPredicate implements Predicate { + @Override + public boolean evaluate(Resource resource) { + boolean isClient = false; + + String componentName = (String) resource.getPropertyValue(HOST_COMPONENT_COMPONENT_NAME_PROPERTY_ID); + try { + if (componentName != null && !componentName.isEmpty()) { + AmbariManagementController managementController = getManagementController(); + String clusterName = (String) resource.getPropertyValue(HOST_COMPONENT_CLUSTER_NAME_PROPERTY_ID); + String serviceName = (String) resource.getPropertyValue(HOST_COMPONENT_SERVICE_NAME_PROPERTY_ID); + if (StringUtils.isEmpty(serviceName)) { + Cluster cluster = managementController.getClusters().getCluster(clusterName); + serviceName = managementController.findServiceName(cluster, componentName); + } + + ServiceComponent sc = getServiceComponent((String) resource.getPropertyValue( + HOST_COMPONENT_CLUSTER_NAME_PROPERTY_ID), serviceName, componentName); + isClient = sc.isClientComponent(); + } + } catch (AmbariException e) { + // this is really a system exception since cluster/service should have been already verified + throw new RuntimeException( + "An unexpected exception occurred while trying to determine if a component is a client", e); + } + return isClient; + } + } }