blob: f48b5ef5f83304be0388e485c0992d7e07d349d4 [file] [log] [blame]
/*
* 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.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;
/**
* 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;
@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 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 = getKerberosDescriptor(cluster);
Collection<String> identityFilter = getIdentityFilter();
List<ServiceComponentHost> schToProcess = getServiceComponentHostsToProcess(cluster, kerberosDescriptor, identityFilter);
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));
}
Map<String, Set<String>> propertiesToBeRemoved = new HashMap<>();
processServiceComponentHosts(cluster, kerberosDescriptor, schToProcess, identityFilter, dataDirectory,
kerberosConfigurations, null, propertiesToBeRemoved, true, "true".equalsIgnoreCase(getCommandParameterValue(commandParameters,
KerberosServerAction.INCLUDE_AMBARI_IDENTITY)));
if ("true".equalsIgnoreCase(getCommandParameterValue(commandParameters, UPDATE_CONFIGURATIONS))) {
processAuthToLocalRules(cluster, kerberosDescriptor, schToProcess, kerberosConfigurations, getDefaultRealm(commandParameters));
processConfigurationChanges(dataDirectory, kerberosConfigurations, propertiesToBeRemoved);
}
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();
}
/**
* Calls {@link KerberosHelper#getServiceComponentHostsToProcess(Cluster, KerberosDescriptor, Map, Collection, Collection, KerberosHelper.Command)}
* with no filter on ServiceComponentHosts
* <p/>
* The <code>shouldProcessCommand</code> implementation passed to KerberosHelper#getServiceComponentHostsToProcess
* always returns true, indicating to process all ServiceComponentHosts.
*
* @param cluster the cluster
* @param kerberosDescriptor the current Kerberos descriptor
* @param identityFilter a list of identities to include, or all if null @return the list of ServiceComponentHosts to process
* @throws AmbariException
* @see KerberosHelper#getServiceComponentHostsToProcess(Cluster, KerberosDescriptor, Map, Collection, Collection, KerberosHelper.Command)
*/
protected List<ServiceComponentHost> getServiceComponentHostsToProcess(Cluster cluster,
KerberosDescriptor kerberosDescriptor,
Collection<String> identityFilter)
throws AmbariException {
return kerberosHelper.getServiceComponentHostsToProcess(cluster,
kerberosDescriptor,
getServiceComponentFilter(),
getHostFilter(), identityFilter,
new KerberosHelper.Command<Boolean, ServiceComponentHost>() {
@Override
public Boolean invoke(ServiceComponentHost sch) throws AmbariException {
return true;
}
});
}
/**
* Calls {@link KerberosHelper#getKerberosDescriptor(Cluster)}
*
* @param cluster cluster instance
* @return the kerberos descriptor associated with the specified cluster
* @throws AmbariException if unable to obtain the descriptor
* @see KerberosHelper#getKerberosDescriptor(Cluster)
*/
protected KerberosDescriptor getKerberosDescriptor(Cluster cluster)
throws AmbariException {
return kerberosHelper.getKerberosDescriptor(cluster);
}
/**
* Conditionally calls {@link KerberosHelper#setAuthToLocalRules(KerberosDescriptor, String, Map, Map, Map)}
* if there are ServiceComponentHosts to process
*
* @param cluster cluster instance
* @param kerberosDescriptor the current Kerberos descriptor
* @param schToProcess a list of ServiceComponentHosts to process
* @param kerberosConfigurations the Kerberos-specific configuration map
* @param defaultRealm the default realm
* @throws AmbariException
* @see KerberosHelper#setAuthToLocalRules(KerberosDescriptor, String, Map, Map, Map)
*/
protected void processAuthToLocalRules(Cluster cluster, KerberosDescriptor kerberosDescriptor,
List<ServiceComponentHost> schToProcess,
Map<String, Map<String, String>> kerberosConfigurations,
String defaultRealm)
throws AmbariException {
if (!schToProcess.isEmpty()) {
actionLog.writeStdOut("Creating auth-to-local rules");
Map<String,Set<String>> services = new HashMap<String, Set<String>>();
for(ServiceComponentHost sch: schToProcess) {
Set<String> components = services.get(sch.getServiceName());
if(components == null) {
components = new HashSet<String>();
services.put(sch.getServiceName(), components);
}
components.add(sch.getServiceComponentName());
}
kerberosHelper.setAuthToLocalRules(kerberosDescriptor, defaultRealm, services,
kerberosHelper.calculateConfigurations(cluster, null, kerberosDescriptor.getProperties()),
kerberosConfigurations);
}
}
/**
* Processes configuration changes to determine if any work needs to be done.
* <p/>
* If work is to be done, a data file containing the details is created so it they changes may be
* processed in the appropriate stage.
*
* @param dataDirectory the directory in which to write the configuration changes data file
* @param kerberosConfigurations the Kerberos-specific configuration map
* @param propertiesToBeRemoved
* @throws AmbariException
*/
protected void processConfigurationChanges(String dataDirectory,
Map<String, Map<String, String>> kerberosConfigurations,
Map<String, Set<String>> propertiesToBeRemoved)
throws AmbariException {
actionLog.writeStdOut("Determining configuration changes");
// 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);
// add properties to be set
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);
}
}
}
// add properties to be removed
if (propertiesToBeRemoved != null) {
for (Map.Entry<String, Set<String>> entry : propertiesToBeRemoved.entrySet()) {
String type = entry.getKey();
Set<String> properties = entry.getValue();
if (properties != null) {
for (String property : properties) {
kerberosConfDataFileWriter.addRecord(type,
property,
"",
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());
}
}
}
}
}
}