/*
* Copyright (c) 2008-2011 EMC Corporation
* All Rights Reserved
*/
package com.emc.storageos.dbutils;
import java.io.File;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.attribute.GroupPrincipal;
import java.nio.file.attribute.PosixFileAttributeView;
import java.nio.file.attribute.UserPrincipal;
import java.nio.file.attribute.UserPrincipalLookupService;
import java.util.ArrayList;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.emc.storageos.dbutils.CommandHandler.*;
/**
* Class provided simple cli for DB, to dump records in user readable format
*/
public class Main {
private static final Logger log = LoggerFactory.getLogger(Main.class);
private enum Command {
LIST,
QUERY,
DELETE,
SHOW_DEPENDENCY,
COUNT,
GET_RECORDS,
GLOBALLOCK,
DUMP_SCHEMA,
DUMP_SECRETKEY,
RESTORE_SECRETKEY,
RECOVER_VDC_CONFIG,
GEOBLACKLIST,
CHECK_DB,
REPAIR_DB,
REBUILD_INDEX,
RUN_MIGRATION_CALLBACK,
DUMP_ORDERS
};
private static final String TYPE_EVENTS = "events";
private static final String TYPE_STATS = "stats";
private static final String TYPE_AUDITS = "audits";
public static final String LIST_LIMIT = "-limit";
public static final String INACTIVE = "-inactive";
public static final String MODIFICATION_TIME = "-mf";
public static final String FILTER = "-filter";
public static final String SORT_BY_URI = "-sortByURI";
public static final String RECOVER_DUMP = "-dump";
public static final String RECOVER_LOAD = "-recover";
public static final String DELETE_FILE = "-file";
public static final String BLACKLIST = "blacklist";
public static final String CF_NAME = "-cf";
private static DBClient _client = null;
private static final String LOG_FILE_PATH = "/opt/storageos/logs/dbutils.log";
private static final String PID_FILE_PATH = "/var/run/storageos/dbutils.pid";
private static final String STORAGEOS_USER = "storageos";
private static final String STORAGEOS_GROUP = "storageos";
private static final String SKIP_MIGRATION_CHECK = "-bypassMigrationCheck";
/**
* Dump usage
*/
private static void usage() {
System.out.printf("Usage: %n");
System.out.printf("\t%s [%s <n>] [%s] [%s] [%s <criterias>] [%s] <Column Family Name>%n",
Command.LIST.name().toLowerCase(), LIST_LIMIT, INACTIVE, MODIFICATION_TIME, FILTER, SORT_BY_URI);
System.out.printf("\t\t%s <n>\t List paginated with a limit of <n>, "
+ "if <n> is missing, default is 100.%n", LIST_LIMIT);
System.out.printf("\t\t%s\t List including inactive=true object ids.%n", INACTIVE);
System.out.printf("\t\t%s\t\t Show the latest modified field of each record.%n", MODIFICATION_TIME);
System.out.printf("\t\t%s <criterias>\t Filter with <criterias>, e.g, -filter resource=\"<resource id>\" -filter pending=true.%n", FILTER);
System.out.printf("\t\t%s\t List will sort by object's URI ID%n", SORT_BY_URI);
System.out.printf("\t%s [%s] <Column Family Name> <id>%n", Command.QUERY.name().toLowerCase(), MODIFICATION_TIME);
System.out.printf("\t\t%s\t\t Show the latest modified field of the record.%n", MODIFICATION_TIME);
System.out.printf("\t%s <%s/%s/%s> <file_prefix> [<YEAR/MONTH/DAY/HOUR>]%n",
Command.LIST.name().toLowerCase(), TYPE_EVENTS, TYPE_STATS, TYPE_AUDITS);
System.out.printf("\t%s [-force] <Column Family Name> <id/-file file_path>%n", Command.DELETE.name().toLowerCase());
System.out
.printf("\t\t%s <file_path>\tEvery single line in this file is an object id, multiple object ids should be separated to different line.%n",
DELETE_FILE);
System.out.printf("\t%s <Column Family Name> [id]%n", Command.SHOW_DEPENDENCY.name().toLowerCase());
System.out.printf("\t\t%s\t\t Print out the exact dependency references for this specific id if exist.%n", "id");
System.out.printf("\t%s [%s] <Column Family Name>%n",
Command.COUNT.name().toLowerCase(), INACTIVE);
System.out.printf("\t\t%s\t Count including inactive object ids.%n", INACTIVE);
System.out.printf("\t%s <%s/%s/%s> <START TIME> <END TIME>[eg:2012/05/18/15]%n",
Command.GET_RECORDS.name().toLowerCase(), "Events", "Stats", "AuditLogs");
System.out.printf("\t%s %s %s %s %s %s%n",
Command.GLOBALLOCK.name().toLowerCase(), "CREATE", "<lock name>", "<owner>", "(<mode>)", "(<timeout>)");
System.out.printf("\t\tNote: For <mode>, could be GL_NodeSvcShared_MODE or GL_VdcShared_MODE(default).%n");
System.out.printf("\t\t : For <timeout>, unit is millisecond and 0 (default) means never expired.%n");
System.out.printf("\t%s %s %s %n",
Command.GLOBALLOCK.name().toLowerCase(), "READ", "<lock name>");
System.out.printf("\t%s %s %s %n",
Command.GLOBALLOCK.name().toLowerCase(), "DELETE", "<lock name>");
System.out.printf("\t%s <schema version> <dump filename>%n",
Command.DUMP_SCHEMA.name().toLowerCase());
System.out.printf("\t%s <dump filename>%n",
Command.DUMP_SECRETKEY.name().toLowerCase());
System.out.printf("\t%s <restore filename>%n",
Command.RESTORE_SECRETKEY.name().toLowerCase());
System.out.printf("\t%s [%s] [%s] Recover Vdc.%n",
Command.RECOVER_VDC_CONFIG.name().toLowerCase(), RECOVER_DUMP, RECOVER_LOAD);
System.out.printf("\t%s [%s] [%s] Geodb blacklist.%n",
Command.GEOBLACKLIST.name().toLowerCase(), "-reset|set", "<vdc short id>");
System.out.printf("\t%s [Column Family Name]\tCheck data consistency of the whole database%n",
Command.CHECK_DB.name().toLowerCase());
System.out.printf("\t\tColumn Family Name\t Only check specific CF.%n");
System.out.printf("\t%s -db|-geodb [-new] [-crossVdc]%n",
Command.REPAIR_DB.name().toLowerCase());
System.out.printf("\t\tNote: %s option can only be executed as %s user%n",
Command.REPAIR_DB.name().toLowerCase(), STORAGEOS_USER);
System.out.printf("\t -bypassMigrationCheck%n");
System.out
.printf("\t\tNote: it's used with other commands together only when migration fail, dbutils still work even migration fail if you pass this option%n");
System.out.printf("\t%s <file_path>%n",
Command.REBUILD_INDEX.name().toLowerCase());
System.out.printf("\t\t Note: use the genereated file to rebuild the index%n");
System.out.printf("\t%s %s <Column Family Name>%n",
Command.REBUILD_INDEX.name().toLowerCase(), CF_NAME);
System.out.printf("\t\t Note: rebuild specific CF.%n");
System.out.printf("\t%s <file_path>%n",
Command.RUN_MIGRATION_CALLBACK.name().toLowerCase());
System.out.printf("\t\t Note: run specified migration callback.%n");
}
/**
* Stop client and exit
*/
private static void stop() {
if (_client != null) {
_client.stop();
}
System.exit(0);
}
private static void deleteDbutilsPidFile() {
File dbutilsPidFile = new File(PID_FILE_PATH);
boolean delStatus = false;
try {
if (dbutilsPidFile.exists()) {
delStatus = dbutilsPidFile.delete();
}
} catch (SecurityException e) {
if (delStatus == false) {
log.warn("Failed to delete dbutils pid file: {}", dbutilsPidFile.getPath());
}
}
}
private static void changeLogFileOwner() throws Exception {
File f = new File(LOG_FILE_PATH);
if (f.exists()) {
PosixFileAttributeView dbutilsLogFile = Files.getFileAttributeView(f.toPath(),
PosixFileAttributeView.class, LinkOption.NOFOLLOW_LINKS);
UserPrincipalLookupService lookupService = FileSystems.getDefault().getUserPrincipalLookupService();
UserPrincipal storageosUser = lookupService.lookupPrincipalByName(STORAGEOS_USER);
GroupPrincipal storageosGroup = lookupService.lookupPrincipalByGroupName(STORAGEOS_GROUP);
dbutilsLogFile.setOwner(storageosUser);
dbutilsLogFile.setGroup(storageosGroup);
}
}
public static void main(String[] args) throws Exception {
deleteDbutilsPidFile();
changeLogFileOwner();
if (args.length == 0) {
usage();
return;
}
boolean skipMigrationCheck = skipMigrationCheck(args);
// it's a hack of passed arg since we already hard-coded
// parameter position in args array.
if (skipMigrationCheck) {
args = removeMigrationCheckArg(args);
}
Command cmd;
try {
cmd = Command.valueOf(args[0].trim().toUpperCase());
} catch (IllegalArgumentException e) {
System.err.println("Invalid command " + args[0]);
usage();
return;
}
if (cmd == Command.REPAIR_DB) {
if (!System.getProperty("user.name").equals(STORAGEOS_USER)) {
System.err.println("Please su to storageos user to do \"db repair\" operation");
System.exit(1);
}
System.exit(CommandHandler.repairDb(args));
}
try {
// Suppress Sonar violation of Lazy initialization of static fields should be synchronized
// This is a CLI application and main method will not be called by multiple threads
_client = new DBClient(skipMigrationCheck); // NOSONAR ("squid:S2444")
CommandHandler handler = null;
boolean result = false;
switch (cmd) {
case LIST:
_client.init();
handler = new ListHandler(args, _client);
break;
case QUERY:
_client.init();
handler = new QueryHandler(args, _client);
break;
case DELETE:
_client.init();
handler = new DeleteHandler(args);
break;
case SHOW_DEPENDENCY:
_client.init();
handler = new DependencyHandler(args);
break;
case COUNT:
_client.init();
handler = new CountHandler(args);
break;
case GET_RECORDS:
_client.init();
handler = new RecordHandler(args);
break;
case GLOBALLOCK:
_client.init();
handler = new GlobalLockHandler(args);
break;
case DUMP_SCHEMA:
_client.init();
handler = new DumpSchemaHandler(args);
break;
case DUMP_SECRETKEY:
_client.init();
handler = new DumpKeyHandler(args);
break;
case RESTORE_SECRETKEY:
_client.init();
handler = new RestoreKeyHandler(args);
break;
case RECOVER_VDC_CONFIG:
_client.init();
handler = new RecoverVdcHandler(args);
break;
case GEOBLACKLIST:
_client.init();
handler = new GeoBlacklistHandler(args);
break;
case CHECK_DB:
_client.init();
handler = new CheckDBHandler(args);
break;
case REBUILD_INDEX:
_client.init();
handler = new RebuildIndexHandler(args);
break;
case RUN_MIGRATION_CALLBACK:
_client.init();
handler = new RunMigrationCallback(args);
break;
case DUMP_ORDERS:
_client.init();
handler = new DumpOrdersHandler(args);
break;
default:
throw new IllegalArgumentException("Invalid command ");
}
handler.process(_client);
} catch (Exception e) {
System.err.println("Exception e=" + e);
log.error("Exception e=", e);
usage();
} finally {
stop();
}
}
private static String[] removeMigrationCheckArg(String[] args) {
List<String> tmpArgs = new ArrayList<String>();
for (String arg : args) {
if (arg != null && arg.equals(SKIP_MIGRATION_CHECK)) {
continue;
}
tmpArgs.add(arg);
}
return tmpArgs.toArray(new String[tmpArgs.size()]);
}
private static boolean skipMigrationCheck(String[] args) {
for (String arg : args) {
if (arg != null && arg.equals(SKIP_MIGRATION_CHECK)) {
return true;
}
}
return false;
}
}