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