AMBARI-12429. Deleting a host using the API causes NPE (alejandro)
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 de9ae52..5aec7eb 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
@@ -1060,6 +1060,11 @@
ServiceComponentHost sch = serviceComponentHostMap.get(request.getHostname());
+ if (null == sch) {
+ // It's possible that the host was deleted during the time that the request was generated.
+ continue;
+ }
+
if (checkDesiredState && (desiredStateToCheck != sch.getDesiredState())) {
continue;
}
@@ -1077,7 +1082,7 @@
}
ServiceComponentHostResponse r = sch.convertToResponse();
- if (filterBasedConfigStaleness && r.isStaleConfig() != staleConfig) {
+ if (null == r || (filterBasedConfigStaleness && r.isStaleConfig() != staleConfig)) {
continue;
}
@@ -1106,6 +1111,11 @@
}
} else {
for (ServiceComponentHost sch : serviceComponentHostMap.values()) {
+ if (null == sch) {
+ // It's possible that the host was deleted during the time that the request was generated.
+ continue;
+ }
+
if (checkDesiredState && (desiredStateToCheck != sch.getDesiredState())) {
continue;
}
@@ -1123,7 +1133,7 @@
}
ServiceComponentHostResponse r = sch.convertToResponse();
- if (filterBasedConfigStaleness && r.isStaleConfig() != staleConfig) {
+ if (null == r || (filterBasedConfigStaleness && r.isStaleConfig() != staleConfig)) {
continue;
}
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/HostResourceProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/HostResourceProvider.java
index 4c14426..c7f0e9b 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/HostResourceProvider.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/HostResourceProvider.java
@@ -55,12 +55,15 @@
import org.apache.ambari.server.state.DesiredConfig;
import org.apache.ambari.server.state.Host;
import org.apache.ambari.server.state.MaintenanceState;
+import org.apache.ambari.server.state.Service;
+import org.apache.ambari.server.state.ServiceComponent;
import org.apache.ambari.server.state.ServiceComponentHost;
import org.apache.ambari.server.state.stack.OsFamily;
import org.apache.ambari.server.topology.InvalidTopologyException;
import org.apache.ambari.server.topology.InvalidTopologyTemplateException;
import org.apache.ambari.server.topology.TopologyManager;
import org.apache.ambari.server.topology.TopologyRequest;
+import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -775,27 +778,47 @@
continue;
}
+ Set<String> clusterNamesForHost = new HashSet<String>();
if (null != hostRequest.getClusterName()) {
- Cluster cluster = clusters.getCluster(hostRequest.getClusterName());
+ clusterNamesForHost.add(hostRequest.getClusterName());
+ } else {
+ Set<Cluster> clustersForHost = clusters.getClustersForHost(hostRequest.getHostname());
+ if (null != clustersForHost) {
+ for (Cluster c : clustersForHost) {
+ clusterNamesForHost.add(c.getClusterName());
+ }
+ }
+ }
+
+ for (String clusterName : clusterNamesForHost) {
+ Cluster cluster = clusters.getCluster(clusterName);
List<ServiceComponentHost> list = cluster.getServiceComponentHosts(hostName);
- if (0 != list.size()) {
- StringBuilder reason = new StringBuilder("Cannot remove host ")
- .append(hostName)
- .append(" from ")
- .append(hostRequest.getClusterName())
- .append(". The following roles exist: ");
+ if (!list.isEmpty()) {
- int i = 0;
+ List<String> componentsToRemove = new ArrayList<String>();
for (ServiceComponentHost sch : list) {
- if ((i++) > 0) {
- reason.append(", ");
+ Service s = cluster.getService(sch.getServiceName());
+ ServiceComponent sc = s.getServiceComponent(sch.getServiceComponentName());
+
+ // Masters and Slaves must be deleted first. Clients are ok.
+ if (!sc.isClientComponent()) {
+ componentsToRemove.add(sch.getServiceComponentName());
}
- reason.append(sch.getServiceComponentName());
}
- throw new AmbariException(reason.toString());
+ if (!componentsToRemove.isEmpty()) {
+ StringBuilder reason = new StringBuilder("Cannot remove host ")
+ .append(hostName)
+ .append(" from ")
+ .append(hostRequest.getClusterName())
+ .append(". The following roles exist, and these components must be stopped if running, and then deleted: ");
+
+ reason.append(StringUtils.join(componentsToRemove, ", "));
+
+ throw new AmbariException(reason.toString());
+ }
}
}
okToRemove.add(hostRequest);
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/RequestOperationLevel.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/RequestOperationLevel.java
index c7c0160..ae57c50 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/RequestOperationLevel.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/RequestOperationLevel.java
@@ -48,7 +48,7 @@
public static final String OPERATION_CLUSTER_ID = "operation_level/cluster_name";
public static final String OPERATION_SERVICE_ID = "operation_level/service_name";
public static final String OPERATION_HOSTCOMPONENT_ID = "operation_level/hostcomponent_name";
- public static final String OPERATION_HOST_ID = "operation_level/host_name";
+ public static final String OPERATION_HOST_NAME = "operation_level/host_name";
/**
* Converts external operation level alias to an internal name
@@ -117,7 +117,7 @@
this.serviceName = requestInfoProperties.get(OPERATION_SERVICE_ID);
this.hostComponentName =
requestInfoProperties.get(OPERATION_HOSTCOMPONENT_ID);
- this.hostName = requestInfoProperties.get(OPERATION_HOST_ID);
+ this.hostName = requestInfoProperties.get(OPERATION_HOST_NAME);
}
/**
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/RequestResourceProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/RequestResourceProvider.java
index fa49d7f..911e3cd 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/RequestResourceProvider.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/RequestResourceProvider.java
@@ -99,7 +99,7 @@
protected static final String COMMAND_ID = "command";
protected static final String SERVICE_ID = "service_name";
protected static final String COMPONENT_ID = "component_name";
- protected static final String HOSTS_ID = "hosts";
+ protected static final String HOSTS_ID = "hosts"; // This is actually a list of hosts
protected static final String ACTION_ID = "action";
protected static final String INPUTS_ID = "parameters";
protected static final String EXLUSIVE_ID = "exclusive";
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/RequestOperationLevelDAO.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/RequestOperationLevelDAO.java
new file mode 100644
index 0000000..346b87b
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/RequestOperationLevelDAO.java
@@ -0,0 +1,82 @@
+/**
+ * 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.orm.dao;
+
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.Singleton;
+import com.google.inject.persist.Transactional;
+import org.apache.ambari.server.orm.RequiresSession;
+import org.apache.ambari.server.orm.entities.RequestOperationLevelEntity;
+
+import javax.persistence.EntityManager;
+import javax.persistence.TypedQuery;
+import java.util.Collection;
+import java.util.List;
+
+@Singleton
+public class RequestOperationLevelDAO {
+ @Inject
+ Provider<EntityManager> entityManagerProvider;
+ @Inject
+ DaoUtils daoUtils;
+
+
+ @RequiresSession
+ public List<RequestOperationLevelEntity> findByHostId(Long hostId) {
+ final TypedQuery<RequestOperationLevelEntity> query = entityManagerProvider.get()
+ .createNamedQuery("requestOperationLevelByHostId", RequestOperationLevelEntity.class);
+ query.setParameter("hostId", hostId);
+ return daoUtils.selectList(query);
+ }
+
+ @RequiresSession
+ public List<RequestOperationLevelEntity> findAll() {
+ return daoUtils.selectAll(entityManagerProvider.get(), RequestOperationLevelEntity.class);
+ }
+
+ @Transactional
+ public void refresh(RequestOperationLevelEntity requestOperationLevelEntity) {
+ entityManagerProvider.get().refresh(requestOperationLevelEntity);
+ }
+
+ @Transactional
+ public void create(RequestOperationLevelEntity requestOperationLevelEntity) {
+ entityManagerProvider.get().persist(requestOperationLevelEntity);
+ }
+
+ @Transactional
+ public RequestOperationLevelEntity merge(RequestOperationLevelEntity requestOperationLevelEntity) {
+ return entityManagerProvider.get().merge(requestOperationLevelEntity);
+ }
+
+ @Transactional
+ public void remove(RequestOperationLevelEntity requestOperationLevelEntity) {
+ entityManagerProvider.get().remove(merge(requestOperationLevelEntity));
+ }
+
+ @Transactional
+ public void removeByHostId(Long hostId) {
+ Collection<RequestOperationLevelEntity> hostRequestOpLevels = this.findByHostId(hostId);
+ for (RequestOperationLevelEntity hostRequestOpLevel : hostRequestOpLevels) {
+ this.remove(hostRequestOpLevel);
+ }
+ }
+
+}
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/RequestOperationLevelEntity.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/RequestOperationLevelEntity.java
index 2c11e55..c03816e 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/RequestOperationLevelEntity.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/RequestOperationLevelEntity.java
@@ -22,13 +22,12 @@
import javax.persistence.Basic;
import javax.persistence.Column;
import javax.persistence.Entity;
-import javax.persistence.EnumType;
-import javax.persistence.Enumerated;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
-import javax.persistence.ManyToOne;
+import javax.persistence.NamedQueries;
+import javax.persistence.NamedQuery;
import javax.persistence.OneToOne;
import javax.persistence.Table;
import javax.persistence.TableGenerator;
@@ -40,6 +39,11 @@
, pkColumnValue = "operation_level_id_seq"
, initialValue = 1
)
+@NamedQueries({
+ @NamedQuery(name = "requestOperationLevelByHostId", query =
+ "SELECT requestOperationLevel FROM RequestOperationLevelEntity requestOperationLevel " +
+ "WHERE requestOperationLevel.hostId=:hostId")
+})
public class RequestOperationLevelEntity {
@Id
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClustersImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClustersImpl.java
index 665dd56..90fdbec 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClustersImpl.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClustersImpl.java
@@ -51,6 +51,7 @@
import org.apache.ambari.server.orm.dao.HostStateDAO;
import org.apache.ambari.server.orm.dao.HostVersionDAO;
import org.apache.ambari.server.orm.dao.KerberosPrincipalHostDAO;
+import org.apache.ambari.server.orm.dao.RequestOperationLevelDAO;
import org.apache.ambari.server.orm.dao.ResourceTypeDAO;
import org.apache.ambari.server.orm.dao.ServiceConfigDAO;
import org.apache.ambari.server.orm.dao.StackDAO;
@@ -117,6 +118,8 @@
@Inject
private ResourceTypeDAO resourceTypeDAO;
@Inject
+ private RequestOperationLevelDAO requestOperationLevelDAO;
+ @Inject
private KerberosPrincipalHostDAO kerberosPrincipalHostDAO;
@Inject
private HostConfigMappingDAO hostConfigMappingDAO;
@@ -811,6 +814,7 @@
hostStateDAO.removeByHostId(entity.getHostId());
hostConfigMappingDAO.removeByHostId(entity.getHostId());
serviceConfigDAO.removeHostFromServiceConfigs(entity.getHostId());
+ requestOperationLevelDAO.removeByHostId(entity.getHostId());
// Remove from dictionaries
hosts.remove(hostname);
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/svccomphost/ServiceComponentHostImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/state/svccomphost/ServiceComponentHostImpl.java
index 9f25ad7..b623479 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/svccomphost/ServiceComponentHostImpl.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/svccomphost/ServiceComponentHostImpl.java
@@ -1142,14 +1142,31 @@
public ServiceComponentHostResponse convertToResponse() {
readLock.lock();
try {
+ HostComponentStateEntity hostComponentStateEntity = getStateEntity();
+ if (null == hostComponentStateEntity) {
+ LOG.warn("Could not convert ServiceComponentHostResponse to a response. It's possible that Host " + getHostName() + " was deleted.");
+ return null;
+ }
+
+ String clusterName = serviceComponent.getClusterName();
+ String serviceName = serviceComponent.getServiceName();
+ String serviceComponentName = serviceComponent.getName();
+ String hostName = getHostName();
+ String state = getState().toString();
+ String stackId = getStackVersion().getStackId();
+ String desiredState = getDesiredState().toString();
+ String desiredStackId = getDesiredStackVersion().getStackId();
+ HostComponentAdminState componentAdminState = getComponentAdminState();
+ UpgradeState upgradeState = hostComponentStateEntity.getUpgradeState();
+
ServiceComponentHostResponse r = new ServiceComponentHostResponse(
- serviceComponent.getClusterName(), serviceComponent.getServiceName(),
- serviceComponent.getName(), getHostName(), getState().toString(),
- getStackVersion().getStackId(), getDesiredState().toString(),
- getDesiredStackVersion().getStackId(), getComponentAdminState());
+ clusterName, serviceName,
+ serviceComponentName, hostName, state,
+ stackId, desiredState,
+ desiredStackId, componentAdminState);
r.setActualConfigs(actualConfigs);
- r.setUpgradeState(getStateEntity().getUpgradeState());
+ r.setUpgradeState(upgradeState);
try {
r.setStaleConfig(helper.isStaleConfigs(this));
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/RequestOperationLevelTest.java b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/RequestOperationLevelTest.java
index bd4ad90..6a38b1d 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/RequestOperationLevelTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/RequestOperationLevelTest.java
@@ -50,7 +50,7 @@
service_id);
requestInfoProperties.put(RequestOperationLevel.OPERATION_HOSTCOMPONENT_ID,
hostcomponent_id);
- requestInfoProperties.put(RequestOperationLevel.OPERATION_HOST_ID,
+ requestInfoProperties.put(RequestOperationLevel.OPERATION_HOST_NAME,
host_id);
// Check normal creation
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/RequestResourceProviderTest.java b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/RequestResourceProviderTest.java
index ed8336e..d150ab1 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/RequestResourceProviderTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/RequestResourceProviderTest.java
@@ -1047,14 +1047,14 @@
String host_component = "HOST_COMPONENT";
String service_id = "HDFS";
String hostcomponent_id = "Namenode";
- String host_id = "host1";
+ String host_name = "host1";
properties.put(RequestResourceProvider.REQUEST_CLUSTER_NAME_PROPERTY_ID, c1);
Set<Map<String, Object>> filterSet = new HashSet<Map<String, Object>>();
Map<String, Object> filterMap = new HashMap<String, Object>();
filterMap.put(RequestResourceProvider.SERVICE_ID, service_id);
- filterMap.put(RequestResourceProvider.HOSTS_ID, host_id);
+ filterMap.put(RequestResourceProvider.HOSTS_ID, host_name);
filterSet.add(filterMap);
properties.put(RequestResourceProvider.REQUEST_RESOURCE_FILTER_ID, filterSet);
@@ -1071,8 +1071,8 @@
service_id);
requestInfoProperties.put(RequestOperationLevel.OPERATION_HOSTCOMPONENT_ID,
hostcomponent_id);
- requestInfoProperties.put(RequestOperationLevel.OPERATION_HOST_ID,
- host_id);
+ requestInfoProperties.put(RequestOperationLevel.OPERATION_HOST_NAME,
+ host_name);
Request request = PropertyHelper.getCreateRequest(propertySet, requestInfoProperties);
ResourceProvider provider = AbstractControllerResourceProvider.getResourceProvider(
@@ -1092,7 +1092,7 @@
Assert.assertEquals(level.getClusterName(), c1);
Assert.assertEquals(level.getServiceName(), service_id);
Assert.assertEquals(level.getHostComponentName(), hostcomponent_id);
- Assert.assertEquals(level.getHostName(), host_id);
+ Assert.assertEquals(level.getHostName(), host_name);
}
@Test