001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.hbase.master.procedure;
019
020import com.google.errorprone.annotations.RestrictedApi;
021import java.io.IOException;
022import java.util.ArrayList;
023import java.util.HashMap;
024import java.util.Iterator;
025import java.util.List;
026import java.util.Map;
027import org.apache.hadoop.conf.Configuration;
028import org.apache.hadoop.fs.FileSystem;
029import org.apache.hadoop.fs.Path;
030import org.apache.hadoop.hbase.DoNotRetryIOException;
031import org.apache.hadoop.hbase.HBaseIOException;
032import org.apache.hadoop.hbase.MetaTableAccessor;
033import org.apache.hadoop.hbase.TableName;
034import org.apache.hadoop.hbase.TableNotFoundException;
035import org.apache.hadoop.hbase.client.Connection;
036import org.apache.hadoop.hbase.client.RegionInfo;
037import org.apache.hadoop.hbase.client.RegionReplicaUtil;
038import org.apache.hadoop.hbase.client.TableDescriptor;
039import org.apache.hadoop.hbase.errorhandling.ForeignException;
040import org.apache.hadoop.hbase.errorhandling.ForeignExceptionDispatcher;
041import org.apache.hadoop.hbase.favored.FavoredNodesManager;
042import org.apache.hadoop.hbase.fs.ErasureCodingUtils;
043import org.apache.hadoop.hbase.master.MasterFileSystem;
044import org.apache.hadoop.hbase.master.MetricsSnapshot;
045import org.apache.hadoop.hbase.master.RegionState;
046import org.apache.hadoop.hbase.master.assignment.AssignmentManager;
047import org.apache.hadoop.hbase.master.assignment.RegionStateStore;
048import org.apache.hadoop.hbase.monitoring.MonitoredTask;
049import org.apache.hadoop.hbase.monitoring.TaskMonitor;
050import org.apache.hadoop.hbase.procedure2.ProcedureStateSerializer;
051import org.apache.hadoop.hbase.snapshot.ClientSnapshotDescriptionUtils;
052import org.apache.hadoop.hbase.snapshot.RestoreSnapshotHelper;
053import org.apache.hadoop.hbase.snapshot.SnapshotDescriptionUtils;
054import org.apache.hadoop.hbase.snapshot.SnapshotManifest;
055import org.apache.hadoop.hbase.snapshot.SnapshotTTLExpiredException;
056import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
057import org.apache.hadoop.hbase.util.Pair;
058import org.apache.yetus.audience.InterfaceAudience;
059import org.slf4j.Logger;
060import org.slf4j.LoggerFactory;
061
062import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil;
063import org.apache.hadoop.hbase.shaded.protobuf.generated.HBaseProtos;
064import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProcedureProtos.RestoreParentToChildRegionsPair;
065import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProcedureProtos.RestoreSnapshotState;
066import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProcedureProtos.RestoreSnapshotStateData;
067import org.apache.hadoop.hbase.shaded.protobuf.generated.SnapshotProtos.SnapshotDescription;
068
069@InterfaceAudience.Private
070public class RestoreSnapshotProcedure
071  extends AbstractStateMachineTableProcedure<RestoreSnapshotState> {
072  private static final Logger LOG = LoggerFactory.getLogger(RestoreSnapshotProcedure.class);
073
074  private TableDescriptor oldTableDescriptor;
075  private TableDescriptor modifiedTableDescriptor;
076  private List<RegionInfo> regionsToRestore = null;
077  private List<RegionInfo> regionsToRemove = null;
078  private List<RegionInfo> regionsToAdd = null;
079  private Map<String, Pair<String, String>> parentsToChildrenPairMap = new HashMap<>();
080
081  private SnapshotDescription snapshot;
082  private boolean restoreAcl;
083
084  // Monitor
085  private MonitoredTask monitorStatus = null;
086
087  /**
088   * Constructor (for failover)
089   */
090  public RestoreSnapshotProcedure() {
091  }
092
093  public RestoreSnapshotProcedure(final MasterProcedureEnv env,
094    final TableDescriptor tableDescriptor, final SnapshotDescription snapshot)
095    throws HBaseIOException {
096    this(env, tableDescriptor, snapshot, false);
097  }
098
099  public RestoreSnapshotProcedure(final MasterProcedureEnv env,
100    final TableDescriptor tableDescriptor, final SnapshotDescription snapshot,
101    final boolean restoreAcl) throws HBaseIOException {
102    this(env, tableDescriptor, tableDescriptor, snapshot, restoreAcl);
103  }
104
105  /**
106   * Constructor
107   * @param env                     MasterProcedureEnv
108   * @param modifiedTableDescriptor the table to operate on
109   * @param snapshot                snapshot to restore from
110   */
111  public RestoreSnapshotProcedure(final MasterProcedureEnv env,
112    final TableDescriptor oldTableDescriptor, final TableDescriptor modifiedTableDescriptor,
113    final SnapshotDescription snapshot, final boolean restoreAcl) throws HBaseIOException {
114    super(env);
115    this.oldTableDescriptor = oldTableDescriptor;
116    // This is the new schema we are going to write out as this modification.
117    this.modifiedTableDescriptor = modifiedTableDescriptor;
118    preflightChecks(env, null/* Table can be online when restore is called? */);
119    // Snapshot information
120    this.snapshot = snapshot;
121    this.restoreAcl = restoreAcl;
122
123    // Monitor
124    getMonitorStatus();
125  }
126
127  /**
128   * Set up monitor status if it is not created.
129   */
130  private MonitoredTask getMonitorStatus() {
131    if (monitorStatus == null) {
132      monitorStatus = TaskMonitor.get().createStatus(
133        "Restoring  snapshot '" + snapshot.getName() + "' to table " + getTableName());
134    }
135    return monitorStatus;
136  }
137
138  @Override
139  protected Flow executeFromState(final MasterProcedureEnv env, final RestoreSnapshotState state)
140    throws InterruptedException {
141    LOG.trace("{} execute state={}", this, state);
142
143    // Make sure that the monitor status is set up
144    getMonitorStatus();
145
146    try {
147      switch (state) {
148        case RESTORE_SNAPSHOT_PRE_OPERATION:
149          // Verify if we can restore the table
150          prepareRestore(env);
151          setNextState(RestoreSnapshotState.RESTORE_SNAPSHOT_UPDATE_TABLE_DESCRIPTOR);
152          break;
153        case RESTORE_SNAPSHOT_UPDATE_TABLE_DESCRIPTOR:
154          updateTableDescriptor(env);
155          // for restore, table dir already exists. sync EC if necessary before doing the real
156          // restore. this may be useful in certain restore scenarios where a user is explicitly
157          // trying to disable EC for some reason as part of the restore.
158          if (ErasureCodingUtils.needsSync(oldTableDescriptor, modifiedTableDescriptor)) {
159            setNextState(RestoreSnapshotState.RESTORE_SNAPSHOT_SYNC_ERASURE_CODING_POLICY);
160          } else {
161            setNextState(RestoreSnapshotState.RESTORE_SNAPSHOT_WRITE_FS_LAYOUT);
162          }
163          break;
164        case RESTORE_SNAPSHOT_SYNC_ERASURE_CODING_POLICY:
165          ErasureCodingUtils.sync(env.getMasterFileSystem().getFileSystem(),
166            env.getMasterFileSystem().getRootDir(), modifiedTableDescriptor);
167          setNextState(RestoreSnapshotState.RESTORE_SNAPSHOT_WRITE_FS_LAYOUT);
168          break;
169        case RESTORE_SNAPSHOT_WRITE_FS_LAYOUT:
170          restoreSnapshot(env);
171          setNextState(RestoreSnapshotState.RESTORE_SNAPSHOT_UPDATE_META);
172          break;
173        case RESTORE_SNAPSHOT_UPDATE_META:
174          updateMETA(env);
175          setNextState(RestoreSnapshotState.RESTORE_SNAPSHOT_RESTORE_ACL);
176          break;
177        case RESTORE_SNAPSHOT_RESTORE_ACL:
178          restoreSnapshotAcl(env);
179          return Flow.NO_MORE_STATE;
180        default:
181          throw new UnsupportedOperationException("unhandled state=" + state);
182      }
183    } catch (IOException e) {
184      if (isRollbackSupported(state)) {
185        setFailure("master-restore-snapshot", e);
186      } else {
187        LOG.warn("Retriable error trying to restore snapshot=" + snapshot.getName() + " to table="
188          + getTableName() + " (in state=" + state + ")", e);
189      }
190    }
191    return Flow.HAS_MORE_STATE;
192  }
193
194  @Override
195  protected void rollbackState(final MasterProcedureEnv env, final RestoreSnapshotState state)
196    throws IOException {
197    if (state == RestoreSnapshotState.RESTORE_SNAPSHOT_PRE_OPERATION) {
198      // nothing to rollback
199      return;
200    }
201
202    // The restore snapshot doesn't have a rollback. The execution will succeed, at some point.
203    throw new UnsupportedOperationException("unhandled state=" + state);
204  }
205
206  @Override
207  protected boolean isRollbackSupported(final RestoreSnapshotState state) {
208    switch (state) {
209      case RESTORE_SNAPSHOT_PRE_OPERATION:
210        return true;
211      default:
212        return false;
213    }
214  }
215
216  @Override
217  protected RestoreSnapshotState getState(final int stateId) {
218    return RestoreSnapshotState.valueOf(stateId);
219  }
220
221  @Override
222  protected int getStateId(final RestoreSnapshotState state) {
223    return state.getNumber();
224  }
225
226  @Override
227  protected RestoreSnapshotState getInitialState() {
228    return RestoreSnapshotState.RESTORE_SNAPSHOT_PRE_OPERATION;
229  }
230
231  @Override
232  public TableName getTableName() {
233    return modifiedTableDescriptor.getTableName();
234  }
235
236  @Override
237  public TableOperationType getTableOperationType() {
238    return TableOperationType.EDIT; // Restore is modifying a table
239  }
240
241  @Override
242  public boolean abort(final MasterProcedureEnv env) {
243    // TODO: We may be able to abort if the procedure is not started yet.
244    return false;
245  }
246
247  @Override
248  public void toStringClassDetails(StringBuilder sb) {
249    sb.append(getClass().getSimpleName());
250    sb.append(" (table=");
251    sb.append(getTableName());
252    sb.append(" snapshot=");
253    sb.append(snapshot);
254    sb.append(")");
255  }
256
257  @Override
258  protected void serializeStateData(ProcedureStateSerializer serializer) throws IOException {
259    super.serializeStateData(serializer);
260
261    RestoreSnapshotStateData.Builder restoreSnapshotMsg = RestoreSnapshotStateData.newBuilder()
262      .setUserInfo(MasterProcedureUtil.toProtoUserInfo(getUser())).setSnapshot(this.snapshot)
263      .setModifiedTableSchema(ProtobufUtil.toTableSchema(modifiedTableDescriptor))
264      .setOldTableSchema(ProtobufUtil.toTableSchema(oldTableDescriptor));
265
266    if (regionsToRestore != null) {
267      for (RegionInfo hri : regionsToRestore) {
268        restoreSnapshotMsg.addRegionInfoForRestore(ProtobufUtil.toRegionInfo(hri));
269      }
270    }
271    if (regionsToRemove != null) {
272      for (RegionInfo hri : regionsToRemove) {
273        restoreSnapshotMsg.addRegionInfoForRemove(ProtobufUtil.toRegionInfo(hri));
274      }
275    }
276    if (regionsToAdd != null) {
277      for (RegionInfo hri : regionsToAdd) {
278        restoreSnapshotMsg.addRegionInfoForAdd(ProtobufUtil.toRegionInfo(hri));
279      }
280    }
281    if (!parentsToChildrenPairMap.isEmpty()) {
282      final Iterator<Map.Entry<String, Pair<String, String>>> it =
283        parentsToChildrenPairMap.entrySet().iterator();
284      while (it.hasNext()) {
285        final Map.Entry<String, Pair<String, String>> entry = it.next();
286
287        RestoreParentToChildRegionsPair.Builder parentToChildrenPair =
288          RestoreParentToChildRegionsPair.newBuilder().setParentRegionName(entry.getKey())
289            .setChild1RegionName(entry.getValue().getFirst())
290            .setChild2RegionName(entry.getValue().getSecond());
291        restoreSnapshotMsg.addParentToChildRegionsPairList(parentToChildrenPair);
292      }
293    }
294    restoreSnapshotMsg.setRestoreAcl(restoreAcl);
295    serializer.serialize(restoreSnapshotMsg.build());
296  }
297
298  @Override
299  protected void deserializeStateData(ProcedureStateSerializer serializer) throws IOException {
300    super.deserializeStateData(serializer);
301
302    RestoreSnapshotStateData restoreSnapshotMsg =
303      serializer.deserialize(RestoreSnapshotStateData.class);
304    setUser(MasterProcedureUtil.toUserInfo(restoreSnapshotMsg.getUserInfo()));
305    snapshot = restoreSnapshotMsg.getSnapshot();
306    oldTableDescriptor = ProtobufUtil.toTableDescriptor(restoreSnapshotMsg.getOldTableSchema());
307    modifiedTableDescriptor =
308      ProtobufUtil.toTableDescriptor(restoreSnapshotMsg.getModifiedTableSchema());
309
310    if (restoreSnapshotMsg.getRegionInfoForRestoreCount() == 0) {
311      regionsToRestore = null;
312    } else {
313      regionsToRestore = new ArrayList<>(restoreSnapshotMsg.getRegionInfoForRestoreCount());
314      for (HBaseProtos.RegionInfo hri : restoreSnapshotMsg.getRegionInfoForRestoreList()) {
315        regionsToRestore.add(ProtobufUtil.toRegionInfo(hri));
316      }
317    }
318    if (restoreSnapshotMsg.getRegionInfoForRemoveCount() == 0) {
319      regionsToRemove = null;
320    } else {
321      regionsToRemove = new ArrayList<>(restoreSnapshotMsg.getRegionInfoForRemoveCount());
322      for (HBaseProtos.RegionInfo hri : restoreSnapshotMsg.getRegionInfoForRemoveList()) {
323        regionsToRemove.add(ProtobufUtil.toRegionInfo(hri));
324      }
325    }
326    if (restoreSnapshotMsg.getRegionInfoForAddCount() == 0) {
327      regionsToAdd = null;
328    } else {
329      regionsToAdd = new ArrayList<>(restoreSnapshotMsg.getRegionInfoForAddCount());
330      for (HBaseProtos.RegionInfo hri : restoreSnapshotMsg.getRegionInfoForAddList()) {
331        regionsToAdd.add(ProtobufUtil.toRegionInfo(hri));
332      }
333    }
334    if (restoreSnapshotMsg.getParentToChildRegionsPairListCount() > 0) {
335      for (RestoreParentToChildRegionsPair parentToChildrenPair : restoreSnapshotMsg
336        .getParentToChildRegionsPairListList()) {
337        parentsToChildrenPairMap.put(parentToChildrenPair.getParentRegionName(), new Pair<>(
338          parentToChildrenPair.getChild1RegionName(), parentToChildrenPair.getChild2RegionName()));
339      }
340    }
341    if (restoreSnapshotMsg.hasRestoreAcl()) {
342      restoreAcl = restoreSnapshotMsg.getRestoreAcl();
343    }
344  }
345
346  /**
347   * Action before any real action of restoring from snapshot.
348   * @param env MasterProcedureEnv
349   */
350  private void prepareRestore(final MasterProcedureEnv env) throws IOException {
351    final TableName tableName = getTableName();
352    // Checks whether the table exists
353    if (!env.getMasterServices().getTableDescriptors().exists(tableName)) {
354      throw new TableNotFoundException(tableName);
355    }
356
357    // check whether ttl has expired for this snapshot
358    if (
359      SnapshotDescriptionUtils.isExpiredSnapshot(snapshot.getTtl(), snapshot.getCreationTime(),
360        EnvironmentEdgeManager.currentTime())
361    ) {
362      throw new SnapshotTTLExpiredException(ProtobufUtil.createSnapshotDesc(snapshot));
363    }
364
365    // Check whether table is disabled.
366    env.getMasterServices().checkTableModifiable(tableName);
367
368    // Check that we have at least 1 CF
369    if (modifiedTableDescriptor.getColumnFamilyCount() == 0) {
370      throw new DoNotRetryIOException(
371        "Table " + getTableName().toString() + " should have at least one column family.");
372    }
373
374    if (!getTableName().isSystemTable()) {
375      // Table already exist. Check and update the region quota for this table namespace.
376      final MasterFileSystem mfs = env.getMasterServices().getMasterFileSystem();
377      SnapshotManifest manifest =
378        SnapshotManifest.open(env.getMasterConfiguration(), mfs.getFileSystem(),
379          SnapshotDescriptionUtils.getCompletedSnapshotDir(snapshot, mfs.getRootDir()), snapshot);
380      int snapshotRegionCount = manifest.getRegionManifestsMap().size();
381      int tableRegionCount =
382        ProcedureSyncWait.getMasterQuotaManager(env).getRegionCountOfTable(tableName);
383
384      if (snapshotRegionCount > 0 && tableRegionCount != snapshotRegionCount) {
385        ProcedureSyncWait.getMasterQuotaManager(env).checkAndUpdateNamespaceRegionQuota(tableName,
386          snapshotRegionCount);
387      }
388    }
389  }
390
391  /**
392   * Update descriptor
393   * @param env MasterProcedureEnv
394   **/
395  private void updateTableDescriptor(final MasterProcedureEnv env) throws IOException {
396    env.getMasterServices().getTableDescriptors().update(modifiedTableDescriptor);
397  }
398
399  /**
400   * Execute the on-disk Restore
401   * @param env MasterProcedureEnv
402   **/
403  private void restoreSnapshot(final MasterProcedureEnv env) throws IOException {
404    MasterFileSystem fileSystemManager = env.getMasterServices().getMasterFileSystem();
405    FileSystem fs = fileSystemManager.getFileSystem();
406    Path rootDir = fileSystemManager.getRootDir();
407    final ForeignExceptionDispatcher monitorException = new ForeignExceptionDispatcher();
408    final Configuration conf = new Configuration(env.getMasterConfiguration());
409
410    LOG.info("Starting restore snapshot=" + ClientSnapshotDescriptionUtils.toString(snapshot));
411    try {
412      Path snapshotDir = SnapshotDescriptionUtils.getCompletedSnapshotDir(snapshot, rootDir);
413      SnapshotManifest manifest = SnapshotManifest.open(conf, fs, snapshotDir, snapshot);
414      RestoreSnapshotHelper restoreHelper = new RestoreSnapshotHelper(conf, fs, manifest,
415        modifiedTableDescriptor, rootDir, monitorException, getMonitorStatus());
416
417      RestoreSnapshotHelper.RestoreMetaChanges metaChanges = restoreHelper.restoreHdfsRegions();
418      regionsToRestore = metaChanges.getRegionsToRestore();
419      regionsToRemove = metaChanges.getRegionsToRemove();
420      regionsToAdd = metaChanges.getRegionsToAdd();
421      parentsToChildrenPairMap = metaChanges.getParentToChildrenPairMap();
422    } catch (IOException e) {
423      String msg = "restore snapshot=" + ClientSnapshotDescriptionUtils.toString(snapshot)
424        + " failed in on-disk restore. Try re-running the restore command.";
425      LOG.error(msg, e);
426      monitorException
427        .receive(new ForeignException(env.getMasterServices().getServerName().toString(), e));
428      throw new IOException(msg, e);
429    }
430  }
431
432  /**
433   * Apply changes to hbase:meta
434   **/
435  private void updateMETA(final MasterProcedureEnv env) throws IOException {
436    try {
437      Connection conn = env.getMasterServices().getConnection();
438      RegionStateStore regionStateStore = env.getAssignmentManager().getRegionStateStore();
439      int regionReplication = modifiedTableDescriptor.getRegionReplication();
440
441      // 1. Prepare to restore
442      getMonitorStatus().setStatus("Preparing to restore each region");
443
444      // 2. Applies changes to hbase:meta and in-memory states
445      // (2.1). Removes the current set of regions from META and in-memory states
446      //
447      // By removing also the regions to restore (the ones present both in the snapshot
448      // and in the current state) we ensure that no extra fields are present in META
449      // e.g. with a simple add addRegionToMeta() the splitA and splitB attributes
450      // not overwritten/removed, so you end up with old informations
451      // that are not correct after the restore.
452      if (regionsToRemove != null) {
453        regionStateStore.deleteRegions(regionsToRemove);
454        deleteRegionsFromInMemoryStates(regionsToRemove, env, regionReplication);
455      }
456
457      // (2.2). Add the new set of regions to META and in-memory states
458      //
459      // At this point the old regions are no longer present in META.
460      // and the set of regions present in the snapshot will be written to META.
461      // All the information in hbase:meta are coming from the .regioninfo of each region present
462      // in the snapshot folder.
463      if (regionsToAdd != null) {
464        MetaTableAccessor.addRegionsToMeta(conn, regionsToAdd, regionReplication);
465        addRegionsToInMemoryStates(regionsToAdd, env, regionReplication);
466      }
467
468      if (regionsToRestore != null) {
469        regionStateStore.overwriteRegions(regionsToRestore, regionReplication);
470
471        deleteRegionsFromInMemoryStates(regionsToRestore, env, regionReplication);
472        addRegionsToInMemoryStates(regionsToRestore, env, regionReplication);
473      }
474
475      RestoreSnapshotHelper.RestoreMetaChanges metaChanges =
476        new RestoreSnapshotHelper.RestoreMetaChanges(modifiedTableDescriptor,
477          parentsToChildrenPairMap);
478      metaChanges.updateMetaParentRegions(conn, regionsToAdd);
479
480      // At this point the restore is complete.
481      LOG.info("Restore snapshot=" + ClientSnapshotDescriptionUtils.toString(snapshot)
482        + " on table=" + getTableName() + " completed!");
483    } catch (IOException e) {
484      final ForeignExceptionDispatcher monitorException = new ForeignExceptionDispatcher();
485      String msg = "restore snapshot=" + ClientSnapshotDescriptionUtils.toString(snapshot)
486        + " failed in meta update. Try re-running the restore command.";
487      LOG.error(msg, e);
488      monitorException
489        .receive(new ForeignException(env.getMasterServices().getServerName().toString(), e));
490      throw new IOException(msg, e);
491    }
492
493    monitorStatus.markComplete("Restore snapshot '" + snapshot.getName() + "'!");
494    MetricsSnapshot metricsSnapshot = new MetricsSnapshot();
495    metricsSnapshot
496      .addSnapshotRestore(monitorStatus.getCompletionTimestamp() - monitorStatus.getStartTime());
497  }
498
499  /**
500   * Delete regions from in-memory states
501   * @param regionInfos       regions to delete
502   * @param env               MasterProcedureEnv
503   * @param regionReplication the number of region replications
504   */
505  private void deleteRegionsFromInMemoryStates(List<RegionInfo> regionInfos, MasterProcedureEnv env,
506    int regionReplication) {
507    FavoredNodesManager fnm = env.getMasterServices().getFavoredNodesManager();
508
509    env.getAssignmentManager().getRegionStates().deleteRegions(regionInfos);
510    env.getMasterServices().getServerManager().removeRegions(regionInfos);
511    if (fnm != null) {
512      fnm.deleteFavoredNodesForRegions(regionInfos);
513    }
514
515    // For region replicas
516    if (regionReplication > 1) {
517      for (RegionInfo regionInfo : regionInfos) {
518        for (int i = 1; i < regionReplication; i++) {
519          RegionInfo regionInfoForReplica =
520            RegionReplicaUtil.getRegionInfoForReplica(regionInfo, i);
521          env.getAssignmentManager().getRegionStates().deleteRegion(regionInfoForReplica);
522          env.getMasterServices().getServerManager().removeRegion(regionInfoForReplica);
523          if (fnm != null) {
524            fnm.deleteFavoredNodesForRegion(regionInfoForReplica);
525          }
526        }
527      }
528    }
529  }
530
531  /**
532   * Add regions to in-memory states
533   * @param regionInfos       regions to add
534   * @param env               MasterProcedureEnv
535   * @param regionReplication the number of region replications
536   */
537  private void addRegionsToInMemoryStates(List<RegionInfo> regionInfos, MasterProcedureEnv env,
538    int regionReplication) {
539    AssignmentManager am = env.getAssignmentManager();
540    for (RegionInfo regionInfo : regionInfos) {
541      if (regionInfo.isSplit()) {
542        am.getRegionStates().updateRegionState(regionInfo, RegionState.State.SPLIT);
543      } else {
544        am.getRegionStates().updateRegionState(regionInfo, RegionState.State.CLOSED);
545
546        // For region replicas
547        for (int i = 1; i < regionReplication; i++) {
548          RegionInfo regionInfoForReplica =
549            RegionReplicaUtil.getRegionInfoForReplica(regionInfo, i);
550          am.getRegionStates().updateRegionState(regionInfoForReplica, RegionState.State.CLOSED);
551        }
552      }
553    }
554  }
555
556  private void restoreSnapshotAcl(final MasterProcedureEnv env) throws IOException {
557    if (
558      restoreAcl && snapshot.hasUsersAndPermissions() && snapshot.getUsersAndPermissions() != null
559        && SnapshotDescriptionUtils.isSecurityAvailable(env.getMasterServices().getConfiguration())
560    ) {
561      // restore acl of snapshot to table.
562      RestoreSnapshotHelper.restoreSnapshotAcl(snapshot, TableName.valueOf(snapshot.getTable()),
563        env.getMasterServices().getConfiguration());
564    }
565  }
566
567  /**
568   * Exposed for Testing: HBASE-26462
569   */
570  @RestrictedApi(explanation = "Should only be called in tests", link = "",
571      allowedOnPath = ".*/src/test/.*")
572  public boolean getRestoreAcl() {
573    return restoreAcl;
574  }
575}