content: /*
   * 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.SQLException;
  import java.util.ArrayList;
  import java.util.Arrays;
  import java.util.Collections;
  import java.util.HashMap;
  import java.util.List;
  import java.util.Map;
  
  import jakarta.persistence.EntityManager;
  import jakarta.persistence.Query;
  
  import org.apache.ambari.server.AmbariException;
  import org.apache.ambari.server.controller.AmbariManagementController;
  import org.apache.ambari.server.orm.DBAccessor;
  import org.apache.ambari.server.orm.dao.ArtifactDAO;
  import org.apache.ambari.server.orm.dao.RepositoryVersionDAO;
  import org.apache.ambari.server.orm.entities.ArtifactEntity;
  import org.apache.ambari.server.orm.entities.ClusterConfigEntity;
  import org.apache.ambari.server.orm.entities.RepositoryVersionEntity;
  import org.apache.ambari.server.state.Cluster;
  import org.apache.ambari.server.state.Clusters;
  import org.apache.ambari.server.state.Config;
  import org.apache.ambari.server.state.kerberos.AbstractKerberosDescriptor;
  import org.apache.ambari.server.state.kerberos.AbstractKerberosDescriptorContainer;
  import org.apache.ambari.server.state.kerberos.KerberosComponentDescriptor;
  import org.apache.ambari.server.state.kerberos.KerberosDescriptor;
  import org.apache.ambari.server.state.kerberos.KerberosDescriptorFactory;
  import org.apache.ambari.server.state.kerberos.KerberosIdentityDescriptor;
  import org.apache.ambari.server.state.kerberos.KerberosKeytabDescriptor;
  import org.apache.ambari.server.state.kerberos.KerberosPrincipalDescriptor;
  import org.apache.ambari.server.state.kerberos.KerberosServiceDescriptor;
  import org.apache.commons.lang.StringUtils;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
  import com.google.inject.Inject;
  import com.google.inject.Injector;
  
  /**
   * The {@link org.apache.ambari.server.upgrade.UpgradeCatalog260} upgrades Ambari from 2.5.2 to 2.6.0.
   */
  public class UpgradeCatalog260 extends AbstractUpgradeCatalog {
  
    public static final String CLUSTER_CONFIG_MAPPING_TABLE = "clusterconfigmapping";
    public static final String CLUSTER_VERSION_TABLE = "cluster_version";
    public static final String CLUSTER_ID_COLUMN = "cluster_id";
    public static final String STATE_COLUMN = "state";
    public static final String CREATE_TIMESTAMP_COLUMN = "create_timestamp";
    public static final String VERSION_TAG_COLUMN = "version_tag";
    public static final String TYPE_NAME_COLUMN = "type_name";
  
    public static final String CLUSTER_CONFIG_TABLE = "clusterconfig";
    public static final String SELECTED_COLUMN = "selected";
    public static final String SERVICE_DELETED_COLUMN = "service_deleted";
    public static final String UNMAPPED_COLUMN = "unmapped";
    public static final String SELECTED_TIMESTAMP_COLUMN = "selected_timestamp";
  
    public static final String SERVICE_COMPONENT_DESIRED_STATE_TABLE = "servicecomponentdesiredstate";
    public static final String DESIRED_STACK_ID_COLUMN = "desired_stack_id";
    public static final String DESIRED_VERSION_COLUMN = "desired_version";
    public static final String DESIRED_REPO_VERSION_ID_COLUMN = "desired_repo_version_id";
    public static final String REPO_STATE_COLUMN = "repo_state";
    public static final String FK_SCDS_DESIRED_STACK_ID = "FK_scds_desired_stack_id";
    public static final String FK_SERVICECOMPONENTDESIREDSTATE_DESIRED_STACK_ID = "FK_servicecomponentdesiredstate_desired_stack_id";
    public static final String FK_SCDS_DESIRED_REPO_ID = "FK_scds_desired_repo_id";
  
    public static final String REPO_VERSION_TABLE = "repo_version";
    public static final String REPO_VERSION_ID_COLUMN = "repo_version_id";
    public static final String REPO_VERSION_RESOLVED_COLUMN = "resolved";
    public static final String REPO_VERSION_HIDDEN_COLUMN = "hidden";
    public static final String REPO_VERSION_LEGACY_COLUMN = "legacy";
  
    public static final String HOST_COMPONENT_DESIRED_STATE_TABLE = "hostcomponentdesiredstate";
    public static final String FK_HCDS_DESIRED_STACK_ID = "FK_hcds_desired_stack_id";
  
    public static final String HOST_COMPONENT_STATE_TABLE = "hostcomponentstate";
    public static final String CURRENT_STACK_ID_COLUMN = "current_stack_id";
    public static final String FK_HCS_CURRENT_STACK_ID = "FK_hcs_current_stack_id";
  
    public static final String HOST_VERSION_TABLE = "host_version";
    public static final String UQ_HOST_REPO = "UQ_host_repo";
    public static final String HOST_ID_COLUMN = "host_id";
  
    public static final String SERVICE_DESIRED_STATE_TABLE = "servicedesiredstate";
    public static final String FK_SDS_DESIRED_STACK_ID = "FK_sds_desired_stack_id";
    public static final String FK_REPO_VERSION_ID = "FK_repo_version_id";
  
    public static final String CLUSTERS_TABLE = "clusters";
  
    public static final String UPGRADE_TABLE = "upgrade";
    public static final String UPGRADE_GROUP_TABLE = "upgrade_group";
    public static final String UPGRADE_ITEM_TABLE = "upgrade_item";
    public static final String FROM_REPO_VERSION_ID_COLUMN = "from_repo_version_id";
    public static final String TO_REPO_VERSION_ID_COLUMN = "to_repo_version_id";
    public static final String ORCHESTRATION_COLUMN = "orchestration";
    public static final String ALLOW_REVERT_COLUMN = "revert_allowed";
    public static final String FK_UPGRADE_FROM_REPO_ID = "FK_upgrade_from_repo_id";
    public static final String FK_UPGRADE_TO_REPO_ID = "FK_upgrade_to_repo_id";
    public static final String FK_UPGRADE_REPO_VERSION_ID = "FK_upgrade_repo_version_id";
    public static final String UPGRADE_ITEM_ITEM_TEXT = "item_text";
  
    public static final String SERVICE_COMPONENT_HISTORY_TABLE = "servicecomponent_history";
    public static final String UPGRADE_HISTORY_TABLE = "upgrade_history";
    public static final String ID_COLUMN = "id";
    public static final String UPGRADE_ID_COLUMN = "upgrade_id";
    public static final String SERVICE_NAME_COLUMN = "service_name";
    public static final String COMPONENT_NAME_COLUMN = "component_name";
    public static final String TARGET_REPO_VERSION_ID_COLUMN = "target_repo_version_id";
    public static final String PK_UPGRADE_HIST = "PK_upgrade_hist";
    public static final String FK_UPGRADE_HIST_UPGRADE_ID = "FK_upgrade_hist_upgrade_id";
    public static final String FK_UPGRADE_HIST_FROM_REPO = "FK_upgrade_hist_from_repo";
    public static final String FK_UPGRADE_HIST_TARGET_REPO = "FK_upgrade_hist_target_repo";
    public static final String UQ_UPGRADE_HIST = "UQ_upgrade_hist";
    public static final String SERVICE_CONFIG_MAPPING_TABLE = "serviceconfigmapping";
    public static final String SERVICE_COMPONENT_DESIRED_STATE = "servicecomponentdesiredstate";
    public static final String HOST_COMPONENT_DESIRED_STATE = "hostcomponentdesiredstate";
    public static final String HOST_COMPONENT_STATE = "hostcomponentstate";
  
    private static final String CORE_SITE = "core-site";
    public static final String AMS_SSL_CLIENT = "ams-ssl-client";
    public static final String METRIC_TRUSTSTORE_ALIAS = "ssl.client.truststore.alias";
  
    private static final String HIVE_INTERACTIVE_SITE = "hive-interactive-site";
    public static final String HIVE_LLAP_DAEMON_KEYTAB_FILE = "hive.llap.daemon.keytab.file";
    public static final String HIVE_LLAP_ZK_SM_KEYTAB_FILE = "hive.llap.zk.sm.keytab.file";
    public static final String HIVE_LLAP_TASK_KEYTAB_FILE = "hive.llap.task.keytab.file";
    public static final String HIVE_SERVER_KERBEROS_PREFIX = "/HIVE/HIVE_SERVER/";
    public static final String YARN_LLAP_ZK_HIVE_KERBEROS_IDENTITY = "llap_zk_hive";
    public static final String YARN_LLAP_TASK_HIVE_KERBEROS_IDENTITY = "llap_task_hive";
    public static final String HIVE_SERVER_HIVE_KERBEROS_IDENTITY = "hive_server_hive";
  
    // Used to track whether YARN -> NODEMANAGER -> 'llap_zk_hive' kerberos descriptor was updated or not.
    private List<String> yarnKerberosDescUpdatedList = new ArrayList<>();
  
  
    /**
     * Logger.
     */
    private static final Logger LOG = LoggerFactory.getLogger(UpgradeCatalog260.class);
    public static final String STANDARD = "STANDARD";
    public static final String NOT_REQUIRED = "NOT_REQUIRED";
    public static final String CURRENT = "CURRENT";
    public static final String SELECTED = "1";
    public static final String VIEWURL_TABLE = "viewurl";
    public static final String VIEWINSTANCE_TABLE = "viewinstance";
    public static final String PK_VIEWURL = "PK_viewurl";
    public static final String URL_ID_COLUMN = "url_id";
    public static final String STALE_POSTGRESS_VIEWURL_PKEY = "viewurl_pkey";
    public static final String USERS_TABLE = "users";
    public static final String STALE_POSTGRESS_USERS_LDAP_USER_KEY = "users_ldap_user_key";
    public static final String SHORT_URL_COLUMN = "short_url";
    public static final String FK_INSTANCE_URL_ID = "FK_instance_url_id";
    public static final String FK_SERVICEDESIREDSTATE_DESIRED_STACK_ID = "FK_servicedesiredstate_desired_stack_id";
    public static final String FK_HOSTCOMPONENTDESIREDSTATE_DESIRED_STACK_ID = "FK_hostcomponentdesiredstate_desired_stack_id";
    public static final String FK_HOSTCOMPONENTSTATE_CURRENT_STACK_ID = "FK_hostcomponentstate_current_stack_id";
    public static final String FK_UPGRADE_FROM_REPO_VERSION_ID = "FK_upgrade_from_repo_version_id";
    public static final String FK_UPGRADE_TO_REPO_VERSION_ID = "FK_upgrade_to_repo_version_id";
  
  
    /**
     * Constructor.
     *
     * @param injector
     */
    @Inject
    public UpgradeCatalog260(Injector injector) {
      super(injector);
    }
  
    /**
     * {@inheritDoc}
     */
    @Override
    public String getSourceVersion()
    {
      return "2.5.2";
    }
  
    /**
     * {@inheritDoc}
     */
    @Override
    public String getTargetVersion() {
      return "2.6.0";
    }
  
    /**
     * {@inheritDoc}
     */
    @Override
    protected void executeDDLUpdates() throws AmbariException, SQLException {
      Integer currentVersionID = getCurrentVersionID();
      dropBrokenFK();
      updateServiceComponentDesiredStateTable(currentVersionID);
      updateServiceDesiredStateTable(currentVersionID);
      addSelectedCollumsToClusterconfigTable();
      updateHostComponentDesiredStateTable();
      updateHostComponentStateTable();
      dropStaleTables();
      updateUpgradeTable();
      createUpgradeHistoryTable();
      updateRepositoryVersionTable();
      renameServiceDeletedColumn();
      addLegacyColumn();
      expandUpgradeItemItemTextColumn();
      addViewUrlPKConstraint();
      removeStaleConstraints();
    }
  
    /**
     * Drop broken FK
     * {@value #FK_SERVICECOMPONENTDESIREDSTATE_DESIRED_STACK_ID}
     * {@value #FK_SERVICEDESIREDSTATE_DESIRED_STACK_ID}
     * {@value #FK_HOSTCOMPONENTDESIREDSTATE_DESIRED_STACK_ID}
     * {@value #FK_HOSTCOMPONENTSTATE_CURRENT_STACK_ID}
     * {@value #FK_UPGRADE_FROM_REPO_VERSION_ID}
     * {@value #FK_UPGRADE_TO_REPO_VERSION_ID}
     */
    private void dropBrokenFK() throws SQLException {
      dbAccessor.dropFKConstraint(UPGRADE_TABLE, FK_UPGRADE_FROM_REPO_VERSION_ID);
      dbAccessor.dropFKConstraint(UPGRADE_TABLE, FK_UPGRADE_TO_REPO_VERSION_ID);
      dbAccessor.dropFKConstraint(SERVICE_COMPONENT_DESIRED_STATE_TABLE, FK_SERVICECOMPONENTDESIREDSTATE_DESIRED_STACK_ID);
      dbAccessor.dropFKConstraint(SERVICE_DESIRED_STATE_TABLE, FK_SERVICEDESIREDSTATE_DESIRED_STACK_ID);
      dbAccessor.dropFKConstraint(HOST_COMPONENT_DESIRED_STATE_TABLE, FK_HOSTCOMPONENTDESIREDSTATE_DESIRED_STACK_ID);
      dbAccessor.dropFKConstraint(HOST_COMPONENT_STATE_TABLE, FK_HOSTCOMPONENTSTATE_CURRENT_STACK_ID);
    }
  
  
    /**
     * Updates {@value #VIEWURL_TABLE} table.
     * Adds the {@value #PK_VIEWURL} constraint.
     */
    private void addViewUrlPKConstraint() throws SQLException {
      dbAccessor.dropFKConstraint(VIEWINSTANCE_TABLE, FK_INSTANCE_URL_ID);
      dbAccessor.dropPKConstraint(VIEWURL_TABLE, STALE_POSTGRESS_VIEWURL_PKEY);
      dbAccessor.addPKConstraint(VIEWURL_TABLE, PK_VIEWURL, URL_ID_COLUMN);
      dbAccessor.addFKConstraint(VIEWINSTANCE_TABLE, FK_INSTANCE_URL_ID,
          SHORT_URL_COLUMN, VIEWURL_TABLE, URL_ID_COLUMN, false);
    }
  
    /**
     * remove stale unnamed constraints
     */
    private void removeStaleConstraints() throws SQLException {
      dbAccessor.dropUniqueConstraint(USERS_TABLE, STALE_POSTGRESS_USERS_LDAP_USER_KEY);
    }
  
    /**
     * Expand item_text column of upgrade_item
     */
    private void expandUpgradeItemItemTextColumn() throws SQLException {
      dbAccessor.changeColumnType(UPGRADE_ITEM_TABLE, UPGRADE_ITEM_ITEM_TEXT,
        String.class, char[].class);
    }
  
    private void addLegacyColumn() throws AmbariException, SQLException {
      Boolean isLegacyColumnExists = dbAccessor.tableHasColumn(REPO_VERSION_TABLE, REPO_VERSION_LEGACY_COLUMN);
      if (!isLegacyColumnExists) {
        DBAccessor.DBColumnInfo legacyColumn = new DBAccessor.DBColumnInfo(REPO_VERSION_LEGACY_COLUMN, Short.class, null, 1, false);
        dbAccessor.addColumn(REPO_VERSION_TABLE, legacyColumn);
  
        legacyColumn.setDefaultValue(0);
        dbAccessor.alterColumn(REPO_VERSION_TABLE, legacyColumn);
      }
    }
  
    private void renameServiceDeletedColumn() throws AmbariException, SQLException {
      if (dbAccessor.tableHasColumn(CLUSTER_CONFIG_TABLE, SERVICE_DELETED_COLUMN)) {
        dbAccessor.renameColumn(CLUSTER_CONFIG_TABLE, SERVICE_DELETED_COLUMN, new DBAccessor.DBColumnInfo(UNMAPPED_COLUMN, Short.class, null, 0, false));
      }
    }
  
    /*
    * This method, search for configs which are not linked with any service
    * and set "unmapped" to true. We need that because in case when
    * config is not mapped and "unmapped" flag = false, db consistency check
    * will show warning about not mapped config. (AMBARI-21795)
    * */
    private void setUnmappedForOrphanedConfigs() {
      executeInTransaction(new Runnable() {
        @Override
        public void run() {
          EntityManager entityManager = getEntityManagerProvider().get();
          Query query = entityManager.createNamedQuery("ClusterConfigEntity.findNotMappedClusterConfigsToService",ClusterConfigEntity.class);
  
          List<ClusterConfigEntity> notMappedConfigs =  query.getResultList();
          if (notMappedConfigs != null) {
            for (ClusterConfigEntity clusterConfigEntity : notMappedConfigs) {
              clusterConfigEntity.setUnmapped(true);
              entityManager.merge(clusterConfigEntity);
            }
          }
        }
      });
    }
  
    private void createUpgradeHistoryTable() throws SQLException {
      List<DBAccessor.DBColumnInfo> columns = new ArrayList<>();
  
      columns.add(new DBAccessor.DBColumnInfo(ID_COLUMN, Long.class, null, null, false));
      columns.add(new DBAccessor.DBColumnInfo(UPGRADE_ID_COLUMN, Long.class, null, null, false));
      columns.add(new DBAccessor.DBColumnInfo(SERVICE_NAME_COLUMN, String.class, 255, null, false));
      columns.add(new DBAccessor.DBColumnInfo(COMPONENT_NAME_COLUMN, String.class, 255, null, false));
      columns.add(new DBAccessor.DBColumnInfo(FROM_REPO_VERSION_ID_COLUMN, Long.class, null, null, false));
      columns.add(new DBAccessor.DBColumnInfo(TARGET_REPO_VERSION_ID_COLUMN, Long.class, null, null, false));
      dbAccessor.createTable(UPGRADE_HISTORY_TABLE, columns);
  
      dbAccessor.addPKConstraint(UPGRADE_HISTORY_TABLE, PK_UPGRADE_HIST, ID_COLUMN);
  
      dbAccessor.addFKConstraint(UPGRADE_HISTORY_TABLE, FK_UPGRADE_HIST_UPGRADE_ID, UPGRADE_ID_COLUMN, UPGRADE_TABLE, UPGRADE_ID_COLUMN, false);
      dbAccessor.addFKConstraint(UPGRADE_HISTORY_TABLE, FK_UPGRADE_HIST_FROM_REPO, FROM_REPO_VERSION_ID_COLUMN, REPO_VERSION_TABLE, REPO_VERSION_ID_COLUMN, false);
      dbAccessor.addFKConstraint(UPGRADE_HISTORY_TABLE, FK_UPGRADE_HIST_TARGET_REPO, TARGET_REPO_VERSION_ID_COLUMN, REPO_VERSION_TABLE, REPO_VERSION_ID_COLUMN, false);
      dbAccessor.addUniqueConstraint(UPGRADE_HISTORY_TABLE, UQ_UPGRADE_HIST, UPGRADE_ID_COLUMN, COMPONENT_NAME_COLUMN, SERVICE_NAME_COLUMN);
  
      addSequence("upgrade_history_id_seq", 0L, false);
    }
  
    /**
     * Updates {@value #UPGRADE_TABLE} table.
     * clear {@value #UPGRADE_TABLE} table
     * Removes {@value #FROM_REPO_VERSION_ID_COLUMN} column.
     * Removes {@value #TO_REPO_VERSION_ID_COLUMN} column.
     * Adds the {@value #ORCHESTRATION_COLUMN} column.
     * Adds the {@value #REPO_VERSION_ID_COLUMN} column.
     * Removes {@value #FK_UPGRADE_FROM_REPO_ID} foreign key.
     * Removes {@value #FK_UPGRADE_TO_REPO_ID} foreign key.
     * adds {@value #FK_REPO_VERSION_ID} foreign key.
     *
     * @throws java.sql.SQLException
     */
    private void updateUpgradeTable() throws SQLException {
      dbAccessor.clearTableColumn(CLUSTERS_TABLE, UPGRADE_ID_COLUMN, null);
  
      dbAccessor.clearTable(UPGRADE_ITEM_TABLE);
      dbAccessor.clearTable(UPGRADE_GROUP_TABLE);
      dbAccessor.clearTable(UPGRADE_TABLE);
      dbAccessor.dropFKConstraint(UPGRADE_TABLE, FK_UPGRADE_FROM_REPO_ID);
      dbAccessor.dropFKConstraint(UPGRADE_TABLE, FK_UPGRADE_TO_REPO_ID);
      dbAccessor.dropColumn(UPGRADE_TABLE, FROM_REPO_VERSION_ID_COLUMN);
      dbAccessor.dropColumn(UPGRADE_TABLE, TO_REPO_VERSION_ID_COLUMN);
  
      dbAccessor.addColumn(UPGRADE_TABLE,
          new DBAccessor.DBColumnInfo(REPO_VERSION_ID_COLUMN, Long.class, null, null, false));
  
      dbAccessor.addColumn(UPGRADE_TABLE,
          new DBAccessor.DBColumnInfo(ORCHESTRATION_COLUMN, String.class, 255, STANDARD, false));
  
      dbAccessor.addColumn(UPGRADE_TABLE,
          new DBAccessor.DBColumnInfo(ALLOW_REVERT_COLUMN, Short.class, null, 0, false));
  
      dbAccessor.addFKConstraint(UPGRADE_TABLE, FK_UPGRADE_REPO_VERSION_ID, REPO_VERSION_ID_COLUMN, REPO_VERSION_TABLE, REPO_VERSION_ID_COLUMN, false);
    }
  
    /**
     * Updates {@value #SERVICE_DESIRED_STATE_TABLE} table.
     * Removes {@value #DESIRED_STACK_ID_COLUMN} column.
     * Adds the {@value #DESIRED_REPO_VERSION_ID_COLUMN} column.
     * Removes {@value #FK_SDS_DESIRED_STACK_ID} foreign key.
     * adds {@value #FK_REPO_VERSION_ID} foreign key.
     *
     * @param currentRepoID id of current repo_version. Can be null if there are no cluster repo versions
     *                      (in this case {@value #SERVICE_DESIRED_STATE_TABLE} table must be empty)
     *
     * @throws java.sql.SQLException
     */
    private void updateServiceDesiredStateTable(Integer currentRepoID) throws SQLException {
      //in case if currentRepoID is null {@value #SERVICE_DESIRED_STATE_TABLE} table must be empty and null defaultValue is ok for non-nullable column
      dbAccessor.addColumn(SERVICE_DESIRED_STATE_TABLE,
          new DBAccessor.DBColumnInfo(DESIRED_REPO_VERSION_ID_COLUMN, Long.class, null, currentRepoID, false));
      dbAccessor.alterColumn(SERVICE_DESIRED_STATE_TABLE,
          new DBAccessor.DBColumnInfo(DESIRED_REPO_VERSION_ID_COLUMN, Long.class, null, null, false));
  
      dbAccessor.addFKConstraint(SERVICE_DESIRED_STATE_TABLE, FK_REPO_VERSION_ID, DESIRED_REPO_VERSION_ID_COLUMN, REPO_VERSION_TABLE, REPO_VERSION_ID_COLUMN, false);
      dbAccessor.dropFKConstraint(SERVICE_DESIRED_STATE_TABLE, FK_SDS_DESIRED_STACK_ID);
      dbAccessor.dropColumn(SERVICE_DESIRED_STATE_TABLE, DESIRED_STACK_ID_COLUMN);
    }
  
    /**
     * drop {@value #CLUSTER_CONFIG_MAPPING_TABLE} and {@value #CLUSTER_VERSION_TABLE} tables.
     *
     * @throws java.sql.SQLException
     */
    private void dropStaleTables() throws SQLException {
      dbAccessor.dropTable(CLUSTER_CONFIG_MAPPING_TABLE);
      dbAccessor.dropTable(CLUSTER_VERSION_TABLE);
      dbAccessor.dropTable(SERVICE_COMPONENT_HISTORY_TABLE);
    }
  
    /**
     * Adds the {@value #SELECTED_COLUMN} and {@value #SELECTED_TIMESTAMP_COLUMN} columns to the
     * {@value #CLUSTER_CONFIG_TABLE} table.
     *
     * @throws java.sql.SQLException
     */
    private void addSelectedCollumsToClusterconfigTable() throws SQLException {
      DBAccessor.DBColumnInfo selectedColumnInfo = new DBAccessor.DBColumnInfo(SELECTED_COLUMN, Short.class, null, 0, false);
      DBAccessor.DBColumnInfo selectedmappingColumnInfo = new DBAccessor.DBColumnInfo(SELECTED_COLUMN, Integer.class, null, 0, false);
      DBAccessor.DBColumnInfo selectedTimestampColumnInfo = new DBAccessor.DBColumnInfo(SELECTED_TIMESTAMP_COLUMN, Long.class, null, 0, false);
      DBAccessor.DBColumnInfo createTimestampColumnInfo = new DBAccessor.DBColumnInfo(CREATE_TIMESTAMP_COLUMN, Long.class, null, null, false);
      dbAccessor.copyColumnToAnotherTable(CLUSTER_CONFIG_MAPPING_TABLE, selectedmappingColumnInfo,
          CLUSTER_ID_COLUMN, TYPE_NAME_COLUMN, VERSION_TAG_COLUMN, CLUSTER_CONFIG_TABLE, selectedColumnInfo,
          CLUSTER_ID_COLUMN, TYPE_NAME_COLUMN, VERSION_TAG_COLUMN, SELECTED_COLUMN, SELECTED, 0);
  
      dbAccessor.copyColumnToAnotherTable(CLUSTER_CONFIG_MAPPING_TABLE, createTimestampColumnInfo,
          CLUSTER_ID_COLUMN, TYPE_NAME_COLUMN, VERSION_TAG_COLUMN, CLUSTER_CONFIG_TABLE, selectedTimestampColumnInfo,
          CLUSTER_ID_COLUMN, TYPE_NAME_COLUMN, VERSION_TAG_COLUMN, SELECTED_COLUMN, SELECTED, 0);
    }
  
  
    /**
     * Updates {@value #SERVICE_COMPONENT_DESIRED_STATE_TABLE} table.
     * Removes {@value #DESIRED_VERSION_COLUMN},{@value #DESIRED_STACK_ID_COLUMN} columns.
     * Adds the {@value #DESIRED_REPO_VERSION_ID_COLUMN},{@value #REPO_STATE_COLUMN} columns.
     * Removes {@value #FK_SCDS_DESIRED_STACK_ID} foreign key.
     * adds {@value #FK_SCDS_DESIRED_REPO_ID} foreign key.
     *
     * @param currentRepoID id of current repo_version. Can be null if there are no cluster repo versions
     *                      (in this case {@value #SERVICE_DESIRED_STATE_TABLE} table must be empty)
     *
     * @throws java.sql.SQLException
     */
    private void updateServiceComponentDesiredStateTable(Integer currentRepoID) throws SQLException {
      //in case if currentRepoID is null {@value #SERVICE_DESIRED_STATE_TABLE} table must be empty and null defaultValue is ok for non-nullable column
      dbAccessor.addColumn(SERVICE_COMPONENT_DESIRED_STATE_TABLE,
          new DBAccessor.DBColumnInfo(DESIRED_REPO_VERSION_ID_COLUMN, Long.class, null, currentRepoID, false));
      dbAccessor.alterColumn(SERVICE_COMPONENT_DESIRED_STATE_TABLE,
          new DBAccessor.DBColumnInfo(DESIRED_REPO_VERSION_ID_COLUMN, Long.class, null, null, false));
  
      dbAccessor.addColumn(SERVICE_COMPONENT_DESIRED_STATE_TABLE,
          new DBAccessor.DBColumnInfo(REPO_STATE_COLUMN, String.class, 255, CURRENT, false));
      dbAccessor.alterColumn(SERVICE_COMPONENT_DESIRED_STATE_TABLE,
          new DBAccessor.DBColumnInfo(REPO_STATE_COLUMN, String.class, 255, NOT_REQUIRED, false));
  
      dbAccessor.addFKConstraint(SERVICE_COMPONENT_DESIRED_STATE_TABLE, FK_SCDS_DESIRED_REPO_ID, DESIRED_REPO_VERSION_ID_COLUMN, REPO_VERSION_TABLE, REPO_VERSION_ID_COLUMN, false);
  
      dbAccessor.dropFKConstraint(SERVICE_COMPONENT_DESIRED_STATE_TABLE, FK_SCDS_DESIRED_STACK_ID);
      dbAccessor.dropColumn(SERVICE_COMPONENT_DESIRED_STATE_TABLE, DESIRED_STACK_ID_COLUMN);
      dbAccessor.dropColumn(SERVICE_COMPONENT_DESIRED_STATE_TABLE, DESIRED_VERSION_COLUMN);
    }
  
    /**
     * Updates {@value #HOST_COMPONENT_DESIRED_STATE_TABLE} table.
     * Removes {@value #DESIRED_STACK_ID_COLUMN} column.
     * Removes {@value #FK_HCDS_DESIRED_STACK_ID} foreign key.
     *
     * @throws java.sql.SQLException
     */
    private void updateHostComponentDesiredStateTable() throws SQLException {
      dbAccessor.dropFKConstraint(HOST_COMPONENT_DESIRED_STATE_TABLE, FK_HCDS_DESIRED_STACK_ID);
      dbAccessor.dropColumn(HOST_COMPONENT_DESIRED_STATE_TABLE, DESIRED_STACK_ID_COLUMN);
    }
  
    /**
     * Updates {@value #HOST_COMPONENT_STATE_TABLE} table.
     * Removes {@value #CURRENT_STACK_ID_COLUMN} column.
     * Removes {@value #FK_HCS_CURRENT_STACK_ID} foreign key.
     *
     * @throws java.sql.SQLException
     */
    private void updateHostComponentStateTable() throws SQLException {
      dbAccessor.dropFKConstraint(HOST_COMPONENT_STATE_TABLE, FK_HCS_CURRENT_STACK_ID);
      dbAccessor.dropColumn(HOST_COMPONENT_STATE_TABLE, CURRENT_STACK_ID_COLUMN);
    }
  
    /**
     * Updates {@value #REPO_VERSION_TABLE} table. Adds the following columns:
     * <ul>
     * <li>{@value #REPO_VERSION_HIDDEN_COLUMN}
     * <li>{@value #REPO_VERSION_RESOLVED_COLUMN}
     * </ul>
     *
     * @throws java.sql.SQLException
     */
    private void updateRepositoryVersionTable() throws SQLException {
      dbAccessor.addColumn(REPO_VERSION_TABLE,
          new DBAccessor.DBColumnInfo(REPO_VERSION_HIDDEN_COLUMN, Short.class, null, 0, false));
  
      dbAccessor.addColumn(REPO_VERSION_TABLE,
          new DBAccessor.DBColumnInfo(REPO_VERSION_RESOLVED_COLUMN, Short.class, null, 0, false));
    }
  
    /**
     * {@inheritDoc}
     */
    @Override
    protected void executePreDMLUpdates() throws AmbariException, SQLException {
      removeSupersetFromDruid();
    }
  
    /**
     * {@inheritDoc}
     */
    @Override
    protected void executeDMLUpdates() throws AmbariException, SQLException {
      addNewConfigurationsFromXml();
      setUnmappedForOrphanedConfigs();
      ensureZeppelinProxyUserConfigs();
      updateKerberosDescriptorArtifacts();
      updateAmsConfigs();
      updateHiveConfigs();
      updateHDFSWidgetDefinition();
      updateExistingRepositoriesToBeResolved();
    }
  
    /**
     * get {@value #REPO_VERSION_ID_COLUMN} value from {@value #CLUSTER_VERSION_TABLE}
     * where {@value #STATE_COLUMN} = {@value #CURRENT}
     * and validate it
     *
     * @return current version ID or null if no cluster versions do exist
     * @throws AmbariException if cluster versions are present, but current is not selected
     * @throws SQLException
     */
    public Integer getCurrentVersionID() throws AmbariException, SQLException {
      if (!dbAccessor.tableExists(CLUSTER_VERSION_TABLE)) {
        return null;
      }
      List<Integer> currentVersionList = dbAccessor.getIntColumnValues(CLUSTER_VERSION_TABLE, REPO_VERSION_ID_COLUMN,
          new String[]{STATE_COLUMN}, new String[]{CURRENT}, false);
      if (currentVersionList.isEmpty()) {
        List<Integer> allVersionList = dbAccessor.getIntColumnValues(CLUSTER_VERSION_TABLE, REPO_VERSION_ID_COLUMN, null, null,false);
        if (allVersionList.isEmpty()){
          return null;
        } else {
          throw new AmbariException("Unable to find any CURRENT repositories.");
        }
      } else if (currentVersionList.size() != 1) {
        throw new AmbariException("The following repositories were found to be CURRENT: ".concat(StringUtils.join(currentVersionList, ",")));
      }
      return currentVersionList.get(0);
    }
  
    // Superset is moved as an independent service in Ambari-2.6.
    // This will remove superset component if installed under Druid Service
    protected void removeSupersetFromDruid() throws SQLException {
      removeComponent("DRUID_SUPERSET", "druid-superset");
    }
  
    private void removeComponent(String componentName, String configPrefix) throws SQLException {
  
      String serviceConfigMappingRemoveSQL = String.format(
          "DELETE FROM %s WHERE config_id IN (SELECT config_id from %s where type_name like '%s%%')",
          SERVICE_CONFIG_MAPPING_TABLE, CLUSTER_CONFIG_TABLE, configPrefix);
  
      String supersetConfigRemoveSQL = String.format(
          "DELETE FROM %s WHERE type_name like '%s%%'",
          CLUSTER_CONFIG_TABLE, configPrefix);
  
      String hostComponentDesiredStateRemoveSQL = String.format(
          "DELETE FROM %s WHERE component_name = '%s'",
          HOST_COMPONENT_DESIRED_STATE, componentName);
  
      String hostComponentStateRemoveSQL = String.format(
          "DELETE FROM %s WHERE component_name = '%s'",
          HOST_COMPONENT_STATE, componentName);
  
      String serviceComponentDesiredStateRemoveSQL = String.format(
          "DELETE FROM %s WHERE component_name = '%s'",
          SERVICE_COMPONENT_DESIRED_STATE, componentName);
  
      dbAccessor.executeQuery(serviceConfigMappingRemoveSQL);
      dbAccessor.executeQuery(supersetConfigRemoveSQL);
      dbAccessor.executeQuery(hostComponentDesiredStateRemoveSQL);
      dbAccessor.executeQuery(hostComponentStateRemoveSQL);
      dbAccessor.executeQuery(serviceComponentDesiredStateRemoveSQL);
    }
  
    /**
     * If Zeppelin is installed, ensure that the proxyuser configurations are set in <code>core-site</code>.
     * <p>
     * The following configurations will be added, if core-site exists and the properties are not in the
     * set of core-site properties:
     * <ul>
     * <li><code>"hadoop.proxyuser.{zeppelin-env/zeppelin_user}.groups": "*"</code></li>
     * <li><code>"hadoop.proxyuser.{zeppelin-env/zeppelin_user}.hosts": "*"</code></li>
     * </ul>
     */
    void ensureZeppelinProxyUserConfigs() throws AmbariException {
      Clusters clusters = injector.getInstance(Clusters.class);
      Map<String, Cluster> clusterMap = getCheckedClusterMap(clusters);
  
      if ((clusterMap != null) && !clusterMap.isEmpty()) {
        for (final Cluster cluster : clusterMap.values()) {
          Config zeppelinEnvConfig = cluster.getDesiredConfigByType("zeppelin-env");
  
          if (zeppelinEnvConfig != null) {
            // If zeppelin-env exists, than it is assumed that Zeppelin is installed
            Map<String, String> zeppelinEnvProperties = zeppelinEnvConfig.getProperties();
  
            String zeppelinUser = null;
            if (zeppelinEnvProperties != null) {
              zeppelinUser = zeppelinEnvProperties.get("zeppelin_user");
            }
  
            if (!StringUtils.isEmpty(zeppelinUser)) {
              // If the zeppelin user is set, see if the proxyuser configs need to be set
  
              Config coreSiteConfig = cluster.getDesiredConfigByType(CORE_SITE);
              if (coreSiteConfig != null) {
                // If core-site exists, ensure the proxyuser configurations for Zeppelin are set.
                // If they are not already set, set them to their default value.
                String proxyUserHostsName = String.format("hadoop.proxyuser.%s.hosts", zeppelinUser);
                String proxyUserGroupsName = String.format("hadoop.proxyuser.%s.groups", zeppelinUser);
  
                Map<String, String> proxyUserProperties = new HashMap<>();
                proxyUserProperties.put(proxyUserHostsName, "*");
                proxyUserProperties.put(proxyUserGroupsName, "*");
  
                Map<String, String> coreSiteConfigProperties = coreSiteConfig.getProperties();
  
                if (coreSiteConfigProperties != null) {
                  if (coreSiteConfigProperties.containsKey(proxyUserHostsName)) {
                    proxyUserProperties.remove(proxyUserHostsName);
                  }
  
                  if (coreSiteConfigProperties.containsKey(proxyUserGroupsName)) {
                    proxyUserProperties.remove(proxyUserGroupsName);
                  }
                }
  
                if (!proxyUserProperties.isEmpty()) {
                  updateConfigurationPropertiesForCluster(cluster, CORE_SITE, proxyUserProperties, true, false);
                }
              }
            }
          }
        }
      }
    }
  
    /**
     * {@inheritDoc}
     */
    @Override
    protected void updateKerberosDescriptorArtifact(ArtifactDAO artifactDAO, ArtifactEntity artifactEntity) throws AmbariException {
      if (artifactEntity != null) {
        Map<String, Object> data = artifactEntity.getArtifactData();
        if (data != null) {
          final KerberosDescriptor kerberosDescriptor = new KerberosDescriptorFactory().createInstance(data);
          if (kerberosDescriptor != null) {
            fixRangerKMSKerberosDescriptor(kerberosDescriptor);
            fixIdentityReferences(getCluster(artifactEntity), kerberosDescriptor);
            fixYarnHsiKerberosDescriptorAndSiteConfig(getCluster(artifactEntity), kerberosDescriptor);
  
            artifactEntity.setArtifactData(kerberosDescriptor.toMap());
            artifactDAO.merge(artifactEntity);
          }
        }
      }
    }
  
    protected void fixRangerKMSKerberosDescriptor(KerberosDescriptor kerberosDescriptor) {
      KerberosServiceDescriptor rangerKmsServiceDescriptor = kerberosDescriptor.getService("RANGER_KMS");
      if (rangerKmsServiceDescriptor != null) {
  
        KerberosIdentityDescriptor rangerKmsServiceIdentity = rangerKmsServiceDescriptor.getIdentity("/smokeuser");
        if (rangerKmsServiceIdentity != null) {
          rangerKmsServiceDescriptor.removeIdentity("/smokeuser");
        }
        KerberosComponentDescriptor rangerKmscomponentDescriptor = rangerKmsServiceDescriptor.getComponent("RANGER_KMS_SERVER");
        if (rangerKmscomponentDescriptor != null) {
          KerberosIdentityDescriptor rangerKmsComponentIdentity = rangerKmscomponentDescriptor.getIdentity("/smokeuser");
          if (rangerKmsComponentIdentity != null) {
            rangerKmscomponentDescriptor.removeIdentity("/smokeuser");
          }
        }
      }
    }
  
    /**
     * Updates YARN's NM 'llap_zk_hive' kerberos descriptor as reference and the associated config
     * hive-interactive-site/hive.llap.zk.sm.keytab.file
     */
    protected void fixYarnHsiKerberosDescriptorAndSiteConfig(Cluster cluster, KerberosDescriptor kerberosDescriptor) {
      LOG.info("Updating YARN's HSI Kerberos Descriptor ....");
  
      // Step 1. Get Hive -> HIVE_SERVER -> 'hive_server_hive' kerberos description for referencing later
      KerberosServiceDescriptor hiveServiceDescriptor = kerberosDescriptor.getService("HIVE");
      KerberosIdentityDescriptor hsh_identityDescriptor = null;
      KerberosPrincipalDescriptor hsh_principalDescriptor = null;
      KerberosKeytabDescriptor hsh_keytabDescriptor = null;
      if (hiveServiceDescriptor != null) {
        KerberosComponentDescriptor hiveServerKerberosDescriptor = hiveServiceDescriptor.getComponent("HIVE_SERVER");
        if (hiveServerKerberosDescriptor != null) {
          hsh_identityDescriptor = hiveServerKerberosDescriptor.getIdentity(HIVE_SERVER_HIVE_KERBEROS_IDENTITY);
          if (hsh_identityDescriptor != null) {
            LOG.info("  Retrieved HIVE->HIVE_SERVER kerberos descriptor. Name = " + hsh_identityDescriptor.getName());
            hsh_principalDescriptor = hsh_identityDescriptor.getPrincipalDescriptor();
            hsh_keytabDescriptor = hsh_identityDescriptor.getKeytabDescriptor();
          }
        }
  
        // Step 2. Update YARN -> NODEMANAGER's : (1). 'llap_zk_hive' and (2). 'llap_task_hive' kerberos descriptor as reference to
        // HIVE -> HIVE_SERVER -> 'hive_server_hive' (Same as YARN -> NODEMANAGER -> 'yarn_nodemanager_hive_server_hive')
        if (hsh_principalDescriptor != null && hsh_keytabDescriptor != null) {
          KerberosServiceDescriptor yarnServiceDescriptor = kerberosDescriptor.getService("YARN");
          if (yarnServiceDescriptor != null) {
            KerberosComponentDescriptor yarnNmKerberosDescriptor = yarnServiceDescriptor.getComponent("NODEMANAGER");
            if (yarnNmKerberosDescriptor != null) {
              String[] identities = {YARN_LLAP_ZK_HIVE_KERBEROS_IDENTITY, YARN_LLAP_TASK_HIVE_KERBEROS_IDENTITY};
              for (String identity : identities) {
                KerberosIdentityDescriptor identityDescriptor = yarnNmKerberosDescriptor.getIdentity(identity);
  
                KerberosPrincipalDescriptor principalDescriptor = null;
                KerberosKeytabDescriptor keytabDescriptor = null;
                if (identityDescriptor != null) {
                  LOG.info("  Retrieved YARN->NODEMANAGER kerberos descriptor to be updated. Name = " + identityDescriptor.getName());
                  principalDescriptor = identityDescriptor.getPrincipalDescriptor();
                  keytabDescriptor = identityDescriptor.getKeytabDescriptor();
  
                  identityDescriptor.setReference(HIVE_SERVER_KERBEROS_PREFIX + hsh_identityDescriptor.getName());
                  LOG.info("    Updated '" + YARN_LLAP_ZK_HIVE_KERBEROS_IDENTITY + "' identity descriptor reference = '"
                          + identityDescriptor.getReference() + "'");
                  principalDescriptor.setValue(null);
                  LOG.info("    Updated '" + YARN_LLAP_ZK_HIVE_KERBEROS_IDENTITY + "' principal descriptor value = '"
                          + principalDescriptor.getValue() + "'");
  
                  // Updating keytabs now
                  keytabDescriptor.setFile(null);
                  LOG.info("    Updated '" + YARN_LLAP_ZK_HIVE_KERBEROS_IDENTITY + "' keytab descriptor file = '"
                          + keytabDescriptor.getFile() + "'");
                  keytabDescriptor.setOwnerName(null);
                  LOG.info("    Updated '" + YARN_LLAP_ZK_HIVE_KERBEROS_IDENTITY + "' keytab descriptor owner name = '" + keytabDescriptor.getOwnerName() + "'");
                  keytabDescriptor.setOwnerAccess(null);
                  LOG.info("    Updated '" + YARN_LLAP_ZK_HIVE_KERBEROS_IDENTITY + "' keytab descriptor owner access = '" + keytabDescriptor.getOwnerAccess() + "'");
                  keytabDescriptor.setGroupName(null);
                  LOG.info("    Updated '" + YARN_LLAP_ZK_HIVE_KERBEROS_IDENTITY + "' keytab descriptor group name = '" + keytabDescriptor.getGroupName() + "'");
                  keytabDescriptor.setGroupAccess(null);
                  LOG.info("    Updated '" + YARN_LLAP_ZK_HIVE_KERBEROS_IDENTITY + "' keytab descriptor group access = '" + keytabDescriptor.getGroupAccess() + "'");
  
                  // Need this as trigger to update the HIVE_LLAP_ZK_SM_KEYTAB_FILE configs later.
  
                  // Get the keytab file 'config name'.
                  String[] splits = keytabDescriptor.getConfiguration().split("/");
                  if (splits.length == 2) {
                    updateYarnKerberosDescUpdatedList(splits[1]);
                    LOG.info("    Updated 'yarnKerberosDescUpdatedList' = " + getYarnKerberosDescUpdatedList());
                  }
                }
              }
            }
          }
        }
      }
    }
  
    public void updateYarnKerberosDescUpdatedList(String val) {
      yarnKerberosDescUpdatedList.add(val);
    }
  
    public List<String> getYarnKerberosDescUpdatedList() {
      return yarnKerberosDescUpdatedList;
    }
  
    protected void updateHiveConfigs() throws AmbariException {
      AmbariManagementController ambariManagementController = injector.getInstance(AmbariManagementController.class);
      Clusters clusters = ambariManagementController.getClusters();
      if (clusters != null) {
        Map<String, Cluster> clusterMap = getCheckedClusterMap(clusters);
        if (clusterMap != null && !clusterMap.isEmpty()) {
          for (final Cluster cluster : clusterMap.values()) {
            // Updating YARN->NodeManager kerebros descriptor : (1). 'llap_zk_hive' and (2). 'llap_task_hive''s associated configs
            // hive-interactive-site/hive.llap.zk.sm.keytab.file and hive-interactive-site/hive.llap.task.keytab.file respectively,
            // based on what hive-interactive-site/hive.llap.daemon.keytab.file has.
            Config hsiSiteConfig = cluster.getDesiredConfigByType(HIVE_INTERACTIVE_SITE);
            if (hsiSiteConfig != null) {
              Map<String, String> hsiSiteConfigProperties = hsiSiteConfig.getProperties();
              if (hsiSiteConfigProperties != null &&
                      hsiSiteConfigProperties.containsKey(HIVE_LLAP_DAEMON_KEYTAB_FILE)) {
                String[] identities = {HIVE_LLAP_ZK_SM_KEYTAB_FILE, HIVE_LLAP_TASK_KEYTAB_FILE};
                Map<String, String> newProperties = new HashMap<>();
                for (String identity : identities) {
                  // Update only if we were able to modify the corresponding kerberos descriptor,
                  // reflected in list 'getYarnKerberosDescUpdatedList'.
                  if (getYarnKerberosDescUpdatedList().contains(identity) && hsiSiteConfigProperties.containsKey(identity)) {
                    newProperties.put(identity, hsiSiteConfigProperties.get(HIVE_LLAP_DAEMON_KEYTAB_FILE));
                  }
                }
  
                // Update step.
                if (newProperties.size() > 0) {
                  try {
                    updateConfigurationPropertiesForCluster(cluster, HIVE_INTERACTIVE_SITE, newProperties, true, false);
                    LOG.info("Updated HSI config(s) : " + newProperties.keySet() + " with value(s) = " + newProperties.values() + " respectively.");
                  } catch (AmbariException e) {
                    e.printStackTrace();
                  }
                }
              }
            }
          }
        }
      }
    }
  
    protected void updateAmsConfigs() throws AmbariException {
      AmbariManagementController ambariManagementController = injector.getInstance(AmbariManagementController.class);
      Clusters clusters = ambariManagementController.getClusters();
      if (clusters != null) {
        Map<String, Cluster> clusterMap = getCheckedClusterMap(clusters);
        if (clusterMap != null && !clusterMap.isEmpty()) {
          for (final Cluster cluster : clusterMap.values()) {
  
  
            Config amsSslClient = cluster.getDesiredConfigByType(AMS_SSL_CLIENT);
            if (amsSslClient != null) {
              Map<String, String> amsSslClientProperties = amsSslClient.getProperties();
  
              if (amsSslClientProperties.containsKey(METRIC_TRUSTSTORE_ALIAS)) {
                LOG.info("Removing " + METRIC_TRUSTSTORE_ALIAS + " from " + AMS_SSL_CLIENT);
                removeConfigurationPropertiesFromCluster(cluster, AMS_SSL_CLIENT, Collections.singleton(METRIC_TRUSTSTORE_ALIAS));
              }
  
            }
          }
        }
      }
    }
  
    protected void updateHDFSWidgetDefinition() throws AmbariException {
      LOG.info("Updating HDFS widget definition.");
  
      Map<String, List<String>> widgetMap = new HashMap<>();
      Map<String, String> sectionLayoutMap = new HashMap<>();
  
      List<String> hdfsHeatmapWidgets = new ArrayList<>(Arrays.asList("HDFS Bytes Read", "HDFS Bytes Written",
        "DataNode Process Disk I/O Utilization", "DataNode Process Network I/O Utilization"));
      widgetMap.put("HDFS_HEATMAPS", hdfsHeatmapWidgets);
      sectionLayoutMap.put("HDFS_HEATMAPS", "default_hdfs_heatmap");
  
      updateWidgetDefinitionsForService("HDFS", widgetMap, sectionLayoutMap);
    }
  
    /**
     * Retrieves the relevant {@link Cluster} given information from the suppliied {@link ArtifactEntity}.
     * <p>
     * The cluster id value is taken from the entity's foreign key value and then used to obtain the cluster object.
     *
     * @param artifactEntity an {@link ArtifactEntity}
     * @return a {@link Cluster}
     */
    private Cluster getCluster(ArtifactEntity artifactEntity) {
      if (artifactEntity != null) {
        Map<String, String> keys = artifactEntity.getForeignKeys();
        if (keys != null) {
          String clusterId = keys.get("cluster");
          if (StringUtils.isNumeric(clusterId)) {
            Clusters clusters = injector.getInstance(Clusters.class);
            try {
              return clusters.getCluster(Long.valueOf(clusterId));
            } catch (AmbariException e) {
              LOG.error(String.format("Failed to obtain cluster using cluster id %s -  %s", clusterId, e.getMessage()), e);
            }
          } else {
            LOG.error(String.format("Failed to obtain cluster id from artifact entity with foreign keys: %s", keys));
          }
        }
      }
  
      return null;
    }
  
    /**
     * Recursively traverses the Kerberos descriptor to find and fix the identity references.
     * <p>
     * Each found identity descriptor that indicates it is a reference by having a <code>name</code>
     * value that starts with a "/" or a "./" is fixed by clearing the <code>principal name</code>value,
     * setting the <code>reference</code> value to the <code>name</code> value and changing the
     * <code>name</code> value to a name with the following pattern:
     * <code>SERVICE_COMPONENT_IDENTITY</code>
     * <p>
     * For example, if the identity is for the "SERVICE1" service and is a reference to "HDFS/NAMENODE/hdfs";
     * then the name is set to "<code>service1_hdfs</code>"
     * <p>
     * For example, if the identity is for the "COMPONENT21" component of the "SERVICE2" service and is a reference to "HDFS/NAMENODE/hdfs";
     * then the name is set to "<code>service2_component21_hdfs</code>"
     * <p>
     * Once the identity descriptor properties of the identity are fixed, the relevant configuration
     * value is fixed to match the value if the referenced identity. This may lead to a new version
     * of the relevant configuration type.
     *
     * @param cluster   the cluster
     * @param container the current Kerberos descriptor container
     * @throws AmbariException if an error occurs
     */
    private void fixIdentityReferences(Cluster cluster, AbstractKerberosDescriptorContainer container)
        throws AmbariException {
      List<KerberosIdentityDescriptor> identities = container.getIdentities();
      if (identities != null) {
        for (KerberosIdentityDescriptor identity : identities) {
          String name = identity.getName();
  
          if (!StringUtils.isEmpty(name) && (name.startsWith("/") || name.startsWith("./"))) {
            String[] parts = name.split("/");
            String newName = buildName(identity.getParent(), parts[parts.length - 1]);
  
            identity.setName(newName);
            identity.setReference(name);
          }
  
          String identityReference = identity.getReference();
          if (!StringUtils.isEmpty(identityReference)) {
            // If this identity references another identity:
            //  * The principal name needs to be the same as the referenced identity
            //    - ensure that no principal name is being set for this identity
            //  * Any configuration set to contain the reference principal name needs to be fixed to
            //    be the correct principal name
            KerberosPrincipalDescriptor principal = identity.getPrincipalDescriptor();
            if (principal != null) {
              // Fix the value
              principal.setValue(null);
  
              // Fix the relative configuration
              if (!StringUtils.isEmpty(principal.getConfiguration())) {
                String referencedPrincipalName = getConfiguredPrincipalNameFromReference(cluster, container, identityReference);
  
                if(!StringUtils.isEmpty(referencedPrincipalName)) {
                  String[] parts = principal.getConfiguration().split("/");
                  if (parts.length == 2) {
                    String type = parts[0];
                    String property = parts[1];
  
                    updateConfigurationPropertiesForCluster(cluster,
                        type,
                        Collections.singletonMap(property, referencedPrincipalName),
                        true,
                        false);
                  }
                }
              }
            }
          }
        }
      }
  
      if (container instanceof KerberosDescriptor) {
        Map<String, KerberosServiceDescriptor> services = ((KerberosDescriptor) container).getServices();
        if (services != null) {
          for (KerberosServiceDescriptor serviceDescriptor : services.values()) {
            fixIdentityReferences(cluster, serviceDescriptor);
          }
        }
      } else if (container instanceof KerberosServiceDescriptor) {
        Map<String, KerberosComponentDescriptor> components = ((KerberosServiceDescriptor) container).getComponents();
        if (components != null) {
          for (KerberosComponentDescriptor componentDescriptor : components.values()) {
            fixIdentityReferences(cluster, componentDescriptor);
          }
        }
      }
    }
  
    /**
     * Finds the value of the configuration found for the principal in the referenced identity
     * descriptor.
     *
     * @param cluster           the cluster
     * @param container         the current {@link KerberosIdentityDescriptor}, ideally the identity's parent descriptor
     * @param identityReference the path to the referenced identity
     * @return the value of the configuration specified in the referenced identity's principal descriptor
     * @throws AmbariException if an error occurs
     */
    private String getConfiguredPrincipalNameFromReference(Cluster cluster,
                                                           AbstractKerberosDescriptorContainer container,
                                                           String identityReference)
        throws AmbariException {
      KerberosIdentityDescriptor identityDescriptor = container.getReferencedIdentityDescriptor(identityReference);
  
      if (identityDescriptor != null) {
        KerberosPrincipalDescriptor principal = identityDescriptor.getPrincipalDescriptor();
        if ((principal != null) && (!StringUtils.isEmpty(principal.getConfiguration()))) {
          String[] parts = principal.getConfiguration().split("/");
          if (parts.length == 2) {
            String type = parts[0];
            String property = parts[1];
  
            Config config = cluster.getDesiredConfigByType(type);
  
            if (config != null) {
              return config.getProperties().get(property);
            }
          }
        }
      }
  
      return null;
    }
  
    /**
     * Builds the name of an identity based on the identity's container and the referenced identity's name.
     * <p>
     * The calculated name will be in the following format and converted to all lowercase characters:
     * <code>SERVICE_COMPONENT_IDENTITY</code>
     *
     * @param container    the current {@link KerberosIdentityDescriptor}, ideally the identity's parent descriptor
     * @param identityName the referenced identity's name
     * @return a name
     */
    private String buildName(AbstractKerberosDescriptor container, String identityName) {
      if (container instanceof KerberosServiceDescriptor) {
        return container.getName().toLowerCase() + "_" + identityName;
      } else if (container instanceof KerberosComponentDescriptor) {
        return container.getParent().getName().toLowerCase() + "_" + container.getName().toLowerCase() + "_" + identityName;
      } else {
        return identityName;
      }
    }
  
    /**
     * Sets all existing repository versions to be resolved (we have to assume
     * that they are good since they've been using them to run stuff).
     *
     * @throws AmbariException
     */
    protected void updateExistingRepositoriesToBeResolved() throws AmbariException {
      RepositoryVersionDAO repositoryVersionDAO = injector.getInstance(RepositoryVersionDAO.class);
      List<RepositoryVersionEntity> repositoryVersions = repositoryVersionDAO.findAll();
      for (RepositoryVersionEntity repositoryVersion : repositoryVersions) {
        repositoryVersion.setResolved(true);
        repositoryVersionDAO.merge(repositoryVersion);
      }
    }
  }
  