/*
* Copyright 2013 Eediom Inc.
*
* Licensed 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.araqne.logstorage.script;
import java.io.IOException;
import java.text.DecimalFormat;
import java.text.ParseException;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import org.araqne.api.Script;
import org.araqne.api.ScriptArgument;
import org.araqne.api.ScriptContext;
import org.araqne.api.ScriptUsage;
import org.araqne.logstorage.LogTableRegistry;
import org.araqne.logstorage.TableWildcardMatcher;
import org.araqne.logstorage.backup.StorageBackupConfigSpec;
import org.araqne.logstorage.backup.StorageBackupJob;
import org.araqne.logstorage.backup.StorageBackupManager;
import org.araqne.logstorage.backup.StorageBackupMediaFactory;
import org.araqne.logstorage.backup.StorageBackupMediaRegistry;
import org.araqne.logstorage.backup.StorageBackupRequest;
import org.araqne.logstorage.backup.StorageBackupType;
import org.araqne.logstorage.dump.DumpConfigSpec;
import org.araqne.logstorage.dump.DumpDriver;
import org.araqne.logstorage.dump.DumpManifest;
import org.araqne.logstorage.dump.DumpService;
import org.araqne.logstorage.dump.DumpTabletEntry;
import org.araqne.logstorage.dump.ExportRequest;
import org.araqne.logstorage.dump.ExportTask;
import org.araqne.logstorage.dump.ImportRequest;
import org.araqne.logstorage.dump.ImportTask;
/**
* @since 2.3.0
* @author xeraph
*/
public class LogStorageBackupScript implements Script {
private LogTableRegistry tableRegistry;
private StorageBackupManager backupManager;
private StorageBackupMediaRegistry mediaRegistry;
private DumpService dumpService;
private ScriptContext context;
public LogStorageBackupScript(LogTableRegistry tableRegistry, StorageBackupManager backupManager,
StorageBackupMediaRegistry mediaRegistry, DumpService dumpService) {
this.tableRegistry = tableRegistry;
this.backupManager = backupManager;
this.mediaRegistry = mediaRegistry;
this.dumpService = dumpService;
}
@Override
public void setScriptContext(ScriptContext context) {
this.context = context;
}
public void exportTasks(String[] args) {
context.println("Export Tasks");
context.println("--------------");
for (ExportTask task : dumpService.getExportTasks()) {
context.println(task);
}
}
public void importTasks(String[] args) {
context.println("Import Tasks");
context.println("--------------");
for (ImportTask task : dumpService.getImportTasks()) {
context.println(task);
}
}
@ScriptUsage(description = "export data", arguments = { @ScriptArgument(name = "type", type = "string", description = "driver type") })
public void beginExport(String[] args) {
String type = args[0];
try {
DumpDriver driver = dumpService.getDumpDriver(type);
if (driver == null) {
context.println("unknown driver type: " + type);
return;
}
context.print("Tables? ");
Set<String> tableNames = split(context.readLine());
SimpleDateFormat df = new SimpleDateFormat("yyyyMMdd");
context.print("From (yyyyMMdd)? ");
Date from = df.parse(context.readLine().trim());
context.print("To (yyyyMMdd)? ");
Date to = df.parse(context.readLine().trim());
Map<String, String> params = new HashMap<String, String>();
for (DumpConfigSpec spec : driver.getExportSpecs()) {
String value = input(spec);
params.put(spec.getKey(), value);
}
dumpService.beginExport(new ExportRequest("local", tableNames, from, to, params));
context.println("export started");
} catch (InterruptedException e) {
context.println("");
context.println("interrupted");
} catch (ParseException e) {
context.println("invalid date format");
}
}
@ScriptUsage(description = "import data", arguments = { @ScriptArgument(name = "driver", type = "string", description = "driver type") })
public void beginImport(String[] args) {
String type = args[0];
try {
DumpDriver driver = dumpService.getDumpDriver(type);
if (driver == null) {
context.println("unknown driver type: " + type);
return;
}
ImportRequest req = new ImportRequest();
req.setDriverType(type);
for (DumpConfigSpec spec : driver.getImportSpecs()) {
String value = input(spec);
req.getParams().put(spec.getKey(), value);
}
DumpManifest manifest = dumpService.readManifest(type, req.getParams());
long total = 0;
for (DumpTabletEntry e : manifest.getEntries()) {
context.println(e.toString());
total += e.getCount();
}
context.print("Total " + total + " rows. proceed (y/N)? ");
String proceed = context.readLine().trim();
if (!proceed.equalsIgnoreCase("y")) {
context.println("cancelled");
return;
}
req.setEntries(manifest.getEntries());
dumpService.beginImport(req);
context.println("import started");
} catch (InterruptedException e) {
context.println("");
context.println("interrupted");
} catch (IOException e) {
context.println(e.getMessage());
}
}
public void dumpDrivers(String[] args) {
context.println("Dump Drivers");
context.println("--------------");
for (DumpDriver driver : dumpService.getDumpDrivers()) {
context.println(driver.getName(Locale.ENGLISH) + ": " + driver.getDescription(Locale.ENGLISH));
}
}
@ScriptUsage(description = "cancel export", arguments = { @ScriptArgument(name = "guid", type = "string", description = "the guid of export job") })
public void cancelExport(String[] args) {
String guid = args[0];
dumpService.cancelExport(guid);
context.println("cancelled");
}
private Set<String> split(String line) {
Set<String> s = new HashSet<String>();
for (String table : line.split(",")) {
table = table.trim();
if (!table.isEmpty())
s.add(table);
}
return s;
}
public void backup(String[] args) throws InterruptedException, IOException {
SimpleDateFormat df = new SimpleDateFormat("yyyyMMdd");
StorageBackupRequest req = new StorageBackupRequest(StorageBackupType.BACKUP);
BackupProgressPrinter printer = new BackupProgressPrinter(context);
req.setProgressMonitor(printer);
configureMedia(req);
Set<String> tableNames = new HashSet<String>();
context.print("Table names (enter to backup all tables): ");
String tableExpr = context.readLine();
HashSet<String> allTableNames = new HashSet<String>(tableRegistry.getTableNames());
if (tableExpr.isEmpty()) {
tableNames = allTableNames;
} else {
tableNames = TableWildcardMatcher.apply(allTableNames, tableExpr);
}
req.setTableNames(tableNames);
context.print("Range from (yyyyMMdd, enter to unlimited): ");
String fromStr = context.readLine().trim();
Date from = df.parse(fromStr, new ParsePosition(0));
if (!fromStr.isEmpty() && from == null) {
context.println("invalid date format");
return;
}
req.setFrom(from);
context.print("Range to (yyyyMMdd, enter to unlimited): ");
String toStr = context.readLine().trim();
Date to = df.parse(toStr, new ParsePosition(0));
if (!toStr.isEmpty() && to == null) {
context.println("invalid date format");
return;
}
req.setTo(to);
StorageBackupJob job = backupManager.prepare(req);
int tableCount = job.getStorageFiles().keySet().size();
context.println("Total " + tableCount + " tables");
context.println("Requires " + formatNumber(job.getTotalBytes()) + " bytes");
long freeSpace = req.getMedia().getFreeSpace();
if (freeSpace < job.getTotalBytes()) {
context.println("Not enough space on media, Aborted");
return;
}
context.print("Proceed? (y/N): ");
String proceed = context.readLine();
if (!proceed.equalsIgnoreCase("y")) {
context.println("cancelled");
return;
}
backupManager.execute(job);
context.println("started backup job");
try {
while (true) {
context.readLine();
}
} catch (InterruptedException e) {
if (!job.isDone())
context.println("backup will be continued in background");
} finally {
printer.setDisabled(true);
}
}
private void configureMedia(StorageBackupRequest req) throws InterruptedException {
Set<String> mediaTypes = new HashSet<String>();
for (StorageBackupMediaFactory f : mediaRegistry.getFactories()) {
mediaTypes.add(f.getName());
}
context.println("Available backup media types " + mediaTypes);
context.print("Select media type: ");
String type = context.readLine().trim();
if (!mediaTypes.contains(type))
throw new IllegalStateException("no such media: " + type);
StorageBackupMediaFactory mediaFactory = mediaRegistry.getFactory(type);
Map<String, String> mediaConfigs = new HashMap<String, String>();
for (StorageBackupConfigSpec spec : mediaFactory.getConfigSpecs()) {
String displayName = spec.getDisplayNames().get(Locale.ENGLISH);
context.print(displayName + ": ");
mediaConfigs.put(spec.getKey(), context.readLine());
}
req.setMedia(mediaFactory.newMedia(mediaConfigs));
}
public void restore(String[] args) throws InterruptedException, IOException {
StorageBackupRequest req = new StorageBackupRequest(StorageBackupType.RESTORE);
BackupProgressPrinter printer = new BackupProgressPrinter(context);
req.setProgressMonitor(printer);
configureMedia(req);
context.print("Table names (enter to restore all tables): ");
String tableExpr = context.readLine().trim();
Set<String> allTableNames = req.getMedia().getTableNames();
if (tableExpr.isEmpty()) {
req.setTableNames(allTableNames);
} else {
req.setTableNames(TableWildcardMatcher.apply(allTableNames, tableExpr));
}
StorageBackupJob job = backupManager.prepare(req);
int tableCount = job.getMediaFiles().keySet().size();
context.println("Total " + tableCount + " tables");
context.println("Restore " + formatNumber(job.getTotalBytes()) + " bytes");
context.print("Proceed? (y/N): ");
String proceed = context.readLine();
if (!proceed.equalsIgnoreCase("y")) {
context.println("cancelled");
return;
}
backupManager.execute(job);
context.println("started restore job");
try {
while (true) {
context.readLine();
}
} catch (InterruptedException e) {
if (!job.isDone())
context.println("restore will be continued in background");
} finally {
printer.setDisabled(true);
}
}
private String formatNumber(long bytes) {
DecimalFormat formatter = new DecimalFormat("###,###");
return formatter.format(bytes);
}
private String input(DumpConfigSpec spec) throws InterruptedException {
String s = spec.isRequired() ? " (required)? " : " (optional)? ";
String value = null;
while (true) {
context.print(spec.getDisplayName(Locale.ENGLISH) + s);
value = context.readLine();
if (value.trim().isEmpty()) {
if (spec.isRequired())
continue;
return null;
}
break;
}
return value;
}
}