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.TableExistsException;
031import org.apache.hadoop.hbase.TableName;
032import org.apache.hadoop.hbase.client.ColumnFamilyDescriptor;
033import org.apache.hadoop.hbase.client.ColumnFamilyDescriptorBuilder;
034import org.apache.hadoop.hbase.client.RegionInfo;
035import org.apache.hadoop.hbase.client.TableDescriptor;
036import org.apache.hadoop.hbase.client.TableDescriptorBuilder;
037import org.apache.hadoop.hbase.errorhandling.ForeignException;
038import org.apache.hadoop.hbase.errorhandling.ForeignExceptionDispatcher;
039import org.apache.hadoop.hbase.fs.ErasureCodingUtils;
040import org.apache.hadoop.hbase.master.MasterCoprocessorHost;
041import org.apache.hadoop.hbase.master.MasterFileSystem;
042import org.apache.hadoop.hbase.master.MetricsSnapshot;
043import org.apache.hadoop.hbase.master.RegionState;
044import org.apache.hadoop.hbase.master.assignment.AssignmentManager;
045import org.apache.hadoop.hbase.master.procedure.CreateTableProcedure.CreateHdfsRegions;
046import org.apache.hadoop.hbase.monitoring.MonitoredTask;
047import org.apache.hadoop.hbase.monitoring.TaskMonitor;
048import org.apache.hadoop.hbase.procedure2.ProcedureStateSerializer;
049import org.apache.hadoop.hbase.procedure2.util.StringUtils;
050import org.apache.hadoop.hbase.regionserver.storefiletracker.StoreFileTrackerFactory;
051import org.apache.hadoop.hbase.snapshot.ClientSnapshotDescriptionUtils;
052import org.apache.hadoop.hbase.snapshot.RestoreSnapshotException;
053import org.apache.hadoop.hbase.snapshot.RestoreSnapshotHelper;
054import org.apache.hadoop.hbase.snapshot.SnapshotDescriptionUtils;
055import org.apache.hadoop.hbase.snapshot.SnapshotManifest;
056import org.apache.hadoop.hbase.snapshot.SnapshotTTLExpiredException;
057import org.apache.hadoop.hbase.util.CommonFSUtils;
058import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
059import org.apache.hadoop.hbase.util.FSTableDescriptors;
060import org.apache.hadoop.hbase.util.Pair;
061import org.apache.yetus.audience.InterfaceAudience;
062import org.slf4j.Logger;
063import org.slf4j.LoggerFactory;
064
065import org.apache.hbase.thirdparty.com.google.common.base.Preconditions;
066
067import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil;
068import org.apache.hadoop.hbase.shaded.protobuf.generated.HBaseProtos;
069import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProcedureProtos.CloneSnapshotState;
070import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProcedureProtos.CloneSnapshotStateData;
071import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProcedureProtos.RestoreParentToChildRegionsPair;
072import org.apache.hadoop.hbase.shaded.protobuf.generated.SnapshotProtos.SnapshotDescription;
073
074@InterfaceAudience.Private
075public class CloneSnapshotProcedure extends AbstractStateMachineTableProcedure<CloneSnapshotState> {
076  private static final Logger LOG = LoggerFactory.getLogger(CloneSnapshotProcedure.class);
077
078  private TableDescriptor tableDescriptor;
079  private SnapshotDescription snapshot;
080  private boolean restoreAcl;
081  private String customSFT;
082  private List<RegionInfo> newRegions = null;
083  private Map<String, Pair<String, String>> parentsToChildrenPairMap = new HashMap<>();
084
085  // Monitor
086  private MonitoredTask monitorStatus = null;
087
088  /**
089   * Constructor (for failover)
090   */
091  public CloneSnapshotProcedure() {
092  }
093
094  public CloneSnapshotProcedure(final MasterProcedureEnv env, final TableDescriptor tableDescriptor,
095    final SnapshotDescription snapshot) {
096    this(env, tableDescriptor, snapshot, false);
097  }
098
099  /**
100   * Constructor
101   * @param env             MasterProcedureEnv
102   * @param tableDescriptor the table to operate on
103   * @param snapshot        snapshot to clone from
104   */
105  public CloneSnapshotProcedure(final MasterProcedureEnv env, final TableDescriptor tableDescriptor,
106    final SnapshotDescription snapshot, final boolean restoreAcl) {
107    this(env, tableDescriptor, snapshot, restoreAcl, null);
108  }
109
110  public CloneSnapshotProcedure(final MasterProcedureEnv env, final TableDescriptor tableDescriptor,
111    final SnapshotDescription snapshot, final boolean restoreAcl, final String customSFT) {
112    super(env);
113    this.tableDescriptor = tableDescriptor;
114    this.snapshot = snapshot;
115    this.restoreAcl = restoreAcl;
116    this.customSFT = customSFT;
117
118    getMonitorStatus();
119  }
120
121  /**
122   * Set up monitor status if it is not created.
123   */
124  private MonitoredTask getMonitorStatus() {
125    if (monitorStatus == null) {
126      monitorStatus = TaskMonitor.get()
127        .createStatus("Cloning  snapshot '" + snapshot.getName() + "' to table " + getTableName());
128    }
129    return monitorStatus;
130  }
131
132  private void restoreSnapshotAcl(MasterProcedureEnv env) throws IOException {
133    Configuration conf = env.getMasterServices().getConfiguration();
134    if (
135      restoreAcl && snapshot.hasUsersAndPermissions() && snapshot.getUsersAndPermissions() != null
136        && SnapshotDescriptionUtils.isSecurityAvailable(conf)
137    ) {
138      RestoreSnapshotHelper.restoreSnapshotAcl(snapshot, tableDescriptor.getTableName(), conf);
139    }
140  }
141
142  @Override
143  protected Flow executeFromState(final MasterProcedureEnv env, final CloneSnapshotState state)
144    throws InterruptedException {
145    LOG.trace("{} execute state={}", this, state);
146    try {
147      switch (state) {
148        case CLONE_SNAPSHOT_PRE_OPERATION:
149          // Verify if we can clone the table
150          prepareClone(env);
151
152          preCloneSnapshot(env);
153          setNextState(CloneSnapshotState.CLONE_SNAPSHOT_WRITE_FS_LAYOUT);
154          break;
155        case CLONE_SNAPSHOT_WRITE_FS_LAYOUT:
156          updateTableDescriptorWithSFT();
157          newRegions = createFilesystemLayout(env, tableDescriptor, newRegions);
158          env.getMasterServices().getTableDescriptors().update(tableDescriptor, true);
159          if (tableDescriptor.getErasureCodingPolicy() != null) {
160            setNextState(CloneSnapshotState.CLONE_SNAPSHOT_SET_ERASURE_CODING_POLICY);
161          } else {
162            setNextState(CloneSnapshotState.CLONE_SNAPSHOT_ADD_TO_META);
163          }
164          break;
165        case CLONE_SNAPSHOT_SET_ERASURE_CODING_POLICY:
166          ErasureCodingUtils.setPolicy(env.getMasterFileSystem().getFileSystem(),
167            env.getMasterFileSystem().getRootDir(), getTableName(),
168            tableDescriptor.getErasureCodingPolicy());
169          setNextState(CloneSnapshotState.CLONE_SNAPSHOT_ADD_TO_META);
170          break;
171        case CLONE_SNAPSHOT_ADD_TO_META:
172          addRegionsToMeta(env);
173          setNextState(CloneSnapshotState.CLONE_SNAPSHOT_ASSIGN_REGIONS);
174          break;
175        case CLONE_SNAPSHOT_ASSIGN_REGIONS:
176          CreateTableProcedure.setEnablingState(env, getTableName());
177
178          // Separate newRegions to split regions and regions to assign
179          List<RegionInfo> splitRegions = new ArrayList<>();
180          List<RegionInfo> regionsToAssign = new ArrayList<>();
181          newRegions.forEach(ri -> {
182            if (ri.isOffline() && (ri.isSplit() || ri.isSplitParent())) {
183              splitRegions.add(ri);
184            } else {
185              regionsToAssign.add(ri);
186            }
187          });
188
189          // For split regions, add them to RegionStates
190          AssignmentManager am = env.getAssignmentManager();
191          splitRegions
192            .forEach(ri -> am.getRegionStates().updateRegionState(ri, RegionState.State.SPLIT));
193
194          addChildProcedure(
195            env.getAssignmentManager().createRoundRobinAssignProcedures(regionsToAssign));
196          setNextState(CloneSnapshotState.CLONE_SNAPSHOT_UPDATE_DESC_CACHE);
197          break;
198        case CLONE_SNAPSHOT_UPDATE_DESC_CACHE:
199          // XXX: this stage should be named as set table enabled, as now we will cache the
200          // descriptor after writing fs layout.
201          CreateTableProcedure.setEnabledState(env, getTableName());
202          setNextState(CloneSnapshotState.CLONE_SNAPHOST_RESTORE_ACL);
203          break;
204        case CLONE_SNAPHOST_RESTORE_ACL:
205          restoreSnapshotAcl(env);
206          setNextState(CloneSnapshotState.CLONE_SNAPSHOT_POST_OPERATION);
207          break;
208        case CLONE_SNAPSHOT_POST_OPERATION:
209          postCloneSnapshot(env);
210
211          MetricsSnapshot metricsSnapshot = new MetricsSnapshot();
212          metricsSnapshot.addSnapshotClone(
213            getMonitorStatus().getCompletionTimestamp() - getMonitorStatus().getStartTime());
214          getMonitorStatus().markComplete("Clone snapshot '" + snapshot.getName() + "' completed!");
215          return Flow.NO_MORE_STATE;
216        default:
217          throw new UnsupportedOperationException("unhandled state=" + state);
218      }
219    } catch (IOException e) {
220      if (isRollbackSupported(state)) {
221        setFailure("master-clone-snapshot", e);
222      } else {
223        LOG.warn("Retriable error trying to clone snapshot=" + snapshot.getName() + " to table="
224          + getTableName() + " state=" + state, e);
225      }
226    }
227    return Flow.HAS_MORE_STATE;
228  }
229
230  /**
231   * If a StoreFileTracker is specified we strip the TableDescriptor from previous SFT config and
232   * set the specified SFT on the table level
233   */
234  private void updateTableDescriptorWithSFT() {
235    if (StringUtils.isEmpty(customSFT)) {
236      return;
237    }
238
239    TableDescriptorBuilder builder = TableDescriptorBuilder.newBuilder(tableDescriptor);
240    builder.setValue(StoreFileTrackerFactory.TRACKER_IMPL, customSFT);
241    for (ColumnFamilyDescriptor family : tableDescriptor.getColumnFamilies()) {
242      ColumnFamilyDescriptorBuilder cfBuilder = ColumnFamilyDescriptorBuilder.newBuilder(family);
243      cfBuilder.setConfiguration(StoreFileTrackerFactory.TRACKER_IMPL, null);
244      cfBuilder.setValue(StoreFileTrackerFactory.TRACKER_IMPL, null);
245      builder.modifyColumnFamily(cfBuilder.build());
246    }
247    tableDescriptor = builder.build();
248  }
249
250  private void validateSFT() {
251    if (StringUtils.isEmpty(customSFT)) {
252      return;
253    }
254
255    // if customSFT is invalid getTrackerClass will throw a RuntimeException
256    Configuration sftConfig = new Configuration();
257    sftConfig.set(StoreFileTrackerFactory.TRACKER_IMPL, customSFT);
258    StoreFileTrackerFactory.getTrackerClass(sftConfig);
259  }
260
261  @Override
262  protected void rollbackState(final MasterProcedureEnv env, final CloneSnapshotState state)
263    throws IOException {
264    if (state == CloneSnapshotState.CLONE_SNAPSHOT_PRE_OPERATION) {
265      DeleteTableProcedure.deleteTableStates(env, getTableName());
266      // TODO-MAYBE: call the deleteTable coprocessor event?
267      return;
268    }
269
270    // The procedure doesn't have a rollback. The execution will succeed, at some point.
271    throw new UnsupportedOperationException("unhandled state=" + state);
272  }
273
274  @Override
275  protected boolean isRollbackSupported(final CloneSnapshotState state) {
276    switch (state) {
277      case CLONE_SNAPSHOT_PRE_OPERATION:
278        return true;
279      default:
280        return false;
281    }
282  }
283
284  @Override
285  protected CloneSnapshotState getState(final int stateId) {
286    return CloneSnapshotState.valueOf(stateId);
287  }
288
289  @Override
290  protected int getStateId(final CloneSnapshotState state) {
291    return state.getNumber();
292  }
293
294  @Override
295  protected CloneSnapshotState getInitialState() {
296    return CloneSnapshotState.CLONE_SNAPSHOT_PRE_OPERATION;
297  }
298
299  @Override
300  public TableName getTableName() {
301    return tableDescriptor.getTableName();
302  }
303
304  @Override
305  public TableOperationType getTableOperationType() {
306    return TableOperationType.CREATE; // Clone is creating a table
307  }
308
309  @Override
310  public void toStringClassDetails(StringBuilder sb) {
311    sb.append(getClass().getSimpleName());
312    sb.append(" (table=");
313    sb.append(getTableName());
314    sb.append(" snapshot=");
315    sb.append(snapshot);
316    sb.append(")");
317  }
318
319  @Override
320  protected void serializeStateData(ProcedureStateSerializer serializer) throws IOException {
321    super.serializeStateData(serializer);
322
323    CloneSnapshotStateData.Builder cloneSnapshotMsg = CloneSnapshotStateData.newBuilder()
324      .setUserInfo(MasterProcedureUtil.toProtoUserInfo(getUser())).setSnapshot(this.snapshot)
325      .setTableSchema(ProtobufUtil.toTableSchema(tableDescriptor));
326
327    cloneSnapshotMsg.setRestoreAcl(restoreAcl);
328    if (newRegions != null) {
329      for (RegionInfo hri : newRegions) {
330        cloneSnapshotMsg.addRegionInfo(ProtobufUtil.toRegionInfo(hri));
331      }
332    }
333    if (!parentsToChildrenPairMap.isEmpty()) {
334      final Iterator<Map.Entry<String, Pair<String, String>>> it =
335        parentsToChildrenPairMap.entrySet().iterator();
336      while (it.hasNext()) {
337        final Map.Entry<String, Pair<String, String>> entry = it.next();
338
339        RestoreParentToChildRegionsPair.Builder parentToChildrenPair =
340          RestoreParentToChildRegionsPair.newBuilder().setParentRegionName(entry.getKey())
341            .setChild1RegionName(entry.getValue().getFirst())
342            .setChild2RegionName(entry.getValue().getSecond());
343        cloneSnapshotMsg.addParentToChildRegionsPairList(parentToChildrenPair);
344      }
345    }
346    if (!StringUtils.isEmpty(customSFT)) {
347      cloneSnapshotMsg.setCustomSFT(customSFT);
348    }
349    serializer.serialize(cloneSnapshotMsg.build());
350  }
351
352  @Override
353  protected void deserializeStateData(ProcedureStateSerializer serializer) throws IOException {
354    super.deserializeStateData(serializer);
355
356    CloneSnapshotStateData cloneSnapshotMsg = serializer.deserialize(CloneSnapshotStateData.class);
357    setUser(MasterProcedureUtil.toUserInfo(cloneSnapshotMsg.getUserInfo()));
358    snapshot = cloneSnapshotMsg.getSnapshot();
359    tableDescriptor = ProtobufUtil.toTableDescriptor(cloneSnapshotMsg.getTableSchema());
360    if (cloneSnapshotMsg.hasRestoreAcl()) {
361      restoreAcl = cloneSnapshotMsg.getRestoreAcl();
362    }
363    if (cloneSnapshotMsg.getRegionInfoCount() == 0) {
364      newRegions = null;
365    } else {
366      newRegions = new ArrayList<>(cloneSnapshotMsg.getRegionInfoCount());
367      for (HBaseProtos.RegionInfo hri : cloneSnapshotMsg.getRegionInfoList()) {
368        newRegions.add(ProtobufUtil.toRegionInfo(hri));
369      }
370    }
371    if (cloneSnapshotMsg.getParentToChildRegionsPairListCount() > 0) {
372      parentsToChildrenPairMap = new HashMap<>();
373      for (RestoreParentToChildRegionsPair parentToChildrenPair : cloneSnapshotMsg
374        .getParentToChildRegionsPairListList()) {
375        parentsToChildrenPairMap.put(parentToChildrenPair.getParentRegionName(), new Pair<>(
376          parentToChildrenPair.getChild1RegionName(), parentToChildrenPair.getChild2RegionName()));
377      }
378    }
379    if (!StringUtils.isEmpty(cloneSnapshotMsg.getCustomSFT())) {
380      customSFT = cloneSnapshotMsg.getCustomSFT();
381    }
382    // Make sure that the monitor status is set up
383    getMonitorStatus();
384  }
385
386  /**
387   * Action before any real action of cloning from snapshot.
388   * @param env MasterProcedureEnv
389   */
390  private void prepareClone(final MasterProcedureEnv env) throws IOException {
391    final TableName tableName = getTableName();
392    if (env.getMasterServices().getTableDescriptors().exists(tableName)) {
393      throw new TableExistsException(tableName);
394    }
395
396    // check whether ttl has expired for this snapshot
397    if (
398      SnapshotDescriptionUtils.isExpiredSnapshot(snapshot.getTtl(), snapshot.getCreationTime(),
399        EnvironmentEdgeManager.currentTime())
400    ) {
401      throw new SnapshotTTLExpiredException(ProtobufUtil.createSnapshotDesc(snapshot));
402    }
403
404    validateSFT();
405  }
406
407  /**
408   * Action before cloning from snapshot.
409   * @param env MasterProcedureEnv
410   */
411  private void preCloneSnapshot(final MasterProcedureEnv env)
412    throws IOException, InterruptedException {
413    if (!getTableName().isSystemTable()) {
414      // Check and update namespace quota
415      final MasterFileSystem mfs = env.getMasterServices().getMasterFileSystem();
416
417      SnapshotManifest manifest =
418        SnapshotManifest.open(env.getMasterConfiguration(), mfs.getFileSystem(),
419          SnapshotDescriptionUtils.getCompletedSnapshotDir(snapshot, mfs.getRootDir()), snapshot);
420
421      ProcedureSyncWait.getMasterQuotaManager(env).checkNamespaceTableAndRegionQuota(getTableName(),
422        manifest.getRegionManifestsMap().size());
423    }
424
425    final MasterCoprocessorHost cpHost = env.getMasterCoprocessorHost();
426    if (cpHost != null) {
427      cpHost.preCreateTableAction(tableDescriptor, null, getUser());
428    }
429  }
430
431  /**
432   * Action after cloning from snapshot.
433   * @param env MasterProcedureEnv
434   */
435  private void postCloneSnapshot(final MasterProcedureEnv env)
436    throws IOException, InterruptedException {
437    final MasterCoprocessorHost cpHost = env.getMasterCoprocessorHost();
438    if (cpHost != null) {
439      final RegionInfo[] regions =
440        (newRegions == null) ? null : newRegions.toArray(new RegionInfo[newRegions.size()]);
441      cpHost.postCompletedCreateTableAction(tableDescriptor, regions, getUser());
442    }
443  }
444
445  /**
446   * Create regions in file system.
447   * @param env MasterProcedureEnv
448   */
449  private List<RegionInfo> createFilesystemLayout(final MasterProcedureEnv env,
450    final TableDescriptor tableDescriptor, final List<RegionInfo> newRegions) throws IOException {
451    return createFsLayout(env, tableDescriptor, newRegions, new CreateHdfsRegions() {
452      @Override
453      public List<RegionInfo> createHdfsRegions(final MasterProcedureEnv env,
454        final Path tableRootDir, final TableName tableName, final List<RegionInfo> newRegions)
455        throws IOException {
456
457        final MasterFileSystem mfs = env.getMasterServices().getMasterFileSystem();
458        final FileSystem fs = mfs.getFileSystem();
459        final Path rootDir = mfs.getRootDir();
460        final Configuration conf = env.getMasterConfiguration();
461        final ForeignExceptionDispatcher monitorException = new ForeignExceptionDispatcher();
462
463        getMonitorStatus().setStatus("Clone snapshot - creating regions for table: " + tableName);
464
465        try {
466          // 1. Execute the on-disk Clone
467          Path snapshotDir = SnapshotDescriptionUtils.getCompletedSnapshotDir(snapshot, rootDir);
468          SnapshotManifest manifest = SnapshotManifest.open(conf, fs, snapshotDir, snapshot);
469          RestoreSnapshotHelper restoreHelper = new RestoreSnapshotHelper(conf, fs, manifest,
470            tableDescriptor, tableRootDir, monitorException, monitorStatus);
471          RestoreSnapshotHelper.RestoreMetaChanges metaChanges = restoreHelper.restoreHdfsRegions();
472
473          // Clone operation should not have stuff to restore or remove
474          Preconditions.checkArgument(!metaChanges.hasRegionsToRestore(),
475            "A clone should not have regions to restore");
476          Preconditions.checkArgument(!metaChanges.hasRegionsToRemove(),
477            "A clone should not have regions to remove");
478
479          // At this point the clone is complete. Next step is enabling the table.
480          String msg =
481            "Clone snapshot=" + snapshot.getName() + " on table=" + tableName + " completed!";
482          LOG.info(msg);
483          monitorStatus.setStatus(msg + " Waiting for table to be enabled...");
484
485          // 2. Let the next step to add the regions to meta
486          return metaChanges.getRegionsToAdd();
487        } catch (Exception e) {
488          String msg = "clone snapshot=" + ClientSnapshotDescriptionUtils.toString(snapshot)
489            + " failed because " + e.getMessage();
490          LOG.error(msg, e);
491          IOException rse =
492            new RestoreSnapshotException(msg, e, ProtobufUtil.createSnapshotDesc(snapshot));
493
494          // these handlers aren't futures so we need to register the error here.
495          monitorException.receive(new ForeignException("Master CloneSnapshotProcedure", rse));
496          throw rse;
497        }
498      }
499    });
500  }
501
502  /**
503   * Create region layout in file system.
504   * @param env MasterProcedureEnv
505   */
506  private List<RegionInfo> createFsLayout(final MasterProcedureEnv env,
507    final TableDescriptor tableDescriptor, List<RegionInfo> newRegions,
508    final CreateHdfsRegions hdfsRegionHandler) throws IOException {
509    final MasterFileSystem mfs = env.getMasterServices().getMasterFileSystem();
510
511    // 1. Create Table Descriptor
512    // using a copy of descriptor, table will be created enabling first
513    final Path tableDir =
514      CommonFSUtils.getTableDir(mfs.getRootDir(), tableDescriptor.getTableName());
515    if (CommonFSUtils.isExists(mfs.getFileSystem(), tableDir)) {
516      // if the region dirs exist, will cause exception and unlimited retry (see HBASE-24546)
517      LOG.warn("temp table dir already exists on disk: {}, will be deleted.", tableDir);
518      CommonFSUtils.deleteDirectory(mfs.getFileSystem(), tableDir);
519    }
520    ((FSTableDescriptors) (env.getMasterServices().getTableDescriptors()))
521      .createTableDescriptorForTableDirectory(tableDir,
522        TableDescriptorBuilder.newBuilder(tableDescriptor).build(), false);
523
524    // 2. Create Regions
525    newRegions = hdfsRegionHandler.createHdfsRegions(env, mfs.getRootDir(),
526      tableDescriptor.getTableName(), newRegions);
527
528    return newRegions;
529  }
530
531  /**
532   * Add regions to hbase:meta table.
533   * @param env MasterProcedureEnv
534   */
535  private void addRegionsToMeta(final MasterProcedureEnv env) throws IOException {
536    newRegions = CreateTableProcedure.addTableToMeta(env, tableDescriptor, newRegions);
537
538    // TODO: parentsToChildrenPairMap is always empty, which makes updateMetaParentRegions()
539    // a no-op. This part seems unnecessary. Figure out. - Appy 12/21/17
540    RestoreSnapshotHelper.RestoreMetaChanges metaChanges =
541      new RestoreSnapshotHelper.RestoreMetaChanges(tableDescriptor, parentsToChildrenPairMap);
542    metaChanges.updateMetaParentRegions(env.getMasterServices().getConnection(), newRegions);
543  }
544
545  /**
546   * Exposed for Testing: HBASE-26462
547   */
548  @RestrictedApi(explanation = "Should only be called in tests", link = "",
549      allowedOnPath = ".*/src/test/.*")
550  public boolean getRestoreAcl() {
551    return restoreAcl;
552  }
553
554}