From d96b8f2bdda136351997098c5a84d05ce80945eb Mon Sep 17 00:00:00 2001 From: "Doroszlai, Attila" Date: Thu, 2 Jan 2025 14:27:58 +0100 Subject: [PATCH] HDDS-12001. Create parent class for repair tools --- .../ozone/repair/RecoverSCMCertificate.java | 36 ++++------- .../hadoop/ozone/repair/RepairTool.java | 62 +++++++++++++++++++ .../ozone/repair/ldb/SnapshotRepair.java | 52 +++++++--------- .../repair/ldb/TransactionInfoRepair.java | 31 +++------- .../hadoop/ozone/repair/om/FSORepairCLI.java | 15 ++--- .../ozone/repair/quota/QuotaTrigger.java | 25 ++++---- 6 files changed, 120 insertions(+), 101 deletions(-) create mode 100644 hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/repair/RepairTool.java diff --git a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/repair/RecoverSCMCertificate.java b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/repair/RecoverSCMCertificate.java index e6462aa3f85..b85ccfb1e8b 100644 --- a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/repair/RecoverSCMCertificate.java +++ b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/repair/RecoverSCMCertificate.java @@ -38,7 +38,6 @@ import picocli.CommandLine; import java.io.IOException; -import java.io.PrintWriter; import java.math.BigInteger; import java.net.InetAddress; import java.nio.charset.StandardCharsets; @@ -52,7 +51,6 @@ import java.util.ArrayList; import java.util.Optional; import java.util.Arrays; -import java.util.concurrent.Callable; import static org.apache.hadoop.hdds.scm.metadata.SCMDBDefinition.VALID_SCM_CERTS; import static org.apache.hadoop.hdds.security.x509.certificate.client.DefaultCertificateClient.CERT_FILE_NAME_FORMAT; @@ -68,7 +66,7 @@ name = "cert-recover", description = "Recover Deleted SCM Certificate from RocksDB") @MetaInfServices(RepairSubcommand.class) -public class RecoverSCMCertificate implements Callable, RepairSubcommand { +public class RecoverSCMCertificate extends RepairTool implements RepairSubcommand { @CommandLine.Option(names = {"--db"}, required = true, @@ -78,19 +76,8 @@ public class RecoverSCMCertificate implements Callable, RepairSubcommand { @CommandLine.ParentCommand private OzoneRepair parent; - @CommandLine.Spec - private CommandLine.Model.CommandSpec spec; - - private PrintWriter err() { - return spec.commandLine().getErr(); - } - - private PrintWriter out() { - return spec.commandLine().getOut(); - } - @Override - public Void call() throws Exception { + public void execute() throws Exception { dbPath = removeTrailingSlashIfNeeded(dbPath); String tableName = VALID_SCM_CERTS.getName(); DBDefinition dbDefinition = @@ -112,15 +99,15 @@ public Void call() throws Exception { SecurityConfig securityConfig = new SecurityConfig(parent.getOzoneConf()); Map allCerts = getAllCerts(columnFamilyDefinition, cfHandle, db); - out().println("All Certs in DB : " + allCerts.keySet()); + info("All Certs in DB : %s", allCerts.keySet()); String hostName = InetAddress.getLocalHost().getHostName(); - out().println("Host: " + hostName); + info("Host: %s", hostName); X509Certificate subCertificate = getSubCertificate(allCerts, hostName); X509Certificate rootCertificate = getRootCertificate(allCerts); - out().println("Sub cert serialID for this host: " + subCertificate.getSerialNumber().toString()); - out().println("Root cert serialID: " + rootCertificate.getSerialNumber().toString()); + info("Sub cert serialID for this host: %s", subCertificate.getSerialNumber()); + info("Root cert serialID: %s", rootCertificate.getSerialNumber()); boolean isRootCA = false; @@ -131,9 +118,8 @@ public Void call() throws Exception { storeCerts(subCertificate, rootCertificate, isRootCA, securityConfig); } } catch (RocksDBException | CertificateException exception) { - err().print("Failed to recover scm cert"); + error("Failed to recover scm cert"); } - return null; } private static ColumnFamilyHandle getColumnFamilyHandle( @@ -210,17 +196,17 @@ private void storeCerts(X509Certificate scmCertificate, CertificateCodec certCodec = new CertificateCodec(securityConfig, SCMCertificateClient.COMPONENT_NAME); - out().println("Writing certs to path : " + certCodec.getLocation().toString()); + info("Writing certs to path : %s", certCodec.getLocation()); CertPath certPath = addRootCertInPath(scmCertificate, rootCertificate); CertPath rootCertPath = getRootCertPath(rootCertificate); String encodedCert = CertificateCodec.getPEMEncodedString(certPath); String certName = String.format(CERT_FILE_NAME_FORMAT, - CAType.NONE.getFileNamePrefix() + scmCertificate.getSerialNumber().toString()); + CAType.NONE.getFileNamePrefix() + scmCertificate.getSerialNumber()); certCodec.writeCertificate(certName, encodedCert); String rootCertName = String.format(CERT_FILE_NAME_FORMAT, - CAType.SUBORDINATE.getFileNamePrefix() + rootCertificate.getSerialNumber().toString()); + CAType.SUBORDINATE.getFileNamePrefix() + rootCertificate.getSerialNumber()); String encodedRootCert = CertificateCodec.getPEMEncodedString(rootCertPath); certCodec.writeCertificate(rootCertName, encodedRootCert); @@ -230,7 +216,7 @@ private void storeCerts(X509Certificate scmCertificate, if (isRootCA) { CertificateCodec rootCertCodec = new CertificateCodec(securityConfig, OzoneConsts.SCM_ROOT_CA_COMPONENT_NAME); - out().println("Writing root certs to path : " + rootCertCodec.getLocation().toString()); + info("Writing root certs to path : %s", rootCertCodec.getLocation()); rootCertCodec.writeCertificate(rootCertCodec.getLocation().toAbsolutePath(), securityConfig.getCertificateFileName(), encodedRootCert); } diff --git a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/repair/RepairTool.java b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/repair/RepairTool.java new file mode 100644 index 00000000000..b94e1c52d82 --- /dev/null +++ b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/repair/RepairTool.java @@ -0,0 +1,62 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF 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.apache.hadoop.ozone.repair; + +import org.apache.hadoop.hdds.cli.AbstractSubcommand; + +import java.io.PrintWriter; +import java.util.concurrent.Callable; + +/** Parent class for all actionable repair commands. */ +public abstract class RepairTool extends AbstractSubcommand implements Callable { + + /** Hook method for subclasses for performing actual repair task. */ + protected abstract void execute() throws Exception; + + @Override + public final Void call() throws Exception { + execute(); + return null; + } + + protected void info(String msg, Object... args) { + out().println(formatMessage(msg, args)); + } + + protected void error(String msg, Object... args) { + err().println(formatMessage(msg, args)); + } + + private PrintWriter out() { + return spec().commandLine() + .getOut(); + } + + private PrintWriter err() { + return spec().commandLine() + .getErr(); + } + + private String formatMessage(String msg, Object[] args) { + if (args != null && args.length > 0) { + msg = String.format(msg, args); + } + return msg; + } + +} diff --git a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/repair/ldb/SnapshotRepair.java b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/repair/ldb/SnapshotRepair.java index 45c10f5668b..9c24b0d131f 100644 --- a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/repair/ldb/SnapshotRepair.java +++ b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/repair/ldb/SnapshotRepair.java @@ -24,6 +24,7 @@ import org.apache.hadoop.hdds.utils.db.managed.ManagedRocksIterator; import org.apache.hadoop.ozone.debug.RocksDBUtils; import org.apache.hadoop.ozone.om.helpers.SnapshotInfo; +import org.apache.hadoop.ozone.repair.RepairTool; import org.apache.hadoop.ozone.shell.bucket.BucketUri; import org.rocksdb.ColumnFamilyDescriptor; import org.rocksdb.ColumnFamilyHandle; @@ -31,7 +32,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import picocli.CommandLine; -import picocli.CommandLine.Model.CommandSpec; import java.io.IOException; import java.util.ArrayList; @@ -40,7 +40,6 @@ import java.util.Objects; import java.util.Set; import java.util.UUID; -import java.util.concurrent.Callable; import static org.apache.hadoop.ozone.OzoneConsts.OM_KEY_PREFIX; import static org.apache.hadoop.ozone.OzoneConsts.SNAPSHOT_INFO_TABLE; @@ -52,13 +51,10 @@ name = "snapshot", description = "CLI to update global and path previous snapshot for a snapshot in case snapshot chain is corrupted." ) -public class SnapshotRepair implements Callable { +public class SnapshotRepair extends RepairTool { protected static final Logger LOG = LoggerFactory.getLogger(SnapshotRepair.class); - @CommandLine.Spec - private static CommandSpec spec; - @CommandLine.ParentCommand private RDBRepair parent; @@ -84,15 +80,15 @@ public class SnapshotRepair implements Callable { private boolean dryRun; @Override - public Void call() throws Exception { + public void execute() throws Exception { List cfHandleList = new ArrayList<>(); List cfDescList = RocksDBUtils.getColumnFamilyDescriptors(parent.getDbPath()); try (ManagedRocksDB db = ManagedRocksDB.open(parent.getDbPath(), cfDescList, cfHandleList)) { ColumnFamilyHandle snapshotInfoCfh = RocksDBUtils.getColumnFamilyHandle(SNAPSHOT_INFO_TABLE, cfHandleList); if (snapshotInfoCfh == null) { - System.err.println(SNAPSHOT_INFO_TABLE + " is not in a column family in DB for the given path."); - return null; + error("%s is not in a column family in DB for the given path.", SNAPSHOT_INFO_TABLE); + return; } String snapshotInfoTableKey = SnapshotInfo.getTableKey(bucketUri.getValue().getVolumeName(), @@ -102,9 +98,9 @@ public Void call() throws Exception { SnapshotInfo.getCodec()); if (snapshotInfo == null) { - System.err.println(snapshotName + " does not exist for given bucketUri: " + OM_KEY_PREFIX + + error("%s does not exist for given bucketUri: %s", snapshotName, OM_KEY_PREFIX + bucketUri.getValue().getVolumeName() + OM_KEY_PREFIX + bucketUri.getValue().getBucketName()); - return null; + return; } // snapshotIdSet is the set of the all existed snapshots ID to make that the provided global previous and path @@ -112,52 +108,50 @@ public Void call() throws Exception { Set snapshotIdSet = getSnapshotIdSet(db, snapshotInfoCfh); if (Objects.equals(snapshotInfo.getSnapshotId(), globalPreviousSnapshotId)) { - System.err.println("globalPreviousSnapshotId: '" + globalPreviousSnapshotId + - "' is equal to given snapshot's ID: '" + snapshotInfo.getSnapshotId() + "'."); - return null; + error("globalPreviousSnapshotId: '%s' is equal to given snapshot's ID: '%s'.", + globalPreviousSnapshotId, snapshotInfo.getSnapshotId()); + return; } if (Objects.equals(snapshotInfo.getSnapshotId(), pathPreviousSnapshotId)) { - System.err.println("pathPreviousSnapshotId: '" + pathPreviousSnapshotId + - "' is equal to given snapshot's ID: '" + snapshotInfo.getSnapshotId() + "'."); - return null; + error("pathPreviousSnapshotId: '%s' is equal to given snapshot's ID: '%s'.", + pathPreviousSnapshotId, snapshotInfo.getSnapshotId()); + return; } if (!snapshotIdSet.contains(globalPreviousSnapshotId)) { - System.err.println("globalPreviousSnapshotId: '" + globalPreviousSnapshotId + - "' does not exist in snapshotInfoTable."); - return null; + error("globalPreviousSnapshotId: '%s' does not exist in snapshotInfoTable.", + globalPreviousSnapshotId); + return; } if (!snapshotIdSet.contains(pathPreviousSnapshotId)) { - System.err.println("pathPreviousSnapshotId: '" + pathPreviousSnapshotId + - "' does not exist in snapshotInfoTable."); - return null; + error("pathPreviousSnapshotId: '%s' does not exist in snapshotInfoTable.", + pathPreviousSnapshotId); + return; } snapshotInfo.setGlobalPreviousSnapshotId(globalPreviousSnapshotId); snapshotInfo.setPathPreviousSnapshotId(pathPreviousSnapshotId); if (dryRun) { - System.out.println("SnapshotInfo would be updated to : " + snapshotInfo); + info("SnapshotInfo would be updated to : %s", snapshotInfo); } else { byte[] snapshotInfoBytes = SnapshotInfo.getCodec().toPersistedFormat(snapshotInfo); db.get() .put(snapshotInfoCfh, StringCodec.get().toPersistedFormat(snapshotInfoTableKey), snapshotInfoBytes); - System.out.println("Snapshot Info is updated to : " + + info("Snapshot Info is updated to : %s", RocksDBUtils.getValue(db, snapshotInfoCfh, snapshotInfoTableKey, SnapshotInfo.getCodec())); } } catch (RocksDBException exception) { - System.err.println("Failed to update the RocksDB for the given path: " + parent.getDbPath()); - System.err.println( + error("Failed to update the RocksDB for the given path: %s", parent.getDbPath()); + error( "Make sure that Ozone entity (OM, SCM or DN) is not running for the give dbPath and current host."); LOG.error(exception.toString()); } finally { IOUtils.closeQuietly(cfHandleList); } - - return null; } private Set getSnapshotIdSet(ManagedRocksDB db, ColumnFamilyHandle snapshotInfoCfh) diff --git a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/repair/ldb/TransactionInfoRepair.java b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/repair/ldb/TransactionInfoRepair.java index 277a2788247..192ff0fb1d5 100644 --- a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/repair/ldb/TransactionInfoRepair.java +++ b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/repair/ldb/TransactionInfoRepair.java @@ -27,6 +27,7 @@ import org.apache.hadoop.hdds.utils.db.StringCodec; import org.apache.hadoop.hdds.utils.db.managed.ManagedRocksDB; import org.apache.hadoop.ozone.debug.RocksDBUtils; +import org.apache.hadoop.ozone.repair.RepairTool; import org.rocksdb.ColumnFamilyDescriptor; import org.rocksdb.ColumnFamilyHandle; import org.rocksdb.RocksDBException; @@ -35,7 +36,6 @@ import java.io.IOException; import java.util.ArrayList; import java.util.List; -import java.util.concurrent.Callable; import static org.apache.hadoop.ozone.OzoneConsts.TRANSACTION_INFO_KEY; import static org.apache.hadoop.ozone.om.OmMetadataManagerImpl.TRANSACTION_INFO_TABLE; @@ -49,10 +49,7 @@ mixinStandardHelpOptions = true, versionProvider = HddsVersionProvider.class ) -public class TransactionInfoRepair implements Callable { - - @CommandLine.Spec - private static CommandLine.Model.CommandSpec spec; +public class TransactionInfoRepair extends RepairTool { @CommandLine.ParentCommand private RDBRepair parent; @@ -67,20 +64,8 @@ public class TransactionInfoRepair implements Callable { description = "Highest index of transactionInfoTable. The input should be non-zero long integer.") private long highestTransactionIndex; - - protected void setHighestTransactionTerm( - long highestTransactionTerm) { - this.highestTransactionTerm = highestTransactionTerm; - } - - protected void setHighestTransactionIndex( - long highestTransactionIndex) { - this.highestTransactionIndex = highestTransactionIndex; - } - - @Override - public Void call() throws Exception { + public void execute() throws Exception { List cfHandleList = new ArrayList<>(); String dbPath = getParent().getDbPath(); List cfDescList = RocksDBUtils.getColumnFamilyDescriptors( @@ -95,7 +80,7 @@ public Void call() throws Exception { TransactionInfo originalTransactionInfo = RocksDBUtils.getValue(db, transactionInfoCfh, TRANSACTION_INFO_KEY, TransactionInfo.getCodec()); - System.out.println("The original highest transaction Info was " + originalTransactionInfo.getTermIndex()); + info("The original highest transaction Info was %s", originalTransactionInfo.getTermIndex()); TransactionInfo transactionInfo = TransactionInfo.valueOf(highestTransactionTerm, highestTransactionIndex); @@ -103,19 +88,17 @@ public Void call() throws Exception { db.get() .put(transactionInfoCfh, StringCodec.get().toPersistedFormat(TRANSACTION_INFO_KEY), transactionInfoBytes); - System.out.println("The highest transaction info has been updated to: " + + info("The highest transaction info has been updated to: %s", RocksDBUtils.getValue(db, transactionInfoCfh, TRANSACTION_INFO_KEY, TransactionInfo.getCodec()).getTermIndex()); } catch (RocksDBException exception) { - System.err.println("Failed to update the RocksDB for the given path: " + dbPath); - System.err.println( + error("Failed to update the RocksDB for the given path: %s", dbPath); + error( "Make sure that Ozone entity (OM) is not running for the give database path and current host."); throw new IOException("Failed to update RocksDB.", exception); } finally { IOUtils.closeQuietly(cfHandleList); } - - return null; } protected RDBRepair getParent() { diff --git a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/repair/om/FSORepairCLI.java b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/repair/om/FSORepairCLI.java index 5a217e9f2de..46af1e847be 100644 --- a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/repair/om/FSORepairCLI.java +++ b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/repair/om/FSORepairCLI.java @@ -18,10 +18,9 @@ package org.apache.hadoop.ozone.repair.om; +import org.apache.hadoop.ozone.repair.RepairTool; import picocli.CommandLine; -import java.util.concurrent.Callable; - /** * Parser for scm.db file. */ @@ -30,7 +29,7 @@ description = "Identify and repair a disconnected FSO tree by marking unreferenced entries for deletion. " + "OM should be stopped while this tool is run." ) -public class FSORepairCLI implements Callable { +public class FSORepairCLI extends RepairTool { @CommandLine.Option(names = {"--db"}, required = true, @@ -55,11 +54,11 @@ public class FSORepairCLI implements Callable { private boolean verbose; @Override - public Void call() throws Exception { + public void execute() throws Exception { if (repair) { - System.out.println("FSO Repair Tool is running in repair mode"); + info("FSO Repair Tool is running in repair mode"); } else { - System.out.println("FSO Repair Tool is running in debug mode"); + info("FSO Repair Tool is running in debug mode"); } try { FSORepairTool @@ -70,9 +69,7 @@ public Void call() throws Exception { } if (verbose) { - System.out.println("FSO repair finished."); + info("FSO repair finished."); } - - return null; } } diff --git a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/repair/quota/QuotaTrigger.java b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/repair/quota/QuotaTrigger.java index daa1f332e3f..2930c873563 100644 --- a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/repair/quota/QuotaTrigger.java +++ b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/repair/quota/QuotaTrigger.java @@ -24,10 +24,10 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; -import java.util.concurrent.Callable; import org.apache.commons.lang3.StringUtils; import org.apache.hadoop.hdds.cli.HddsVersionProvider; import org.apache.hadoop.ozone.om.protocol.OzoneManagerProtocol; +import org.apache.hadoop.ozone.repair.RepairTool; import picocli.CommandLine; /** @@ -39,9 +39,7 @@ mixinStandardHelpOptions = true, versionProvider = HddsVersionProvider.class ) -public class QuotaTrigger implements Callable { - @CommandLine.Spec - private static CommandLine.Model.CommandSpec spec; +public class QuotaTrigger extends RepairTool { @CommandLine.ParentCommand private QuotaRepair parent; @@ -68,20 +66,19 @@ public class QuotaTrigger implements Callable { private String buckets; @Override - public Void call() throws Exception { + public void execute() throws Exception { List bucketList = Collections.emptyList(); if (StringUtils.isNotEmpty(buckets)) { bucketList = Arrays.asList(buckets.split(",")); } - - OzoneManagerProtocol ozoneManagerClient = - parent.createOmClient(omServiceId, omHost, false); - try { - ozoneManagerClient.startQuotaRepair(bucketList); - System.out.println(ozoneManagerClient.getQuotaRepairStatus()); - } catch (Exception ex) { - System.out.println(ex.getMessage()); + + try (OzoneManagerProtocol omClient = parent.createOmClient(omServiceId, omHost, false)) { + info("Triggering quota repair for %s", + bucketList.isEmpty() + ? "all buckets" + : ("buckets " + buckets)); + omClient.startQuotaRepair(bucketList); + info(omClient.getQuotaRepairStatus()); } - return null; } }