/* * Licensed to Elasticsearch under one or more contributor * license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright * ownership. Elasticsearch 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.elasticsearch.cluster.metadata; import com.carrotsearch.hppc.cursors.ObjectObjectCursor; import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.admin.indices.close.CloseIndexClusterStateUpdateRequest; import org.elasticsearch.action.admin.indices.open.OpenIndexClusterStateUpdateRequest; import org.elasticsearch.cluster.AckedClusterStateUpdateTask; import org.elasticsearch.cluster.ClusterService; import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.RestoreInProgress; import org.elasticsearch.cluster.ack.ClusterStateUpdateResponse; import org.elasticsearch.cluster.block.ClusterBlock; import org.elasticsearch.cluster.block.ClusterBlockLevel; import org.elasticsearch.cluster.block.ClusterBlocks; import org.elasticsearch.cluster.routing.IndexRoutingTable; import org.elasticsearch.cluster.routing.IndexShardRoutingTable; import org.elasticsearch.cluster.routing.RoutingTable; import org.elasticsearch.cluster.routing.ShardRouting; import org.elasticsearch.cluster.routing.allocation.AllocationService; import org.elasticsearch.cluster.routing.allocation.RoutingAllocation; import org.elasticsearch.common.Priority; import org.elasticsearch.common.component.AbstractComponent; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.index.Index; import org.elasticsearch.index.IndexNotFoundException; import org.elasticsearch.indices.IndexPrimaryShardNotAllocatedException; import org.elasticsearch.index.shard.ShardId; import org.elasticsearch.rest.RestStatus; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; /** * Service responsible for submitting open/close index requests */ public class MetaDataIndexStateService extends AbstractComponent { public static final ClusterBlock INDEX_CLOSED_BLOCK = new ClusterBlock(4, "index closed", false, false, RestStatus.FORBIDDEN, ClusterBlockLevel.READ_WRITE); private final ClusterService clusterService; private final AllocationService allocationService; private final MetaDataIndexUpgradeService metaDataIndexUpgradeService; @Inject public MetaDataIndexStateService(Settings settings, ClusterService clusterService, AllocationService allocationService, MetaDataIndexUpgradeService metaDataIndexUpgradeService) { super(settings); this.clusterService = clusterService; this.allocationService = allocationService; this.metaDataIndexUpgradeService = metaDataIndexUpgradeService; } public void closeIndex(final CloseIndexClusterStateUpdateRequest request, final ActionListener<ClusterStateUpdateResponse> listener) { if (request.indices() == null || request.indices().length == 0) { throw new IllegalArgumentException("Index name is required"); } final String indicesAsString = Arrays.toString(request.indices()); clusterService.submitStateUpdateTask("close-indices " + indicesAsString, new AckedClusterStateUpdateTask<ClusterStateUpdateResponse>(Priority.URGENT, request, listener) { @Override protected ClusterStateUpdateResponse newResponse(boolean acknowledged) { return new ClusterStateUpdateResponse(acknowledged); } @Override public boolean doPresistMetaData() { return true; } @Override public ClusterState execute(ClusterState currentState) { Set<String> indicesToClose = new HashSet<>(); for (String index : request.indices()) { IndexMetaData indexMetaData = currentState.metaData().index(index); if (indexMetaData == null) { throw new IndexNotFoundException(index); } if (indexMetaData.getState() != IndexMetaData.State.CLOSE) { IndexRoutingTable indexRoutingTable = currentState.routingTable().index(index); if (indexRoutingTable != null) for (IndexShardRoutingTable shard : indexRoutingTable) { for (ShardRouting shardRouting : shard) { if (shardRouting.primary() == true && shardRouting.allocatedPostIndexCreate() == false) { throw new IndexPrimaryShardNotAllocatedException(new Index(index)); } } } indicesToClose.add(index); } } if (indicesToClose.isEmpty()) { return currentState; } // Check if any of the indices to be closed are currently being restored from a snapshot and fail closing if such an index // is found as closing an index that is being restored makes the index unusable (it cannot be recovered). RestoreInProgress restore = currentState.custom(RestoreInProgress.TYPE); if (restore != null) { Set<String> indicesToFail = null; for (RestoreInProgress.Entry entry : restore.entries()) { for (Map.Entry<ShardId, RestoreInProgress.ShardRestoreStatus> shard : entry.shards().entrySet()) { if (!shard.getValue().state().completed()) { if (indicesToClose.contains(shard.getKey().getIndex())) { if (indicesToFail == null) { indicesToFail = new HashSet<>(); } indicesToFail.add(shard.getKey().getIndex()); } } } } if (indicesToFail != null) { throw new IllegalArgumentException("Cannot close indices that are being restored: " + indicesToFail); } } logger.info("closing indices [{}]", indicesAsString); MetaData.Builder mdBuilder = MetaData.builder(currentState.metaData()); ClusterBlocks.Builder blocksBuilder = ClusterBlocks.builder() .blocks(currentState.blocks()); for (String index : indicesToClose) { mdBuilder.put(IndexMetaData.builder(currentState.metaData().index(index)).state(IndexMetaData.State.CLOSE)); blocksBuilder.addIndexBlock(index, INDEX_CLOSED_BLOCK); } ClusterState updatedState = ClusterState.builder(currentState).metaData(mdBuilder).blocks(blocksBuilder).build(); RoutingTable routingTable = RoutingTable.build(MetaDataIndexStateService.this.clusterService, updatedState); /* RoutingTable.Builder rtBuilder = RoutingTable.builder(currentState.routingTable()); for (String index : indicesToClose) { rtBuilder.remove(index); } RoutingAllocation.Result routingResult = allocationService.reroute( ClusterState.builder(updatedState).routingTable(rtBuilder.build()).build(), "indices closed [" + indicesAsString + "]"); */ //no explicit wait for other nodes needed as we use AckedClusterStateUpdateTask return ClusterState.builder(updatedState).incrementVersion().routingTable(routingTable).build(); } }); } public void openIndex(final OpenIndexClusterStateUpdateRequest request, final ActionListener<ClusterStateUpdateResponse> listener) { if (request.indices() == null || request.indices().length == 0) { throw new IllegalArgumentException("Index name is required"); } final String indicesAsString = Arrays.toString(request.indices()); clusterService.submitStateUpdateTask("open-indices " + indicesAsString, new AckedClusterStateUpdateTask<ClusterStateUpdateResponse>(Priority.URGENT, request, listener) { @Override protected ClusterStateUpdateResponse newResponse(boolean acknowledged) { return new ClusterStateUpdateResponse(acknowledged); } @Override public boolean doPresistMetaData() { return true; } @Override public ClusterState execute(ClusterState currentState) { List<String> indicesToOpen = new ArrayList<>(); for (String index : request.indices()) { IndexMetaData indexMetaData = currentState.metaData().index(index); if (indexMetaData == null) { throw new IndexNotFoundException(index); } if (indexMetaData.getState() != IndexMetaData.State.OPEN) { indicesToOpen.add(index); } } if (indicesToOpen.isEmpty()) { return currentState; } logger.info("opening indices [{}]", indicesAsString); MetaData.Builder mdBuilder = MetaData.builder(currentState.metaData()); ClusterBlocks.Builder blocksBuilder = ClusterBlocks.builder() .blocks(currentState.blocks()); for (String index : indicesToOpen) { IndexMetaData indexMetaData = IndexMetaData.builder(currentState.metaData().index(index)).state(IndexMetaData.State.OPEN).build(); // The index might be closed because we couldn't import it due to old incompatible version // We need to check that this index can be upgraded to the current version indexMetaData = metaDataIndexUpgradeService.upgradeIndexMetaData(indexMetaData); mdBuilder.put(indexMetaData, true); blocksBuilder.removeIndexBlock(index, INDEX_CLOSED_BLOCK); } ClusterState updatedState = ClusterState.builder(currentState).metaData(mdBuilder).blocks(blocksBuilder).build(); /* RoutingTable.Builder rtBuilder = RoutingTable.builder(updatedState.routingTable()); for (String index : indicesToOpen) { rtBuilder.addAsFromCloseToOpen(updatedState.metaData().index(index)); } RoutingAllocation.Result routingResult = allocationService.reroute( ClusterState.builder(updatedState).routingTable(rtBuilder.build()).build(), "indices opened [" + indicesAsString + "]"); */ //no explicit wait for other nodes needed as we use AckedClusterStateUpdateTask return ClusterState.builder(updatedState).incrementVersion().build(); } }); } }