http://git-wip-us.apache.org/repos/asf/ambari/blob/e3acc7f0/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/AbstractPrepareKerberosServerAction.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/AbstractPrepareKerberosServerAction.java b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/AbstractPrepareKerberosServerAction.java new file mode 100644 index 0000000..b3b6b48 --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/AbstractPrepareKerberosServerAction.java @@ -0,0 +1,192 @@ +/* + * 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.serveraction.kerberos; + +import com.google.common.reflect.TypeToken; +import com.google.inject.Inject; +import org.apache.ambari.server.AmbariException; +import org.apache.ambari.server.agent.CommandReport; +import org.apache.ambari.server.controller.KerberosHelper; +import org.apache.ambari.server.state.Cluster; +import org.apache.ambari.server.state.ServiceComponentHost; +import org.apache.ambari.server.state.kerberos.KerberosComponentDescriptor; +import org.apache.ambari.server.state.kerberos.KerberosDescriptor; +import org.apache.ambari.server.state.kerberos.KerberosIdentityDescriptor; +import org.apache.ambari.server.state.kerberos.KerberosServiceDescriptor; +import org.apache.ambari.server.utils.StageUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.io.IOException; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public abstract class AbstractPrepareKerberosServerAction extends KerberosServerAction { + private final static Logger LOG = LoggerFactory.getLogger(AbstractPrepareKerberosServerAction.class); + + /** + * KerberosHelper + */ + @Inject + private KerberosHelper kerberosHelper; + + @Inject + private KerberosIdentityDataFileWriterFactory kerberosIdentityDataFileWriterFactory; + + @Override + protected CommandReport processIdentity(Map<String, String> identityRecord, String evaluatedPrincipal, KerberosOperationHandler operationHandler, Map<String, String> kerberosConfiguration, Map<String, Object> requestSharedDataContext) throws AmbariException { + throw new UnsupportedOperationException(); + } + + protected void processIdentities(Cluster cluster, KerberosDescriptor kerberosDescriptor, List<ServiceComponentHost> schToProcess, + Collection<String> identityFilter, String dataDirectory, + Map<String, Map<String, String>> kerberosConfigurations) throws AmbariException { + + actionLog.writeStdOut("Processing Kerberos Identities"); + + if (!schToProcess.isEmpty()) { + if(dataDirectory == null) { + String message = "The data directory has not been set. Generated data can not be stored."; + LOG.error(message); + throw new AmbariException(message); + } + + // Create the file used to store details about principals and keytabs to create + File identityDataFile = new File(dataDirectory, KerberosIdentityDataFileWriter.DATA_FILE_NAME); + + // Group ServiceComponentHosts with their relevant hosts so we can create the relevant host-based + // configurations once per host, rather than for every ServiceComponentHost we encounter + Map<String, List<ServiceComponentHost>> hostServiceComponentHosts = new HashMap<String, List<ServiceComponentHost>>(); + for (ServiceComponentHost sch : schToProcess) { + String hostName = sch.getHostName(); + List<ServiceComponentHost> serviceComponentHosts = hostServiceComponentHosts.get(hostName); + + if (serviceComponentHosts == null) { + serviceComponentHosts = new ArrayList<ServiceComponentHost>(); + hostServiceComponentHosts.put(hostName, serviceComponentHosts); + } + + serviceComponentHosts.add(sch); + } + + Map<String, String> kerberosDescriptorProperties = kerberosDescriptor.getProperties(); + KerberosIdentityDataFileWriter kerberosIdentityDataFileWriter = null; + + try { + for (Map.Entry<String, List<ServiceComponentHost>> entry : hostServiceComponentHosts.entrySet()) { + String hostName = entry.getKey(); + List<ServiceComponentHost> serviceComponentHosts = entry.getValue(); + + // Calculate the current host-specific configurations. These will be used to replace + // variables within the Kerberos descriptor data + Map<String, Map<String, String>> configurations = kerberosHelper.calculateConfigurations(cluster, hostName, kerberosDescriptorProperties); + + try { + // Iterate over the components installed on the current host to get the service and + // component-level Kerberos descriptors in order to determine which principals, + // keytab files, and configurations need to be created or updated. + for (ServiceComponentHost sch : serviceComponentHosts) { + String serviceName = sch.getServiceName(); + String componentName = sch.getServiceComponentName(); + + KerberosServiceDescriptor serviceDescriptor = kerberosDescriptor.getService(serviceName); + + if (serviceDescriptor != null) { + List<KerberosIdentityDescriptor> serviceIdentities = serviceDescriptor.getIdentities(true); + + // Lazily create the KerberosIdentityDataFileWriter instance... + if (kerberosIdentityDataFileWriter == null) { + actionLog.writeStdOut(String.format("Writing Kerberos identity data metadata file to %s", identityDataFile.getAbsolutePath())); + kerberosIdentityDataFileWriter = kerberosIdentityDataFileWriterFactory.createKerberosIdentityDataFileWriter(identityDataFile); + } + + // Add service-level principals (and keytabs) + kerberosHelper.addIdentities(kerberosIdentityDataFileWriter, serviceIdentities, + identityFilter, hostName, serviceName, componentName, kerberosConfigurations, configurations); + + KerberosComponentDescriptor componentDescriptor = serviceDescriptor.getComponent(componentName); + + if (componentDescriptor != null) { + List<KerberosIdentityDescriptor> componentIdentities = componentDescriptor.getIdentities(true); + + // Calculate the set of configurations to update and replace any variables + // using the previously calculated Map of configurations for the host. + kerberosHelper.mergeConfigurations(kerberosConfigurations, + componentDescriptor.getConfigurations(true), configurations); + + // Add component-level principals (and keytabs) + kerberosHelper.addIdentities(kerberosIdentityDataFileWriter, componentIdentities, + identityFilter, hostName, serviceName, componentName, kerberosConfigurations, configurations); + } + } + } + } catch (IOException e) { + String message = String.format("Failed to write index file - %s", identityDataFile.getAbsolutePath()); + LOG.error(message, e); + actionLog.writeStdOut(message); + actionLog.writeStdErr(message + "\n" + e.getLocalizedMessage()); + throw new AmbariException(message, e); + } + } + } + finally { + if (kerberosIdentityDataFileWriter != null) { + // Make sure the data file is closed + try { + kerberosIdentityDataFileWriter.close(); + } catch (IOException e) { + String message = "Failed to close the index file writer"; + LOG.warn(message, e); + actionLog.writeStdOut(message); + actionLog.writeStdErr(message + "\n" + e.getLocalizedMessage()); + } + } + } + } + } + + protected Map<String, ? extends Collection<String>> getServiceComponentFilter() { + String serializedValue = getCommandParameterValue(SERVICE_COMPONENT_FILTER); + + if(serializedValue != null) { + Type type = new TypeToken<Map<String, ? extends Collection<String>>>() {}.getType(); + return StageUtils.getGson().fromJson(serializedValue, type); + } + else { + return null; + } + } + + protected Collection<String> getIdentityFilter() { + String serializedValue = getCommandParameterValue(IDENTITY_FILTER); + + if(serializedValue != null) { + Type type = new TypeToken<Collection<String>>() {}.getType(); + return StageUtils.getGson().fromJson(serializedValue, type); + } + else { + return null; + } + } +}
http://git-wip-us.apache.org/repos/asf/ambari/blob/e3acc7f0/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/CreateKeytabFilesServerAction.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/CreateKeytabFilesServerAction.java b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/CreateKeytabFilesServerAction.java index 34780d6..c7123a4 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/CreateKeytabFilesServerAction.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/CreateKeytabFilesServerAction.java @@ -150,13 +150,19 @@ public class CreateKeytabFilesServerAction extends KerberosServerAction { if (identityRecord != null) { String message; + String dataDirectory = getDataDirectoryPath(); if (operationHandler == null) { message = String.format("Failed to create keytab file for %s, missing KerberosOperationHandler", evaluatedPrincipal); actionLog.writeStdErr(message); LOG.error(message); commandReport = createCommandReport(1, HostRoleStatus.FAILED, "{}", actionLog.getStdOut(), actionLog.getStdErr()); - } else { + } else if (dataDirectory == null) { + message = "The data directory has not been set. Generated keytab files can not be stored."; + LOG.error(message); + commandReport = createCommandReport(1, HostRoleStatus.FAILED, "{}", actionLog.getStdOut(), actionLog.getStdErr()); + } + else { Map<String, String> principalPasswordMap = getPrincipalPasswordMap(requestSharedDataContext); Map<String, Integer> principalKeyNumberMap = getPrincipalKeyNumberMap(requestSharedDataContext); @@ -178,7 +184,7 @@ public class CreateKeytabFilesServerAction extends KerberosServerAction { // Determine where to store the keytab file. It should go into a host-specific // directory under the previously determined data directory. - File hostDirectory = new File(getDataDirectoryPath(), hostName); + File hostDirectory = new File(dataDirectory, hostName); // Ensure the host directory exists... if (!hostDirectory.exists() && hostDirectory.mkdirs()) { http://git-wip-us.apache.org/repos/asf/ambari/blob/e3acc7f0/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/KerberosServerAction.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/KerberosServerAction.java b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/KerberosServerAction.java index 55018de..57e5527 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/KerberosServerAction.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/KerberosServerAction.java @@ -70,6 +70,15 @@ public abstract class KerberosServerAction extends AbstractServerAction { public static final String DEFAULT_REALM = "default_realm"; /** + * A (command parameter) property name used to hold the (serialized) service/component filter map. + */ + public static final String SERVICE_COMPONENT_FILTER = "service_component_filter"; + + /** + * A (command parameter) property name used to hold the (serialized) identity filter list. + */ + public static final String IDENTITY_FILTER = "identity_filter"; + /** * A (command parameter) property name used to hold the relevant KDC type value. See * {@link org.apache.ambari.server.serveraction.kerberos.KDCType} for valid values */ http://git-wip-us.apache.org/repos/asf/ambari/blob/e3acc7f0/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/PrepareDisableKerberosServerAction.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/PrepareDisableKerberosServerAction.java b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/PrepareDisableKerberosServerAction.java new file mode 100644 index 0000000..68fcca0 --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/PrepareDisableKerberosServerAction.java @@ -0,0 +1,250 @@ +/* + * 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.serveraction.kerberos; + +import com.google.inject.Inject; +import org.apache.ambari.server.AmbariException; +import org.apache.ambari.server.actionmanager.HostRoleStatus; +import org.apache.ambari.server.agent.CommandReport; +import org.apache.ambari.server.controller.KerberosHelper; +import org.apache.ambari.server.state.Cluster; +import org.apache.ambari.server.state.ConfigHelper; +import org.apache.ambari.server.state.PropertyInfo; +import org.apache.ambari.server.state.SecurityState; +import org.apache.ambari.server.state.ServiceComponentHost; +import org.apache.ambari.server.state.StackId; +import org.apache.ambari.server.state.kerberos.KerberosDescriptor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.io.IOException; +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.ConcurrentMap; + +/** + * PrepareEnableKerberosServerAction is a ServerAction implementation that prepares metadata needed + * to enable Kerberos on the cluster. + */ +public class PrepareDisableKerberosServerAction extends AbstractPrepareKerberosServerAction { + private final static Logger LOG = LoggerFactory.getLogger(PrepareDisableKerberosServerAction.class); + + /** + * KerberosHelper + */ + @Inject + private KerberosHelper kerberosHelper; + + @Inject + private ConfigHelper configHelper; + + @Inject + private KerberosConfigDataFileWriterFactory kerberosConfigDataFileWriterFactory; + + + /** + * Called to execute this action. Upon invocation, calls + * {@link KerberosServerAction#processIdentities(Map)} + * to iterate through the Kerberos identity metadata and call + * {@link PrepareDisableKerberosServerAction#processIdentities(Map)} + * for each identity to process. + * + * @param requestSharedDataContext a Map to be used a shared data among all ServerActions related + * to a given request + * @return a CommandReport indicating the result of this action + * @throws AmbariException + * @throws InterruptedException + */ + @Override + public CommandReport execute(ConcurrentMap<String, Object> requestSharedDataContext) throws + AmbariException, InterruptedException { + + Cluster cluster = getCluster(); + + if (cluster == null) { + throw new AmbariException("Missing cluster object"); + } + + KerberosDescriptor kerberosDescriptor = kerberosHelper.getKerberosDescriptor(cluster); + Collection<String> identityFilter = getIdentityFilter(); + List<ServiceComponentHost> schToProcess = kerberosHelper.getServiceComponentHostsToProcess(cluster, + kerberosDescriptor, + getServiceComponentFilter(), + identityFilter, + new KerberosHelper.Command<Boolean, ServiceComponentHost>() { + @Override + public Boolean invoke(ServiceComponentHost sch) throws AmbariException { + return (sch.getDesiredSecurityState() == SecurityState.UNSECURED) && (sch.getSecurityState() != SecurityState.UNSECURED); + } + }); + + Map<String, Map<String, String>> kerberosConfigurations = new HashMap<String, Map<String, String>>(); + Map<String, String> commandParameters = getCommandParameters(); + String dataDirectory = getCommandParameterValue(commandParameters, DATA_DIRECTORY); + + int schCount = schToProcess.size(); + if (schCount == 0) { + actionLog.writeStdOut("There are no components to process"); + } else if (schCount == 1) { + actionLog.writeStdOut(String.format("Processing %d component", schCount)); + } else { + actionLog.writeStdOut(String.format("Processing %d components", schCount)); + } + + processIdentities(cluster, kerberosDescriptor, schToProcess, identityFilter, dataDirectory, kerberosConfigurations); + + actionLog.writeStdOut("Determining configuration changes"); + // Ensure the cluster-env/security_enabled flag is set properly + Map<String, String> clusterEnvProperties = kerberosConfigurations.get(KerberosHelper.SECURITY_ENABLED_CONFIG_TYPE); + if (clusterEnvProperties == null) { + clusterEnvProperties = new HashMap<String, String>(); + kerberosConfigurations.put(KerberosHelper.SECURITY_ENABLED_CONFIG_TYPE, clusterEnvProperties); + } + clusterEnvProperties.put(KerberosHelper.SECURITY_ENABLED_PROPERTY_NAME, "false"); + + // If there are configurations to set, create a (temporary) data file to store the configuration + // updates and fill it will the relevant configurations. + if (!kerberosConfigurations.isEmpty()) { + if(dataDirectory == null) { + String message = "The data directory has not been set. Generated data can not be stored."; + LOG.error(message); + throw new AmbariException(message); + } + + Map<String, Collection<String>> configurationsToRemove = new HashMap<String, Collection<String>>(); + File configFile = new File(dataDirectory, KerberosConfigDataFileWriter.DATA_FILE_NAME); + KerberosConfigDataFileWriter kerberosConfDataFileWriter = null; + + // Fill the configurationsToRemove map with all Kerberos-related configurations. Values + // needed to be kept will have new values from the stack definition and thus pruned from + // this map. + for (Map.Entry<String, Map<String, String>> entry : kerberosConfigurations.entrySet()) { + configurationsToRemove.put(entry.getKey(), new HashSet<String>(entry.getValue().keySet())); + } + + // Remove cluster-env from the set of configurations to remove since it has no default set + // or properties and the logic below will remove all from this set - which is not desirable. + configurationsToRemove.remove("cluster-env"); + + if (!schToProcess.isEmpty()) { + Set<String> visitedServices = new HashSet<String>(); + + for (ServiceComponentHost sch : schToProcess) { + String serviceName = sch.getServiceName(); + + if (!visitedServices.contains(serviceName)) { + StackId stackVersion = sch.getStackVersion(); + + visitedServices.add(serviceName); + + if (stackVersion != null) { + Set<PropertyInfo> serviceProperties = configHelper.getServiceProperties(stackVersion, serviceName, true); + + if (serviceProperties != null) { + for (PropertyInfo propertyInfo : serviceProperties) { + String filename = propertyInfo.getFilename(); + + if (filename != null) { + String type = ConfigHelper.fileNameToConfigType(filename); + String propertyName = propertyInfo.getName(); + + Map<String, String> kerberosConfiguration = kerberosConfigurations.get(type); + if ((kerberosConfiguration != null) && (kerberosConfiguration.containsKey(propertyName))) { + kerberosConfiguration.put(propertyName, propertyInfo.getValue()); + } + + // Remove the relevant from the set of properties (for the given type) to remove + Collection<String> propertiesToRemove = configurationsToRemove.get(type); + if (propertiesToRemove != null) { + propertiesToRemove.remove(propertyName); + } + } + } + } + } + } + } + } + + actionLog.writeStdOut(String.format("Writing configuration changes metadata file to %s", configFile.getAbsolutePath())); + try { + kerberosConfDataFileWriter = kerberosConfigDataFileWriterFactory.createKerberosConfigDataFileWriter(configFile); + + for (Map.Entry<String, Map<String, String>> entry : kerberosConfigurations.entrySet()) { + String type = entry.getKey(); + Map<String, String> properties = entry.getValue(); + Collection<String> propertiesToRemove = configurationsToRemove.get(type); + + if (properties != null) { + for (Map.Entry<String, String> configTypeEntry : properties.entrySet()) { + String propertyName = configTypeEntry.getKey(); + + // Ignore properties that should be removed + if ((propertiesToRemove == null) || !propertiesToRemove.contains(propertyName)) { + String value = configTypeEntry.getValue(); + String operation = (value == null) + ? KerberosConfigDataFileWriter.OPERATION_TYPE_REMOVE + : KerberosConfigDataFileWriter.OPERATION_TYPE_SET; + + kerberosConfDataFileWriter.addRecord(type, propertyName, value, operation); + } + } + } + } + + // Declare which properties to remove from the configurations + for (Map.Entry<String, Collection<String>> entry : configurationsToRemove.entrySet()) { + String type = entry.getKey(); + Collection<String> properties = entry.getValue(); + + if (properties != null) { + for (String propertyName : properties) { + kerberosConfDataFileWriter.addRecord(type, propertyName, null, KerberosConfigDataFileWriter.OPERATION_TYPE_REMOVE); + } + } + } + } catch (IOException e) { + String message = String.format("Failed to write kerberos configurations file - %s", configFile.getAbsolutePath()); + LOG.error(message, e); + actionLog.writeStdOut(message); + actionLog.writeStdErr(message + "\n" + e.getLocalizedMessage()); + throw new AmbariException(message, e); + } finally { + if (kerberosConfDataFileWriter != null) { + try { + kerberosConfDataFileWriter.close(); + } catch (IOException e) { + String message = "Failed to close the kerberos configurations file writer"; + LOG.warn(message, e); + actionLog.writeStdOut(message); + actionLog.writeStdErr(message + "\n" + e.getLocalizedMessage()); + } + } + } + } + + return createCommandReport(0, HostRoleStatus.COMPLETED, "{}", actionLog.getStdOut(), actionLog.getStdErr()); + } +} + http://git-wip-us.apache.org/repos/asf/ambari/blob/e3acc7f0/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/PrepareEnableKerberosServerAction.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/PrepareEnableKerberosServerAction.java b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/PrepareEnableKerberosServerAction.java new file mode 100644 index 0000000..2295eeb --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/PrepareEnableKerberosServerAction.java @@ -0,0 +1,183 @@ +/* + * 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.serveraction.kerberos; + +import com.google.inject.Inject; +import org.apache.ambari.server.AmbariException; +import org.apache.ambari.server.actionmanager.HostRoleStatus; +import org.apache.ambari.server.agent.CommandReport; +import org.apache.ambari.server.controller.KerberosHelper; +import org.apache.ambari.server.state.Cluster; +import org.apache.ambari.server.state.SecurityState; +import org.apache.ambari.server.state.ServiceComponentHost; +import org.apache.ambari.server.state.kerberos.KerberosDescriptor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.io.IOException; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentMap; + +/** + * PrepareEnableKerberosServerAction is a ServerAction implementation that prepares metadata needed + * to enable Kerberos on the cluster. + */ +public class PrepareEnableKerberosServerAction extends AbstractPrepareKerberosServerAction { + private final static Logger LOG = LoggerFactory.getLogger(PrepareEnableKerberosServerAction.class); + + /** + * KerberosHelper + */ + @Inject + private KerberosHelper kerberosHelper; + + @Inject + private KerberosConfigDataFileWriterFactory kerberosConfigDataFileWriterFactory; + + + /** + * Called to execute this action. Upon invocation, calls + * {@link KerberosServerAction#processIdentities(Map)} + * to iterate through the Kerberos identity metadata and call + * {@link PrepareEnableKerberosServerAction#processIdentities(Map)} + * for each identity to process. + * + * @param requestSharedDataContext a Map to be used a shared data among all ServerActions related + * to a given request + * @return a CommandReport indicating the result of this action + * @throws AmbariException + * @throws InterruptedException + */ + @Override + public CommandReport execute(ConcurrentMap<String, Object> requestSharedDataContext) throws + AmbariException, InterruptedException { + + Cluster cluster = getCluster(); + + if (cluster == null) { + throw new AmbariException("Missing cluster object"); + } + + KerberosDescriptor kerberosDescriptor = kerberosHelper.getKerberosDescriptor(cluster); + Collection<String> identityFilter = getIdentityFilter(); + List<ServiceComponentHost> schToProcess = kerberosHelper.getServiceComponentHostsToProcess(cluster, + kerberosDescriptor, + getServiceComponentFilter(), + identityFilter, + new KerberosHelper.Command<Boolean, ServiceComponentHost>() { + @Override + public Boolean invoke(ServiceComponentHost sch) throws AmbariException { + return (sch.getDesiredSecurityState() == SecurityState.SECURED_KERBEROS) && (sch.getSecurityState() != SecurityState.SECURED_KERBEROS); + } + }); + + Map<String, String> commandParameters = getCommandParameters(); + String dataDirectory = getCommandParameterValue(commandParameters, DATA_DIRECTORY); + Map<String, Map<String, String>> kerberosConfigurations = new HashMap<String, Map<String, String>>(); + + int schCount = schToProcess.size(); + if (schCount == 0) { + actionLog.writeStdOut("There are no components to process"); + } else if (schCount == 1) { + actionLog.writeStdOut(String.format("Processing %d component", schCount)); + } else { + actionLog.writeStdOut(String.format("Processing %d components", schCount)); + } + + processIdentities(cluster, kerberosDescriptor, schToProcess, identityFilter, dataDirectory, kerberosConfigurations); + + if (!schToProcess.isEmpty()) { + actionLog.writeStdOut("Creating auth-to-local rules"); + kerberosHelper.setAuthToLocalRules(kerberosDescriptor, cluster, getDefaultRealm(commandParameters), + kerberosHelper.calculateConfigurations(cluster, null, kerberosDescriptor.getProperties()), + kerberosConfigurations); + } + + + actionLog.writeStdOut("Determining configuration changes"); + // Ensure the cluster-env/security_enabled flag is set properly + Map<String, String> clusterEnvProperties = kerberosConfigurations.get(KerberosHelper.SECURITY_ENABLED_CONFIG_TYPE); + if (clusterEnvProperties == null) { + clusterEnvProperties = new HashMap<String, String>(); + kerberosConfigurations.put(KerberosHelper.SECURITY_ENABLED_CONFIG_TYPE, clusterEnvProperties); + } + clusterEnvProperties.put(KerberosHelper.SECURITY_ENABLED_PROPERTY_NAME, "true"); + + // If there are configurations to set, create a (temporary) data file to store the configuration + // updates and fill it will the relevant configurations. + if (!kerberosConfigurations.isEmpty()) { + if(dataDirectory == null) { + String message = "The data directory has not been set. Generated data can not be stored."; + LOG.error(message); + throw new AmbariException(message); + } + + File configFile = new File(dataDirectory, KerberosConfigDataFileWriter.DATA_FILE_NAME); + KerberosConfigDataFileWriter kerberosConfDataFileWriter = null; + + actionLog.writeStdOut(String.format("Writing configuration changes metadata file to %s", configFile.getAbsolutePath())); + try { + kerberosConfDataFileWriter = kerberosConfigDataFileWriterFactory.createKerberosConfigDataFileWriter(configFile); + + for (Map.Entry<String, Map<String, String>> entry : kerberosConfigurations.entrySet()) { + String type = entry.getKey(); + Map<String, String> properties = entry.getValue(); + + if (properties != null) { + for (Map.Entry<String, String> configTypeEntry : properties.entrySet()) { + kerberosConfDataFileWriter.addRecord(type, + configTypeEntry.getKey(), + configTypeEntry.getValue(), + KerberosConfigDataFileWriter.OPERATION_TYPE_SET); + } + } + } + } catch (IOException e) { + String message = String.format("Failed to write kerberos configurations file - %s", configFile.getAbsolutePath()); + LOG.error(message, e); + actionLog.writeStdOut(message); + actionLog.writeStdErr(message + "\n" + e.getLocalizedMessage()); + throw new AmbariException(message, e); + } finally { + if (kerberosConfDataFileWriter != null) { + try { + kerberosConfDataFileWriter.close(); + } catch (IOException e) { + String message = "Failed to close the kerberos configurations file writer"; + LOG.warn(message, e); + actionLog.writeStdOut(message); + actionLog.writeStdErr(message + "\n" + e.getLocalizedMessage()); + } + } + } + } + + return createCommandReport(0, HostRoleStatus.COMPLETED, "{}", actionLog.getStdOut(), actionLog.getStdErr()); + } + + @Override + protected CommandReport processIdentity(Map<String, String> identityRecord, String evaluatedPrincipal, KerberosOperationHandler operationHandler, Map<String, String> kerberosConfiguration, Map<String, Object> requestSharedDataContext) throws AmbariException { + throw new UnsupportedOperationException(); + } +} + http://git-wip-us.apache.org/repos/asf/ambari/blob/e3acc7f0/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/PrepareKerberosIdentitiesServerAction.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/PrepareKerberosIdentitiesServerAction.java b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/PrepareKerberosIdentitiesServerAction.java new file mode 100644 index 0000000..e39d868 --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/PrepareKerberosIdentitiesServerAction.java @@ -0,0 +1,111 @@ +/* + * 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.serveraction.kerberos; + +import com.google.inject.Inject; +import org.apache.ambari.server.AmbariException; +import org.apache.ambari.server.actionmanager.HostRoleStatus; +import org.apache.ambari.server.agent.CommandReport; +import org.apache.ambari.server.controller.KerberosHelper; +import org.apache.ambari.server.state.Cluster; +import org.apache.ambari.server.state.ServiceComponentHost; +import org.apache.ambari.server.state.kerberos.KerberosDescriptor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentMap; + +/** + * PrepareKerberosIdentitiesServerAction is a ServerAction implementation that prepares metadata needed + * to process Kerberos identities (principals and keytabs files). + */ +public class PrepareKerberosIdentitiesServerAction extends AbstractPrepareKerberosServerAction { + private final static Logger LOG = LoggerFactory.getLogger(PrepareKerberosIdentitiesServerAction.class); + + /** + * KerberosHelper + */ + @Inject + private KerberosHelper kerberosHelper; + + /** + * Called to execute this action. Upon invocation, calls + * {@link KerberosServerAction#processIdentities(Map)} + * to iterate through the Kerberos identity metadata and call + * {@link PrepareKerberosIdentitiesServerAction#processIdentities(Map)} + * for each identity to process. + * + * @param requestSharedDataContext a Map to be used a shared data among all ServerActions related + * to a given request + * @return a CommandReport indicating the result of this action + * @throws AmbariException + * @throws InterruptedException + */ + @Override + public CommandReport execute(ConcurrentMap<String, Object> requestSharedDataContext) throws + AmbariException, InterruptedException { + + Cluster cluster = getCluster(); + + if (cluster == null) { + throw new AmbariException("Missing cluster object"); + } + + KerberosDescriptor kerberosDescriptor = kerberosHelper.getKerberosDescriptor(cluster); + Collection<String> identityFilter = getIdentityFilter(); + List<ServiceComponentHost> schToProcess = kerberosHelper.getServiceComponentHostsToProcess(cluster, + kerberosDescriptor, + getServiceComponentFilter(), + identityFilter, + new KerberosHelper.Command<Boolean, ServiceComponentHost>() { + @Override + public Boolean invoke(ServiceComponentHost sch) throws AmbariException { + return true; + } + }); + + Map<String, String> commandParameters = getCommandParameters(); + String dataDirectory = getCommandParameterValue(commandParameters, DATA_DIRECTORY); + Map<String, Map<String, String>> kerberosConfigurations = new HashMap<String, Map<String, String>>(); + + int schCount = schToProcess.size(); + if (schCount == 0) { + actionLog.writeStdOut("There are no components to process"); + } else if (schCount == 1) { + actionLog.writeStdOut(String.format("Processing %d component", schCount)); + } else { + actionLog.writeStdOut(String.format("Processing %d components", schCount)); + } + + processIdentities(cluster, kerberosDescriptor, schToProcess, identityFilter, dataDirectory, kerberosConfigurations); + + + return createCommandReport(0, HostRoleStatus.COMPLETED, "{}", actionLog.getStdOut(), actionLog.getStdErr()); + } + + @Override + protected CommandReport processIdentity(Map<String, String> identityRecord, String evaluatedPrincipal, KerberosOperationHandler operationHandler, Map<String, String> kerberosConfiguration, Map<String, Object> requestSharedDataContext) throws AmbariException { + throw new UnsupportedOperationException(); + } +} + http://git-wip-us.apache.org/repos/asf/ambari/blob/e3acc7f0/ambari-server/src/main/java/org/apache/ambari/server/state/Cluster.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/Cluster.java b/ambari-server/src/main/java/org/apache/ambari/server/state/Cluster.java index 10204ea..2e2cc29 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/state/Cluster.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/state/Cluster.java @@ -81,6 +81,17 @@ public interface Cluster { List<ServiceComponentHost> getServiceComponentHosts(String hostname); /** + * Get all ServiceComponentHosts for a given service and optional component + * + * If the component name is <code>null</code>, all components for the requested service will be returned. + * + * @param serviceName the name a the desired service + * @param componentName the name a the desired component - null indicates all components for the service + * @return a list of found ServiceComponentHost instances + */ + List<ServiceComponentHost> getServiceComponentHosts(String serviceName, String componentName); + + /** * Get all hosts associated with this cluster. * * @return collection of hosts that are associated with this cluster http://git-wip-us.apache.org/repos/asf/ambari/blob/e3acc7f0/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClusterImpl.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClusterImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClusterImpl.java index 186963f..8fa4034 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClusterImpl.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClusterImpl.java @@ -797,6 +797,30 @@ public class ClusterImpl implements Cluster { } @Override + public List<ServiceComponentHost> getServiceComponentHosts(String serviceName, String componentName) { + ArrayList<ServiceComponentHost> foundItems = new ArrayList<ServiceComponentHost>(); + + loadServiceHostComponents(); + clusterGlobalLock.readLock().lock(); + try { + Map<String, Map<String, ServiceComponentHost>> foundByService = serviceComponentHosts.get(serviceName); + if (foundByService != null) { + if (componentName == null) { + for(Map<String, ServiceComponentHost> foundByComponent :foundByService.values()) { + foundItems.addAll(foundByComponent.values()); + } + } else if (foundByService.containsKey(componentName)) { + foundItems.addAll(foundByService.get(componentName).values()); + } + } + } finally { + clusterGlobalLock.readLock().unlock(); + } + + return foundItems; + } + + @Override public void addService(Service service) throws AmbariException { loadServices();
