/*
* 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.upgrade;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.text.MessageFormat;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicLong;
import org.apache.ambari.server.AmbariException;
import org.apache.ambari.server.configuration.Configuration;
import org.apache.ambari.server.configuration.Configuration.DatabaseType;
import org.apache.ambari.server.controller.AmbariManagementController;
import org.apache.ambari.server.orm.DBAccessor.DBColumnInfo;
import org.apache.ambari.server.orm.dao.DaoUtils;
import org.apache.ambari.server.state.Cluster;
import org.apache.ambari.server.state.Clusters;
import org.apache.ambari.server.state.Config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.support.JdbcUtils;
import com.google.inject.Inject;
import com.google.inject.Injector;
/**
* Upgrade catalog for version 2.1.1.
*/
public class UpgradeCatalog211 extends AbstractUpgradeCatalog {
private static final String HOST_COMPONENT_STATE_TABLE = "hostcomponentstate";
private static final String HOST_COMPONENT_STATE_ID_COLUMN = "id";
private static final String HOST_COMPONENT_STATE_INDEX = "idx_host_component_state";
/**
* Logger.
*/
private static final Logger LOG = LoggerFactory.getLogger(UpgradeCatalog211.class);
// this "id holder" is a field only for a test that verifies "big" 4 digit+
// numbers are formatted correctly
private AtomicLong m_hcsId = new AtomicLong(1);
@Inject
DaoUtils daoUtils;
// ----- Constructors ------------------------------------------------------
/**
* Don't forget to register new UpgradeCatalogs in {@link SchemaUpgradeHelper.UpgradeHelperModule#configure()}
*
* @param injector Guice injector to track dependencies and uses bindings to inject them.
*/
@Inject
public UpgradeCatalog211(Injector injector) {
super(injector);
daoUtils = injector.getInstance(DaoUtils.class);
}
// ----- UpgradeCatalog ----------------------------------------------------
/**
* {@inheritDoc}
*/
@Override
public String getTargetVersion() {
return "2.1.1";
}
// ----- AbstractUpgradeCatalog --------------------------------------------
/**
* {@inheritDoc}
*/
@Override
public String getSourceVersion() {
return "2.1.0";
}
/**
* {@inheritDoc}
*/
@Override
protected void executeDDLUpdates() throws AmbariException, SQLException {
// change out the PK on hostcomponentstate
executeHostComponentStateDDLUpdates();
// make viewinstanceproperty.value & viewinstancedata.value nullable
dbAccessor.setColumnNullable("viewinstanceproperty", "value", true);
dbAccessor.setColumnNullable("viewinstancedata", "value", true);
}
/**
* {@inheritDoc}
*/
@Override
protected void executePreDMLUpdates() throws AmbariException, SQLException {
}
/**
* {@inheritDoc}
*/
@Override
protected void executeDMLUpdates() throws AmbariException, SQLException {
addNewConfigurationsFromXml();
updateExistingConfigurations();
}
// ----- UpgradeCatalog211 --------------------------------------------
/**
* Iterates over the set of clusters to call service-specific configuration
* update routines.
*
* @throws AmbariException
* if an error occurs while updating the configurations
*/
protected void updateExistingConfigurations() throws AmbariException {
AmbariManagementController ambariManagementController = injector.getInstance(AmbariManagementController.class);
Clusters clusters = ambariManagementController.getClusters();
if (clusters != null) {
Map<String, Cluster> clusterMap = clusters.getClusters();
if ((clusterMap != null) && !clusterMap.isEmpty()) {
// Iterate through the clusters and perform any configuration updates
for (final Cluster cluster : clusterMap.values()) {
updateKerberosConfigurations(cluster);
/* *********************************************************
* Add additional configuration update methods here
* ********************************************************* */
}
}
}
}
/**
* Updates the Kerberos configurations for the given cluster
* <p/>
* Performs the following updates:
* <ul>
* <li>Rename <code>create_attributes_template</code> to
* <code>ad_create_attributes_template</code></li>
* </ul>
*
* @param cluster
* the cluster
* @throws AmbariException
* if an error occurs while updating the configurations
*/
protected void updateKerberosConfigurations(Cluster cluster) throws AmbariException {
Config config = cluster.getDesiredConfigByType("kerberos-env");
if (config != null) {
// Rename create_attributes_template to ad_create_attributes_template
String value = config.getProperties().get("create_attributes_template");
Map<String, String> updates = Collections.singletonMap("ad_create_attributes_template", value);
Set<String> removes = Collections.singleton("create_attributes_template");
updateConfigurationPropertiesForCluster(cluster, "kerberos-env", updates, removes, true, false);
}
}
/**
* Perform the DDL updates required to add a new Primary Key ID column to the
* {@code hostcomponentstate} table. This will perform the following actions:
* <ul>
* <li>Add a new column to hostcomponentstate named id</li>
* <li>Populated id with an incrementing long, then make it non-NULL</li>
* <li>Drop the existing PK on hostcomponentstate</li>
* <li>Add a new surrogate PK on hostcomponentstate on the id column</li>
* <li>Add an index on hostcomponentstate for host_id, component_name,
* service_name, cluster_id</li>
* </ul>
*
* @throws AmbariException
* @throws SQLException
*/
private void executeHostComponentStateDDLUpdates() throws AmbariException, SQLException {
if (!dbAccessor.tableHasPrimaryKey(HOST_COMPONENT_STATE_TABLE, HOST_COMPONENT_STATE_ID_COLUMN)) {
// add the new column, nullable for now until we insert unique IDs
dbAccessor.addColumn(HOST_COMPONENT_STATE_TABLE,
new DBColumnInfo(HOST_COMPONENT_STATE_ID_COLUMN, Long.class, null, null, true));
Statement statement = null;
ResultSet resultSet = null;
try {
statement = dbAccessor.getConnection().createStatement();
if (statement != null) {
String selectSQL = MessageFormat.format(
"SELECT id, cluster_id, service_name, component_name, host_id FROM {0} ORDER BY {1} {2}",
HOST_COMPONENT_STATE_TABLE, "id", "DESC");
resultSet = statement.executeQuery(selectSQL);
while (resultSet.next()) {
final Long clusterId = resultSet.getLong("cluster_id");
final String serviceName = resultSet.getString("service_name");
final String componentName = resultSet.getString("component_name");
final Long hostId = resultSet.getLong("host_id");
final Long idKey = resultSet.getLong("id");
if (idKey != 0 && m_hcsId.get() == 1) {
m_hcsId.set(idKey);
m_hcsId.getAndIncrement();
} else if(idKey == 0) {
String updateSQL = MessageFormat.format(
"UPDATE {0} SET {1} = {2,number,#} WHERE cluster_id = {3} AND service_name = ''{4}'' AND component_name = ''{5}'' and host_id = {6,number,#}",
HOST_COMPONENT_STATE_TABLE, HOST_COMPONENT_STATE_ID_COLUMN, m_hcsId.getAndIncrement(),
clusterId, serviceName, componentName, hostId);
dbAccessor.executeQuery(updateSQL);
}
}
}
} finally {
JdbcUtils.closeResultSet(resultSet);
JdbcUtils.closeStatement(statement);
}
// make the column NON NULL now
dbAccessor.alterColumn(HOST_COMPONENT_STATE_TABLE,
new DBColumnInfo(HOST_COMPONENT_STATE_ID_COLUMN, Long.class, null, null, false));
// Add sequence for hostcomponentstate id
addSequence("hostcomponentstate_id_seq", m_hcsId.get(), false);
// drop the current PK
String primaryKeyConstraintName = null;
Configuration.DatabaseType databaseType = configuration.getDatabaseType();
switch (databaseType) {
case POSTGRES: {
primaryKeyConstraintName = "hostcomponentstate_pkey";
break;
}
case ORACLE:
case SQL_SERVER: {
// Oracle and SQL Server require us to lookup the PK name
primaryKeyConstraintName = dbAccessor.getPrimaryKeyConstraintName(
HOST_COMPONENT_STATE_TABLE);
break;
}
default:
break;
}
if (databaseType == DatabaseType.MYSQL) {
String mysqlDropQuery = MessageFormat.format("ALTER TABLE {0} DROP PRIMARY KEY",
HOST_COMPONENT_STATE_TABLE);
dbAccessor.executeQuery(mysqlDropQuery, true);
} else {
// warn if we can't find it
if (null == primaryKeyConstraintName) {
LOG.warn("Unable to determine the primary key constraint name for {}",
HOST_COMPONENT_STATE_TABLE);
} else {
dbAccessor.dropPKConstraint(HOST_COMPONENT_STATE_TABLE, primaryKeyConstraintName, true);
}
}
// create a new PK, matching the name of the constraint found in the SQL
// files
dbAccessor.addPKConstraint(HOST_COMPONENT_STATE_TABLE, "pk_hostcomponentstate", "id");
// create index, ensuring column order matches that of the SQL files
dbAccessor.createIndex(HOST_COMPONENT_STATE_INDEX, HOST_COMPONENT_STATE_TABLE, "host_id",
"component_name", "service_name", "cluster_id");
}
}
}