@Override
    public void execute() throws IOException {
      // Get the stage ID to fail on
      try (Admin admin = conn.getAdmin()) {
        // Begin BACKUP
        beginBackup(backupManager, backupInfo);
        failStageIf(Stage.stage_0);
        String savedStartCode;
        boolean firstBackup;
        // do snapshot for full table backup
        savedStartCode = backupManager.readBackupStartCode();
        firstBackup = savedStartCode == null || Long.parseLong(savedStartCode) == 0L;
        if (firstBackup) {
          // This is our first backup. Let's put some marker to system table so that we can hold the
          // logs while we do the backup.
          backupManager.writeBackupStartCode(0L);
        }
        failStageIf(Stage.stage_1);
        // We roll log here before we do the snapshot. It is possible there is duplicate data
        // in the log that is already in the snapshot. But if we do it after the snapshot, we
        // could have data loss.
        // A better approach is to do the roll log on each RS in the same global procedure as
        // the snapshot.
        LOG.info("Execute roll log procedure for full backup ...");

        Map<String, String> props = new HashMap<>();
        props.put("backupRoot", backupInfo.getBackupRootDir());
        admin.execProcedure(LogRollMasterProcedureManager.ROLLLOG_PROCEDURE_SIGNATURE,
          LogRollMasterProcedureManager.ROLLLOG_PROCEDURE_NAME, props);
        failStageIf(Stage.stage_2);
        newTimestamps = backupManager.readRegionServerLastLogRollResult();

        // SNAPSHOT_TABLES:
        backupInfo.setPhase(BackupPhase.SNAPSHOT);
        for (TableName tableName : tableList) {
          String snapshotName = "snapshot_" + Long.toString(EnvironmentEdgeManager.currentTime())
            + "_" + tableName.getNamespaceAsString() + "_" + tableName.getQualifierAsString();

          snapshotTable(admin, tableName, snapshotName);
          backupInfo.setSnapshotName(tableName, snapshotName);
        }
        failStageIf(Stage.stage_3);
        // SNAPSHOT_COPY:
        // do snapshot copy
        LOG.debug("snapshot copy for " + backupId);
        snapshotCopy(backupInfo);
        // Updates incremental backup table set
        backupManager.addIncrementalBackupTableSet(backupInfo.getTables());

        // BACKUP_COMPLETE:
        // set overall backup status: complete. Here we make sure to complete the backup.
        // After this checkpoint, even if entering cancel process, will let the backup finished
        backupInfo.setState(BackupState.COMPLETE);
        // The table list in backupInfo is good for both full backup and incremental backup.
        // For incremental backup, it contains the incremental backup table set.
        backupManager.writeRegionServerLogTimestamp(backupInfo.getTables(), newTimestamps);

        Map<TableName, Map<String, Long>> newTableSetTimestampMap =
          backupManager.readLogTimestampMap();

        Long newStartCode =
          BackupUtils.getMinValue(BackupUtils.getRSLogTimestampMins(newTableSetTimestampMap));
        backupManager.writeBackupStartCode(newStartCode);
        failStageIf(Stage.stage_4);
        // backup complete
        completeBackup(conn, backupInfo, BackupType.FULL, conf);

      } catch (Exception e) {

        if (autoRestoreOnFailure) {
          failBackup(conn, backupInfo, backupManager, e, "Unexpected BackupException : ",
            BackupType.FULL, conf);
        }
        throw new IOException(e);
      }
    }
This class is only a base for other integration-level backup tests. Do not add tests here.
TestBackupSmallTests is where tests that don't require bring machines up/down should go All other
tests should have their own classes and extend this one
hbase-backup/src/test/java/org/apache/hadoop/hbase/backup/TestBackupBase.java