Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HDDS-12001. Create parent class for repair tools #7633

Merged
merged 1 commit into from
Jan 3, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -68,7 +66,7 @@
name = "cert-recover",
description = "Recover Deleted SCM Certificate from RocksDB")
@MetaInfServices(RepairSubcommand.class)
public class RecoverSCMCertificate implements Callable<Void>, RepairSubcommand {
public class RecoverSCMCertificate extends RepairTool implements RepairSubcommand {

@CommandLine.Option(names = {"--db"},
required = true,
Expand All @@ -78,19 +76,8 @@ public class RecoverSCMCertificate implements Callable<Void>, 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 =
Expand All @@ -112,15 +99,15 @@ public Void call() throws Exception {
SecurityConfig securityConfig = new SecurityConfig(parent.getOzoneConf());

Map<BigInteger, X509Certificate> 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;

Expand All @@ -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(
Expand Down Expand Up @@ -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);

Expand All @@ -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);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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<Void> {

/** 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;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,14 @@
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;
import org.rocksdb.RocksDBException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import picocli.CommandLine;
import picocli.CommandLine.Model.CommandSpec;

import java.io.IOException;
import java.util.ArrayList;
Expand All @@ -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;
Expand All @@ -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<Void> {
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;

Expand All @@ -84,15 +80,15 @@ public class SnapshotRepair implements Callable<Void> {
private boolean dryRun;

@Override
public Void call() throws Exception {
public void execute() throws Exception {
List<ColumnFamilyHandle> cfHandleList = new ArrayList<>();
List<ColumnFamilyDescriptor> 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(),
Expand All @@ -102,62 +98,60 @@ 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
// previous exist and after the update snapshot does not point to ghost snapshot.
Set<UUID> 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<UUID> getSnapshotIdSet(ManagedRocksDB db, ColumnFamilyHandle snapshotInfoCfh)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -49,10 +49,7 @@
mixinStandardHelpOptions = true,
versionProvider = HddsVersionProvider.class
)
public class TransactionInfoRepair implements Callable<Void> {

@CommandLine.Spec
private static CommandLine.Model.CommandSpec spec;
public class TransactionInfoRepair extends RepairTool {

@CommandLine.ParentCommand
private RDBRepair parent;
Expand All @@ -67,20 +64,8 @@ public class TransactionInfoRepair implements Callable<Void> {
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<ColumnFamilyHandle> cfHandleList = new ArrayList<>();
String dbPath = getParent().getDbPath();
List<ColumnFamilyDescriptor> cfDescList = RocksDBUtils.getColumnFamilyDescriptors(
Expand All @@ -95,27 +80,25 @@ 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);

byte[] transactionInfoBytes = TransactionInfo.getCodec().toPersistedFormat(transactionInfo);
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() {
Expand Down
Loading
Loading