// $Id: PoolStatisticsV0.java,v 1.12 2006-12-15 15:45:40 tigran Exp $Cg
package diskCacheV111.services;
import com.google.common.escape.Escaper;
import com.google.common.escape.Escapers;
import com.google.common.net.UrlEscapers;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Comparator;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.TreeMap;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;
import diskCacheV111.poolManager.PoolManagerCellInfo;
import diskCacheV111.util.CacheException;
import dmg.cells.nucleus.CellAdapter;
import dmg.cells.nucleus.CellAddressCore;
import dmg.cells.nucleus.CellMessage;
import dmg.cells.nucleus.CellNucleus;
import dmg.cells.nucleus.CellPath;
import dmg.cells.nucleus.NoRouteToCellException;
import dmg.util.CellCron;
import org.dcache.cells.CellStub;
import org.dcache.util.Args;
import static java.util.Arrays.asList;
import static org.dcache.util.ByteUnit.KiB;
import static org.dcache.util.ByteUnit.BYTES;
/**
* @author Patrick Fuhrmann
*
* PoolStatisticsV0 collects statistical information from the billing cell and
* the connected pools and prepares a raw statistics file for further processing.
* As an optional second step, PoolStatisticsV0 converts the raw statistics
* file into a tree of html pages.
*
*
* BASIC steps :
*
* Step 1 :
* The PoolManager is queried for the name of currently active Pools
* Step 2 :
* Those pools are queried for the 'per class' statistics information.
* Step 3 :
* The billing cell is asked for the data flow data per pool and class.
* Step 4 :
* This information is merged and stored as
* lt;year>-<month>-<day>-<hour>.raw and
* lt;year>-<month>-<day>-day.raw
* in
* <dbBase>/<year>/<month>/<day>
* Step 5 :
* If available, the 'raw' files of today and yesterday are merged
* and stored as
* lt;year>-<month>-<day>-day.drw
* into the same directory.
*
*
* HTML steps :
*
*
* <base>/<year>/<month>/<day>/class-<class>.html
* <base>/<year>/<month>/<day>/pool-<pool>.html
* <base>/<year>/<month>/<day>/pools.html
* <base>/<year>/<month>/<day>/classes.html
* <base>/<year>/<month>/<day>/total.drw
* <base>/<year>/<month>/<day>/index.html
* <base>/<year>/<month>/<day>/total.raw # sum of(total.drw)
*
* updateHtmlMonth
* ----------------
* <base>/<year>/<month>/index.html
* <base>/<year>/<month>/total.raw # sum of float of xx/total.raw and av. of pool of xx/total.raw
*
*
* first rows are of pool and storage group
*
* repository at beginning and end of the day
*
* 0 bytes on this pool of this storage group of beginning of this day
* 1 number of files on this pool of this sg of beginning of this day
* 2 bytes on this pool of this storage group of end of this day
* 3 number of files on this pool of this sg of end of this day
*
* actions during the day
*
* 4 total number of transfer (in and out client <-> dCache)
* 5 total number of restores (hsm -> dCache)
* 6 total number of stores (dCache -> HSM)
* 7 total number of errors
*
* 8 total bytes transferred into the dCache (client -> dCache)
* 9 total bytes transferred out of the dCache (dCache -> client)
* 10 total bytes transferred from HSM into the dCache
* 11 total bytes transferred into the HSM from the dCache
*
*
*/
public class PoolStatisticsV0 extends CellAdapter implements CellCron.TaskRunnable {
private static final Logger _log = LoggerFactory.getLogger(PoolStatisticsV0.class);
/*
* Magic spells to get the infos out of the different cells.
*/
private static final String GET_REP_STATISTICS = "rep ls -s -binary";
private static final String GET_CELL_INFO = "xgetcellinfo";
private static final String GET_POOL_STATISTICS = "get pool statistics";
private static final String RESET_POOL_STATISTICS = "clear pool statistics";
/*
* Definition of the counter values from 'billing' cell.
*/
private static final int YESTERDAY = 0;
private static final int TODAY = 2;
private static final int TRANSFER_IN = 8;
private static final int TRANSFER_OUT = 9;
private static final int RESTORE = 10;
private static final int STORE = 11;
private static final Escaper AS_FILENAME = Escapers.builder().
addEscape('^', "^o^").
addEscape('/', "^s^").
build();
private static final Escaper AS_PATH_ELEMENT = UrlEscapers.urlPathSegmentEscaper();
private static final String DEFAULT_AUTHOR = "© dCache.org ";
private static final ThreadLocal<SimpleDateFormat> _pathFromDate =
new ThreadLocal<SimpleDateFormat>()
{
@Override
protected SimpleDateFormat initialValue()
{
return new SimpleDateFormat(
"yyyy" + File.separator + "MM" + File.separator + "dd" + File.separator + "yyyy-MM-dd-HH'.raw'");
}
};
private static final ThreadLocal<SimpleDateFormat> _dayPathFromDate =
new ThreadLocal<SimpleDateFormat>()
{
@Override
protected SimpleDateFormat initialValue()
{
return new SimpleDateFormat(
"yyyy" + File.separator + "MM" + File.separator + "dd" + File.separator + "yyyy-MM-dd-'day.raw'");
}
};
private static final ThreadLocal<SimpleDateFormat> _dayDiffPathFromDate =
new ThreadLocal<SimpleDateFormat>()
{
@Override
protected SimpleDateFormat initialValue()
{
return new SimpleDateFormat(
"yyyy" + File.separator + "MM" + File.separator + "dd" + File.separator + "yyyy-MM-dd-'day.drw'");
}
};
private static final ThreadLocal<SimpleDateFormat> _htmlPathFromDate =
new ThreadLocal<SimpleDateFormat>()
{
@Override
protected SimpleDateFormat initialValue()
{
return new SimpleDateFormat("yyyy" + File.separator + "MM" + File.separator + "dd");
}
};
private static final ThreadLocal<SimpleDateFormat> _yearOfCalendar =
new ThreadLocal<SimpleDateFormat>()
{
@Override
protected SimpleDateFormat initialValue()
{
return new SimpleDateFormat("yyyy");
}
};
private static final ThreadLocal<SimpleDateFormat> _monthOfCalendar =
new ThreadLocal<SimpleDateFormat>()
{
@Override
protected SimpleDateFormat initialValue()
{
return new SimpleDateFormat("MMM MM yyyy");
}
};
private static final ThreadLocal<SimpleDateFormat> _dayOfCalendar =
new ThreadLocal<SimpleDateFormat>()
{
@Override
protected SimpleDateFormat initialValue()
{
return new SimpleDateFormat("MM/dd yyyy (EEE)");
}
};
private static final ThreadLocal<SimpleDateFormat> _dayOfCalendarKey =
new ThreadLocal<SimpleDateFormat>()
{
@Override
protected SimpleDateFormat initialValue()
{
return new SimpleDateFormat("EEE-MM/dd-yyyy");
}
};
private static String _domainName = "dCache.Unknown.Org";
private static String _bodyString = "<body bgcolor=white>";
private CellStub _poolManager;
private CellStub _billing;
private CellStub _poolStub;
private final CellNucleus _nucleus;
private CellCron.TimerTask _hourly;
private File _dbBase;
private File _htmlBase;
private Map<String,Map<String,long[]>> _recentPoolStatistics;
private boolean _createHtmlTree = true;
private Thread _cronTimer;
public PoolStatisticsV0(String name, String args)
{
super(name, args);
_nucleus = getNucleus();
}
@Override
protected void starting() throws Exception
{
Args args = getArgs();
if (args.argc() < 1) {
throw new
IllegalArgumentException(
"Usage : ... <baseDirectory> " +
"[-htmlBase=<htmlBase>|none] [-create] [-images=<images>]");
}
_poolManager = new CellStub(this, new CellPath(args.getOption("poolManager")),
TimeUnit.MILLISECONDS.convert(args.getLongOption("poolManagerTimeout"),
TimeUnit.valueOf(args.getOption("poolManagerTimeoutUnit"))));
_billing = new CellStub(this, new CellPath(args.getOption("billing")),
TimeUnit.MILLISECONDS.convert(args.getLongOption("billingTimeout"),
TimeUnit.valueOf(args.getOption("billingTimeoutUnit"))));
_poolStub = new CellStub(this, null, TimeUnit.MILLISECONDS.convert(args.getLongOption("poolTimeout"),
TimeUnit.valueOf(args.getOption("poolTimeoutUnit"))));
_htmlBase = _dbBase = new File(args.argv(0));
String tmp = args.getOpt("htmlBase");
if ((tmp != null) && (!tmp.isEmpty())) {
if (tmp.equals("none")) {
_createHtmlTree = false;
} else {
_htmlBase = new File(tmp);
}
}
tmp = args.getOpt("domain");
if (tmp != null) {
_domainName = tmp;
}
if (args.hasOption("create")) {
if (!_dbBase.exists()) {
//noinspection ResultOfMethodCallIgnored
_dbBase.mkdirs();
}
if (_createHtmlTree && !_htmlBase.exists()) {
//noinspection ResultOfMethodCallIgnored
_htmlBase.mkdirs();
}
} else {
if ((!_dbBase.exists()) || (_createHtmlTree && !_htmlBase.exists())) {
throw new IllegalArgumentException("Either <baseDirectory> or <htmlBase> doesn't exist");
}
}
CellCron _cron = new CellCron();
_cronTimer = _nucleus.newThread(_cron, "Cron");
_hourly = _cron.add(55, this, "Hour");
}
@Override
protected void started()
{
_cronTimer.start();
}
@Override
protected void stopping()
{
if (_cronTimer != null) {
_cronTimer.interrupt();
try {
_cronTimer.join();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
@Override
public void getInfo(PrintWriter pw) {
String _images = "/images";
pw.println(" Cell Name : "+getCellName());
pw.println(" Cell Class : "+this.getClass().getName());
pw.println(" Stat Base : "+_dbBase);
pw.println(" Html Base : "+_htmlBase);
pw.println(" Images : "+ _images);
pw.println(" Next Run : "+_hourly);
}
@Override
public void messageArrived(CellMessage message) {}
private String getNiceDayOfCalendar(Calendar calendar) {
return _dayOfCalendar.get().format(calendar.getTime());
}
private File getCurrentPath(Calendar calendar) {
return new File(_dbBase, _pathFromDate.get().format(calendar.getTime()));
}
private File getTodayPath(Calendar calendar) {
return new File(_dbBase, _dayPathFromDate.get().format(calendar.getTime()));
}
private File getTodayDiffPath(Calendar calendar) {
return new File(_dbBase, _dayDiffPathFromDate.get().format(calendar.getTime()));
}
private File getYesterdayPath(Calendar calendar) {
calendar = (Calendar)calendar.clone();
calendar.set(Calendar.DAY_OF_YEAR,calendar.get(Calendar.DAY_OF_YEAR)-1);
return getTodayPath(calendar);
}
private File getHtmlPath(Calendar calendar) {
return new File(_htmlBase, _htmlPathFromDate.get().format(calendar.getTime()));
}
private void copyFile(File from, File to) throws IOException {
try (InputStream in = new FileInputStream(from)) {
try (OutputStream out = new FileOutputStream(to)) {
byte [] buffer = new byte[KiB.toBytes(16)];
for (int rc = in.read(buffer, 0, buffer.length); rc > 0; rc = in.read(buffer, 0, buffer.length)) {
out.write(buffer, 0, rc);
}
}
}
}
public void createDiffFile(File today, File yesterday, File resultFile) throws IOException {
DataStore todayStore = new DataStore(today);
Map<String, long[]> todayMap = todayStore.getLinearMap();
Map<String, long[]> yesterdayMap = new DataStore(yesterday).getLinearMap();
Map<String, long[]> resultMap = new HashMap<>();
for (Map.Entry<String, long[]> entry : todayMap.entrySet()) {
String key = entry.getKey();
long[] counter = entry.getValue();
long[] result = new long[12];
long[] yesterdayCounter = yesterdayMap.get(key);
if (yesterdayCounter == null) {
result[0] = -1;
result[1] = -1;
} else {
result[0] = yesterdayCounter[0];
result[1] = yesterdayCounter[1];
}
System.arraycopy(counter, 0, result, 2, 10);
resultMap.put(key, result);
}
//noinspection ResultOfMethodCallIgnored
resultFile.delete();
try (PrintWriter pw = new PrintWriter(new FileWriter(resultFile))) {
pw.println("#");
Iterator<Map.Entry<String,Object>> it = todayStore.iterator();
it.forEachRemaining(entry -> pw.println("# "+entry.getKey()+"="+entry.getValue()));
pw.println("#");
resultMap.entrySet().forEach(entry -> {
pw.print(entry.getKey());
for (long l : entry.getValue()) {
pw.print(" ");
pw.print(l);
}
pw.println("");
});
}
}
private class HourlyRunner implements Runnable {
private final Calendar _calendar;
private HourlyRunner(Calendar calendar) {
_calendar = calendar;
_nucleus.newThread(this,"FreeRunner").start();
}
@Override
public void run() {
File path = getCurrentPath(_calendar);
File parent = path.getParentFile();
if (!parent.exists()) {
//noinspection ResultOfMethodCallIgnored
parent.mkdirs();
}
try {
_log.info("Starting hourly run for : "+path);
createHourlyRawFile(path, _calendar);
_log.info("Hourly run finished for : "+path);
File today = getTodayPath(_calendar);
_log.info("Creating daily file : "+today);
//noinspection ResultOfMethodCallIgnored
today.delete();
copyFile(path, today);
_log.info("Daily file done : "+today);
File yesterday = getYesterdayPath(_calendar);
if (yesterday.exists()) {
File diffFile = getTodayDiffPath(_calendar);
_log.info("Starting diff run for : "+yesterday);
createDiffFile(today, yesterday, diffFile);
_log.info("Finishing diff run for : "+diffFile);
}
if (_calendar.get(Calendar.HOUR_OF_DAY) == 23) {
resetBillingStatistics();
}
} catch(Exception ee) {
_log.warn("Exception in full run for : "+path, ee);
//noinspection ResultOfMethodCallIgnored
path.delete();
}
if (!_createHtmlTree) {
return;
}
try {
_log.info("Creating html tree");
prepareDailyHtmlFiles(_calendar);
if (_calendar.get(Calendar.HOUR_OF_DAY) == 23) {
updateHtmlMonth(_calendar);
updateHtmlYear(_calendar);
updateHtmlTop();
}
} catch(Exception eee) {
_log.warn("Exception in creating html tree for : "+path, eee);
}
}
}
@Override
public void run(CellCron.TimerTask task) {
if (task == _hourly) {
_log.info("Hourly ticker : "+ new Date());
Calendar calendar = (Calendar)task.getCalendar().clone();
new HourlyRunner(calendar);
task.repeatNextHour();
}
}
private void updateHtmlMonth(Calendar calendar) throws IOException {
File dir = getHtmlPath(calendar).getParentFile();
//noinspection ResultOfMethodCallIgnored
dir.mkdirs();
File [] list = dir.listFiles(new MonthFileFilter());
list = resortFileList(list, -1);
long [] counter = null;
long [] total = new long[12];
long [] lastInMonth = new long[12];
BaseStatisticsHtml html = new BaseStatisticsHtml();
html.setSorted(false);
html.setTitle(_monthOfCalendar.get().format(calendar.getTime()));
html.setKeyType("Date");
html.setAuthor(DEFAULT_AUTHOR);
html.setHeader(new MonthDirectoryHeader());
for(int i = 0, n = list.length; i < n; i++) {
File x = new File(list[i], "total.raw");
if (! x.exists()) {
continue;
}
try (BufferedReader br = new BufferedReader(new FileReader(x))) {
String line = br.readLine();
if ((line == null) || (line.length() < 12)) {
continue;
}
StringTokenizer st = new StringTokenizer(line);
st.nextToken();
String key = _dayOfCalendar.get().format(new Date(Long.parseLong(st.nextToken())));
counter = new long[12];
for(int j = 0; j < counter.length; j++) {
counter[j] = Long.parseLong(st.nextToken());
}
if (i == 0) {
System.arraycopy(counter, 0, lastInMonth, 0, counter.length);
}
add(total, counter);
html.add(key, AS_PATH_ELEMENT.escape(list[i].getName())+"/index.html", counter);
} catch(IOException ignored) {}
}
try (PrintWriter pw = new PrintWriter(new FileWriter(new File(dir, "index.html")))) {
html.setPrintWriter(pw);
html.dump();
}
if (counter != null) {
total[YESTERDAY] = counter[YESTERDAY];
total[YESTERDAY+1] = counter[YESTERDAY+1];
}
total[TODAY] = lastInMonth[TODAY];
total[TODAY+1] = lastInMonth[TODAY+1];
printTotal(new File(dir, "total.raw"), total, calendar.getTime());
}
private void updateHtmlYear(Calendar calendar) throws IOException {
File dir = getHtmlPath(calendar).getParentFile().getParentFile();
//noinspection ResultOfMethodCallIgnored
dir.mkdirs();
File [] list = dir.listFiles(new MonthFileFilter());
list = resortFileList(list, -1 );
long [] counter = null;
long [] total = new long[12];
long [] lastInMonth = new long[12];
BaseStatisticsHtml html = new BaseStatisticsHtml();
html.setSorted(false);
html.setTitle(_yearOfCalendar.get().format(calendar.getTime()));
html.setKeyType("Date");
html.setAuthor(DEFAULT_AUTHOR);
html.setHeader(new YearDirectoryHeader());
for(int i = 0, n = list.length; i < n; i++) {
File x = new File(list[i], "total.raw");
if (!x.exists()) {
continue;
}
try (BufferedReader br = new BufferedReader(new FileReader(x))) {
String line = br.readLine();
if ((line == null) || (line.length() < 12)) {
continue;
}
StringTokenizer st = new StringTokenizer(line);
st.nextToken();
String key = _monthOfCalendar.get().format(new Date(Long.parseLong(st.nextToken())));
counter = new long[12];
for(int j = 0; j < counter.length; j++) {
counter[j] = Long.parseLong(st.nextToken());
}
if (i == 0) {
System.arraycopy(counter, 0, lastInMonth, 0, counter.length);
}
add(total, counter);
html.add(key, AS_PATH_ELEMENT.escape(list[i].getName())+"/index.html", counter);
} catch(IOException ignored) {}
}
try (PrintWriter pw = new PrintWriter(new FileWriter(new File(dir, "index.html")))) {
html.setPrintWriter(pw);
html.dump();
}
if (counter != null) {
total[YESTERDAY] = counter[YESTERDAY];
total[YESTERDAY+1] = counter[YESTERDAY+1];
}
total[TODAY] = lastInMonth[TODAY];
total[TODAY+1] = lastInMonth[TODAY+1];
printTotal(new File(dir, "total.raw"), total, calendar.getTime());
}
private void updateHtmlTop() throws IOException {
File dir = _htmlBase;
//noinspection ResultOfMethodCallIgnored
dir.mkdirs();
File [] list = dir.listFiles(new YearFileFilter());
list = resortFileList(list, -1 );
long [] counter = null;
long [] total = new long[12];
long [] lastInYear = new long[12];
BaseStatisticsHtml html = new BaseStatisticsHtml();
html.setSorted(false);
html.setTitle("dCache Statistics of dCache Domain : "+_domainName);
html.setKeyType("Year");
html.setAuthor(DEFAULT_AUTHOR);
html.setHeader(new TopDirectoryHeader());
for(int i = 0, n = list.length; i < n; i++) {
File x = new File(list[i], "total.raw");
if (! x.exists()) {
continue;
}
try (BufferedReader br = new BufferedReader(new FileReader(x))) {
String line = br.readLine();
if ((line == null) || (line.length() < 12)) {
continue;
}
StringTokenizer st = new StringTokenizer(line);
st.nextToken();
String key = _yearOfCalendar.get().format(new Date(Long.parseLong(st.nextToken())));
counter = new long[12];
for(int j = 0; j < counter.length; j++) {
counter[j] = Long.parseLong(st.nextToken());
}
if (i == 0) {
System.arraycopy(counter, 0, lastInYear, 0, counter.length);
}
add(total, counter);
html.add(key, list[i].getName()+File.separator+"index.html", counter);
} catch(IOException ignored) {}
}
try (PrintWriter pw = new PrintWriter(new FileWriter(new File(dir, "index.html")))) {
html.setPrintWriter(pw);
html.dump();
}
if (counter != null) {
total[YESTERDAY] = counter[YESTERDAY];
total[YESTERDAY+1] = counter[YESTERDAY+1];
}
total[TODAY] = lastInYear[TODAY];
total[TODAY+1] = lastInYear[TODAY+1];
printTotal(new File(dir, "total.raw"), total, new Date());
}
private File[] resortFileList(File[] list, int direction) {
Comparator<File> comparator = (f1, f2) -> direction * f1.getName().compareTo(f2.getName());
return asList(list).stream().sorted(comparator).toArray(File[]::new);
}
private static class MonthFileFilter implements FileFilter {
@Override
public boolean accept(File file) {
return file.isDirectory() && (file.getName().length() == 2);
}
}
private static class YearFileFilter implements FileFilter {
@Override
public boolean accept(File file) {
return file.isDirectory() && (file.getName().length() == 4);
}
}
@SuppressWarnings("unused")
public static final String hh_set_html_body = "<bodyString>; eg: \"<body background=/images/bg.svg>\"";
@SuppressWarnings("unused")
public String ac_set_html_body_$_1(Args args) {
_bodyString = args.argv(0);
return "";
}
@SuppressWarnings("unused")
public static final String hh_create_html = "[<year> [<month> [[<day>]]]";
@SuppressWarnings("unused")
public String ac_create_html_$_0_3(Args args)throws IOException {
if (args.argc() == 3) {
int year = Integer.parseInt(args.argv(0));
int month = Integer.parseInt(args.argv(1));
int day = Integer.parseInt(args.argv(2));
Calendar calendar = new GregorianCalendar();
calendar.set(Calendar.YEAR , year);
calendar.set(Calendar.MONTH , month - 1);
calendar.set(Calendar.DAY_OF_MONTH, day);
prepareDailyHtmlFiles(calendar);
} else if (args.argc() == 2) {
int year = Integer.parseInt(args.argv(0));
int month = Integer.parseInt(args.argv(1));
Calendar calendar = new GregorianCalendar();
calendar.set(Calendar.YEAR , year);
calendar.set(Calendar.MONTH , month - 1);
calendar.set(Calendar.DAY_OF_MONTH, 1);
updateHtmlMonth(calendar);
} else if (args.argc() == 1) {
int year = Integer.parseInt(args.argv(0));
Calendar calendar = new GregorianCalendar();
calendar.set(Calendar.YEAR , year);
calendar.set(Calendar.MONTH , 1);
calendar.set(Calendar.DAY_OF_MONTH, 1);
updateHtmlYear(calendar);
} else if (args.argc() == 0) {
updateHtmlTop();
}
return "Done";
}
@SuppressWarnings("unused")
public static final String hh_create_stat = "[<outputFileName>]";
@SuppressWarnings("unused")
public String ac_create_stat_$_0_1(Args args) {
if (args.argc() == 0) {
_nucleus.newThread(
new Runnable() {
@Override
public void run() {
try {
_log.info("Starting internal Manual run");
synchronized(this) { _recentPoolStatistics = null; }
Map<String,Map<String,long[]>> map = createStatisticsMap();
synchronized(this) { _recentPoolStatistics = map; }
_log.info("Finishing internal Manual run");
} catch(Exception e) {
_log.info("Aborting internal Manual run "+e);
}
}
},
"internal"
).start();
return "Thread started for internal run";
} else {
final File file = new File(args.argv(0));
_nucleus.newThread(() -> {
try {
_log.info("Starting Manual run for file : "+file);
createHourlyRawFile(file, new GregorianCalendar());
_log.info("Finishing Manual run for file : "+file);
} catch(Exception e) {
_log.info("Aborting Manual run for file : "+file+" "+e);
}
}, file.toString()).start();
return "Thread started for : "+args.argv(0);
}
}
private interface Iteratable {
void mapEntry(String pool, String className, long[] counters);
}
private interface HtmlDrawable {
void draw(PrintWriter pw);
}
private static class PatternIterator implements Iteratable {
private final Pattern _pattern;
private final StringBuffer _sb;
private final long [] _sum = new long[16];
private int _mx;
private PatternIterator(Pattern pattern) {
_pattern = pattern;
_sb = new StringBuffer();
}
@Override
public void mapEntry(String poolName, String className, long [] counters) {
StringBuilder sb = new StringBuilder();
sb.append(poolName).append(" ").append(className).append(" ");
for (long counter : counters) {
sb.append(counter);
sb.append(" ");
}
String line = sb.toString();
if (! _pattern.matcher(line).matches()) {
return;
}
_sb.append(line).append("\n");
for(int i = 0, n = counters.length, m = _sum.length; (i < n) && (i < m); i++) {
if (_sum[i] > -1) {
_sum[i] += counters[i];
}
}
_mx = Math.max(_mx, counters.length);
}
public long [] getCounters() {
long [] result = new long[_mx];
System.arraycopy(_sum, 0, result, 0, _mx);
return result;
}
public String toString() { return _sb.toString(); }
public StringBuffer getStringBuffer() { return _sb; }
}
@SuppressWarnings("unused")
public static final String hh_show = "[<pattern>]";
@SuppressWarnings("unused")
public String ac_show_$_0_1(Args args) {
Map<String,Map<String,long[]>> map;
synchronized(this) { map = _recentPoolStatistics; }
if (map == null ) {
return "Pool Statistics not available yet";
} else if (args.argc() == 0) {
StringBuffer st = new StringBuffer();
dumpStatistics(map, st);
return st.toString();
}
Pattern p = Pattern.compile(args.argv(0));
PatternIterator pi = new PatternIterator(p);
dumpStatistics(map, pi);
StringBuffer sb = pi.getStringBuffer();
long [] counters = pi.getCounters();
sb.append("* *");
for (long counter : counters) {
sb.append(" ").append(counter);
}
return sb.toString();
}
private Map<String,Map<String,long[]>> createStatisticsMap() throws InterruptedException, IOException, NoRouteToCellException {
Map<String,Map<String,long[]>> pool = getPoolRepositoryStatistics();
Map<String,Map<String,long[]>> billing = getBillingStatistics();
return mergeStorageClassMaps(pool, billing);
}
private static class DataStore {
private Map<String,Map<String,long[]>> _data;
private final Map<String,Object> _attributes = new HashMap<>();
private int _minCount = 64;
private int _maxCount;
public DataStore(Map<String,Map<String,long[]>> data) {
_data = data;
}
public DataStore(File file) throws IOException {
load(file);
}
public Map<String, long[]> getLinearMap() {
final Map<String, long[]> map = new HashMap<>();
dumpStatistics(_data, (pool, className, counters) -> map.put(pool + " " + className, counters));
return map;
}
public Iterator<Map.Entry<String,Object>> iterator() { return _attributes.entrySet().iterator(); }
public void setTime(Date date) {
add("timestamp", date.getTime());
add("date" , date);
}
private void add(String key, Object value) {
_attributes.put(key, value);
}
public Map<String,Map<String,long[]>> getMap() { return _data; }
public void store(File file) throws IOException {
try (PrintWriter pw = new PrintWriter(new FileWriter(file))) {
pw.println("#");
for (Map.Entry<String, Object> entry : _attributes.entrySet()) {
pw.println("# " + entry.getKey() + "=" + entry.getValue());
}
pw.println("#");
dumpStatistics(_data, pw);
}
}
private void scanAttributes(String line ) {
if (line.length() < 3) {
return;
}
line = line.substring(2).trim();
if (line.isEmpty()) {
return;
}
int indx = line.indexOf('=');
if ((indx == -1) || (indx == (line.length() - 1))) {
_attributes.put(line,"");
} else if (indx != 0) {
_attributes.put(line.substring(0,indx).trim(),
line.substring(indx+1).trim());
}
}
private void load(File f) throws IOException {
HashMap<String,Map<String,long[]>> map = new HashMap<>();
try (BufferedReader br = new BufferedReader(new FileReader(f))) {
for (String line = br.readLine(); line != null; line = br.readLine()) {
if ((!line.isEmpty()) && (line.charAt(0) == '#')) {
scanAttributes(line);
continue;
}
StringTokenizer st = new StringTokenizer(line);
if (!st.hasMoreTokens()) {
continue;
}
String poolName = st.nextToken();
if (!st.hasMoreTokens()) {
continue;
}
String className = st.nextToken();
long [] counter = new long[64];
int i = 0;
for(int n = counter.length; (i < n) && st.hasMoreTokens(); i++) {
counter[i] = Long.parseLong(st.nextToken());
}
_minCount = Math.min(_minCount, i);
_maxCount = Math.max(_maxCount, i);
long [] res = new long[i];
System.arraycopy(counter, 0, res, 0, i);
Map<String,long[]> classMap = map.get(poolName);
if (classMap == null) {
map.put(poolName, classMap = new HashMap<>());
}
classMap.put(className, res);
}
_data = map;
}
}
}
private void createHourlyRawFile(File outputFile, Calendar calendar)
throws InterruptedException, IOException, NoRouteToCellException {
if (outputFile.exists() || !outputFile.getParentFile().canWrite()) {
throw new IOException("File exists or directory not writable : " + outputFile);
}
DataStore store = new DataStore(createStatisticsMap());
store.setTime(calendar.getTime());
store.store(outputFile);
}
private static void dumpStatistics(Map<String, Map<String, long[]>> result, Iteratable mapentry) {
for (Map.Entry<String, Map<String, long[]>> entry : result.entrySet()) {
String poolName = entry.getKey();
Map<String, long[]> classes = entry.getValue();
for (Map.Entry<String, long[]> classEntry : classes.entrySet()) {
String className = classEntry.getKey();
long[] values = classEntry.getValue();
mapentry.mapEntry(poolName, className, values);
}
}
}
private void printIndex(File file, String title) throws IOException {
try (PrintWriter pw = new PrintWriter(new FileWriter(file))) {
pw.println("<html><head><title>" + title + "</title></head>");
pw.println(_bodyString);
pw.print("<center><h2>");
pw.print(_domainName);
pw.println("</h2></center>");
pw.println("<hr>");
pw.print("<pre> <a href=\"/docs/statisticsHelp.html\">Help</a>");
pw.print(" <a href=\"/\">Birds Home</a>");
pw.print(" <a href=\"../../../index.html\">Top</a>");
pw.print(" <a href=\"../../index.html\">This Year</a>");
pw.println(" <a href=\"../index.html\">This Month</a></pre>");
pw.println("<hr>");
pw.println("<center><h1>" + title + "</h1></center>");
pw.println("<center>");
pw.println("<h3><a href=\"pools.html\">Pools</a></h3>");
pw.println("<h3><a href=\"classes.html\">Classes</a></h3>");
pw.println("<h3><a href=\"total.drw\">Raw</a></h3>");
pw.println("</center>");
pw.println("</body></html>");
}
}
private void prepareDailyHtmlFiles(Calendar calendar) {
File diffFile = getTodayDiffPath(calendar);
if (! diffFile.exists()) {
_log.warn("prepareDailyHtmlFiles : File not found : "+diffFile);
return;
}
File dir = getHtmlPath(calendar);
if (! dir.exists()) {
//noinspection ResultOfMethodCallIgnored
dir.mkdirs();
}
try {
// copy the raw file into the html directory
copyFile(diffFile, new File(dir, "total.drw"));
// load the raw data file
Map<String,Map<String,long[]>> map = new DataStore(diffFile).getMap();
// create todays html files
prepareDailyHtml(map, dir, calendar.getTime());
// prepare the daily index.html file
printIndex(new File(dir, "index.html"), getNiceDayOfCalendar(calendar));
} catch(IOException ee) {
_log.warn("Can't prepare Html directory : "+dir, ee);
}
}
private Map<String, Map<String, long[]>>[] prepareDailyHtml(
Map<String, Map<String, long[]>> poolMap,
File pathBase,
Date date
) throws IOException {
Iterator<Map.Entry<String,Map<String,long[]>>> entries = poolMap.entrySet().iterator();
Map<String,Map<String,long[]>> classMap2 = new HashMap<>();
DayDirectoryHeader header = new DayDirectoryHeader(date, _dayOfCalendar.get());
BaseStatisticsHtml allPoolsHtml = new BaseStatisticsHtml();
allPoolsHtml.setSorted(true);
allPoolsHtml.setTitle("Pools");
allPoolsHtml.setKeyType("PoolNames");
allPoolsHtml.setAuthor(DEFAULT_AUTHOR);
allPoolsHtml.setHeader(header);
long[] total = null;
while(entries.hasNext()) {
Map.Entry<String, Map<String, long[]>> entry = entries.next();
String poolName = entry.getKey();
Map<String, long[]> classMap = entry.getValue();
Iterator<Map.Entry<String, long[]>> classEntries =
classMap.entrySet().iterator();
long[] sum = null;
BaseStatisticsHtml html = new BaseStatisticsHtml();
html.setSorted(true);
html.setTitle("Pool : "+poolName);
html.setKeyType("ClassNames");
html.setAuthor(DEFAULT_AUTHOR);
html.setHeader(header);
while(classEntries.hasNext()) {
Map.Entry<String, long[]> classEntry = classEntries.next();
String className = classEntry.getKey();
long[] values = classEntry.getValue();
if (sum == null) {
sum = new long[values.length];
}
add(sum, values);
if (total == null) {
total = new long[values.length];
}
add(total, values);
//
// poolName, className, values
//
Map<String,long[]> poolMap2 = new HashMap<>();
classMap2.putIfAbsent(className, poolMap2);
poolMap2.put(poolName, values);
//
// statistics ...
//
html.add(className, AS_PATH_ELEMENT.escape(AS_FILENAME.escape("class-"+className+".html")), values);
}
String filename = AS_FILENAME.escape("pool-" + poolName + ".html");
if (sum != null) {
allPoolsHtml.add(poolName, AS_PATH_ELEMENT.escape(filename), sum);
}
try (PrintWriter pw = new PrintWriter(new FileWriter(new File(pathBase, filename)))) {
html.setPrintWriter(pw);
html.dump();
}
}
try (PrintWriter allPw = new PrintWriter(new FileWriter(new File(pathBase, "pools.html")))) {
allPoolsHtml.setPrintWriter(allPw);
allPoolsHtml.dump();
}
if (total != null) {
printTotal(new File(pathBase, "total.raw"), total, date);
}
BaseStatisticsHtml allClassesHtml = new BaseStatisticsHtml();
allClassesHtml.setSorted(true);
allClassesHtml.setTitle("Classes");
allClassesHtml.setAuthor(DEFAULT_AUTHOR);
allClassesHtml.setKeyType("ClassNames");
allClassesHtml.setHeader(header);
entries = classMap2.entrySet().iterator();
while(entries.hasNext()) {
Map.Entry<String, Map<String, long[]>> entry = entries.next();
String className = entry.getKey();
Map<String,long[]> poolMap2;
poolMap2 = entry.getValue();
long [] sum = null;
BaseStatisticsHtml html = new BaseStatisticsHtml();
html.setSorted(true);
html.setTitle("Class "+className);
html.setAuthor(DEFAULT_AUTHOR);
html.setKeyType("Pools");
html.setHeader(header);
for (Map.Entry<String, long[]> poolEntry : poolMap2.entrySet()) {
String poolName = poolEntry.getKey();
long[] values = poolEntry.getValue();
if (sum == null) {
sum = new long[values.length];
}
add(sum, values);
html.add(poolName, AS_PATH_ELEMENT.escape("pool-" + poolName + ".html"), values);
}
String filename = AS_FILENAME.escape("class-" + className + ".html");
if (sum != null) {
allClassesHtml.add(className, AS_PATH_ELEMENT.escape(filename), sum);
}
try (PrintWriter pw = new PrintWriter(new FileWriter(new File(pathBase, filename)))) {
html.setPrintWriter(pw);
html.dump();
}
}
try (PrintWriter allPw = new PrintWriter(new FileWriter(new File(pathBase, "classes.html")))) {
allClassesHtml.setPrintWriter(allPw);
allClassesHtml.dump();
}
//noinspection unchecked
return new Map[] { poolMap, classMap2 };
}
private void printTotal(File filename, long [] total, Date date) throws IOException {
try (PrintWriter dayTotal = new PrintWriter(new FileWriter(filename))) {
String day = _dayOfCalendarKey.get().format(date);
dayTotal.print(day);
dayTotal.print(" ");
dayTotal.print(date.getTime());
for (long aTotal : total) {
dayTotal.print(" ");
dayTotal.print(aTotal);
}
dayTotal.println("");
}
}
private void add(long[] sum, long[] add) {
int l = Math.min(sum.length, add.length);
for (int i = 0; i < l; i++) {
if (add[i] > -1) {
sum[i] += add[i];
}
}
}
private static void dumpStatistics(Map<String, Map<String, long[]>> result, final PrintWriter pw) {
dumpStatistics(result, (poolName, className, counters) -> {
pw.print(poolName+" "+className+" ");
for (long counter : counters) {
pw.print(counter);
pw.print(" ");
}
pw.println("");
});
pw.flush();
}
private void dumpStatistics(Map<String, Map<String, long[]>> result, final StringBuffer pw) {
dumpStatistics(result, (poolName, className, counters) -> {
pw.append(poolName).append(" ").append(className).append(" ");
for (long counter : counters) {
pw.append(counter);
pw.append(" ");
}
pw.append("\n");
});
}
private Map<String,Map<String,long[]>> mergeStorageClassMaps(
Map<String,Map<String,long[]>> fromPools, Map<String,Map<String,long[]>> fromBilling) {
HashMap<String,Map<String,long[]>> result = new HashMap<>();
Iterator<String> pools = fromPools.keySet().iterator();
pools.forEachRemaining(poolName -> {
Map<String, long[]> resultClasses = result.get(poolName);
if (resultClasses == null) {
result.put(poolName, resultClasses = new HashMap<>());
}
Map<String, long[]> stat = fromPools.get(poolName);
if (stat == null) {
return;
}
for (Map.Entry<String, long[]> entry : stat.entrySet()) {
long[] values = entry.getValue();
if (values == null) {
continue;
}
long[] resultArray = resultClasses.get(entry.getKey());
if (resultArray == null) {
resultClasses.put(entry.getKey(), resultArray = new long[10]);
int preset = fromBilling.get(poolName) == null ? -1 : 0;
for (int i = 2; i < 10; i++) {
resultArray[i] = preset;
}
}
System.arraycopy(values, 0, resultArray, 0, 2);
}
});
pools = fromBilling.keySet().iterator();
pools.forEachRemaining(poolName -> {
Map<String, long[]> resultClasses = result.get(poolName);
if (resultClasses == null) {
result.put(poolName, resultClasses = new HashMap<>());
}
Map<String, long[]> stat = fromBilling.get(poolName);
if (stat == null) {
return;
}
for (Map.Entry<String, long[]> entry : stat.entrySet()) {
long[] values = entry.getValue();
if (values == null) {
continue;
}
long[] resultArray = resultClasses.get(entry.getKey());
if (resultArray == null) {
resultClasses.put(entry.getKey(), resultArray = new long[10]);
int preset = fromPools.get(poolName) == null ? -1 : 0;
for (int i = 0; i < 2; i++) {
resultArray[i] = preset;
}
}
System.arraycopy(values, 0, resultArray, 2, 8);
}
});
return result;
}
//
// expected format from 'rep ls -s -binary'
// Object[*]
// Object [2]
// 0 String <storageClass>
// 1 long[2]
// 0 # of bytes in repository
// 1 # of files in repository
//
private Map<String,Map<String,long[]>> getPoolRepositoryStatistics()
throws InterruptedException,
NoRouteToCellException,
IOException
{
_log.info("getPoolRepositoryStatistics : asking PoolManager for cell info");
PoolManagerCellInfo info;
try {
info = _poolManager.sendAndWait(GET_CELL_INFO, PoolManagerCellInfo.class);
} catch (CacheException e) {
throw new IOException(e.getMessage(), e);
}
_log.info("getPoolRepositoryStatistics : PoolManager replied : {}", info);
Map<String, Map<String, long[]>> map = new HashMap<>();
for (Map.Entry<String,CellAddressCore> pool : info.getPoolMap().entrySet()) {
CellAddressCore address = pool.getValue();
try {
_log.info("getPoolRepositoryStatistics : asking {} for statistics", address);
Object[] result =
_poolStub.sendAndWait(new CellPath(address), GET_REP_STATISTICS, Object[].class);
Map<String, long[]> classMap = new HashMap<>();
for (Object entry : result) {
Object[] e = (Object[]) entry;
classMap.put((String) e[0], (long[]) e[1]);
}
_log.info("getPoolRepositoryStatistics : {} replied with {}", address, classMap);
map.put(pool.getKey(), classMap);
} catch (InterruptedException ie) {
_log.warn("getPoolRepositoryStatistics : sendAndWait interrupted");
throw ie;
} catch (CacheException e) {
_log.warn("getPoolRepositoryStatistics : {} : {}", address, e.getMessage());
}
}
return map;
}
private void resetBillingStatistics() {
_log.info("Resetting Billing statistics");
_billing.notify(RESET_POOL_STATISTICS);
}
//
// structur expected from 'billing' : get pool statistics
// map(String <poolName>, long[4])
// 0 : # of transfers
// 1 : # of restore
// 2 : # of store
// 3 # of total errors
//
// structur expected from 'billing' : get pool statistics <poolName>
//
// storageClassMap(String <storageClass>, long[8]);
//
// 0 : # of transfers
// 1 : # of restore
// 2 : # of store
// 3 # of total errors
// 4 : bytes transferred IN
// 5 : bytes transferred OUT
// 6 : bytes restored
// 7 : bytes stored
//
private Map<String,Map<String,long[]>> getBillingStatistics()
throws InterruptedException,
IOException, NoRouteToCellException
{
_log.info("getBillingStatistics : asking billing for generic pool statistics");
Map<String,Map<String,long[]>> generic;
try {
//noinspection unchecked
generic = _billing.sendAndWait(GET_POOL_STATISTICS, Map.class);
} catch (CacheException e) {
throw new IOException(e.getMessage(), e);
}
_log.info("getBillingStatistics : billing replied with {}", generic);
Map<String, Map<String, long[]>> map = new HashMap<>();
for (String poolName: generic.keySet()) {
try {
_log.info("getBillingStatistics : asking billing for [{}] statistics", poolName);
//noinspection unchecked
Map<String,long[]> result = _billing.sendAndWait(GET_POOL_STATISTICS + " " + poolName, Map.class);
_log.info("getBillingStatistics : billing replied with {}", result);
map.put(poolName, result);
} catch(InterruptedException ie) {
_log.warn("'get pool statistics' : sendAndWait interrupted");
throw ie;
} catch (CacheException e) {
_log.warn("'get pool statistics' : {} : {}", poolName, e.getMessage());
}
}
return map;
}
public static void main(String [] args)throws Exception {
DataStore store = new DataStore(new File("xxinput"));
store.store(new File("xxoutput"));
}
private static class DayDirectoryHeader implements HtmlDrawable {
private final Date _date;
private final SimpleDateFormat _dayOfCalendar;
private DayDirectoryHeader(Date date,SimpleDateFormat dayOfCalendar) {
_date = date;
_dayOfCalendar = dayOfCalendar;
}
@Override
public void draw(PrintWriter pw) {
pw.print("<hr><pre> ");
pw.print("<a href=\"/docs/statisticsHelp.html\">Help</a> ");
pw.print("<a href=\"/\">Birds Home</a> ");
pw.print("<a href=\"../../../index.html\">Top</a> ");
pw.print("<a href=\"../../index.html\">This Year</a> ");
pw.print("<a href=\"../index.html\">This Month</a> ");
pw.print("<a href=\"index.html\">Today</a> ");
pw.print("<a href=\"pools.html\">Pools</a> ");
pw.print("<a href=\"classes.html\">Classes</a> ");
pw.print("<a href=\"total.drw\">Raw</a>");
pw.println("</pre><hr>");
pw.println("<center><h1>"+_dayOfCalendar.format(_date)+"</h1></center>");
}
}
private static class MonthDirectoryHeader implements HtmlDrawable {
private MonthDirectoryHeader() {}
@Override
public void draw(PrintWriter pw) {
pw.print("<hr><pre> ");
pw.print("<a href=\"/docs/statisticsHelp.html\">Help</a> ");
pw.print("<a href=\"/\">Birds Home</a> ");
pw.print("<a href=\"../../index.html\">Top</a> ");
pw.print("<a href=\"../index.html\">This Year</a> ");
pw.println("</pre><hr>");
}
}
private static class YearDirectoryHeader implements HtmlDrawable {
private YearDirectoryHeader() {}
@Override
public void draw(PrintWriter pw) {
pw.print("<hr><pre> ");
pw.print("<a href=\"/docs/statisticsHelp.html\">Help</a> ");
pw.print("<a href=\"/\">Birds Home</a> ");
pw.print("<a href=\"../index.html\">Top</a> ");
pw.println("</pre><hr>");
}
}
private static class TopDirectoryHeader implements HtmlDrawable {
private TopDirectoryHeader() {}
@Override
public void draw(PrintWriter pw) {
pw.print("<hr><pre> ");
pw.print("<a href=\"/docs/statisticsHelp.html\">Help</a> ");
pw.print("<a href=\"/\">Birds Home</a> ");
pw.println("</pre><hr>");
}
}
private static class BaseStatisticsHtml {
private final int _height = 10;
private final int _absoluteWidth = 500;
private final int _relativeWidth = 100;
private final String [] _bgcolor = { "white", "#bebebe" };
private PrintWriter _pw;
private final String _imageBase = "/images/";
private final String _poolYesterday = _imageBase + "greenbox.gif";
private final String _poolToday = _imageBase + "redbox.gif";
private final String _restore = _imageBase + "bluebox.gif";
private final String _transferIn = _imageBase + "navybox.gif";
private final String _transferOut = _imageBase + "yellowbox.gif";
private final String _store = _imageBase + "orangebox.gif";
private long _absoluteNorm = 1L;
private Map<String,Object[]> _map;
private long _maxCounterValue;
private String _title = "Title";
private String _author = "dCache Team";
private final String _tableTitleColor = "#115259";
private final String _keyType = "Key";
private final String [] _tableTitles = { _keyType,
"Absolute Values",
"Data / MiB",
"Relative Values" };
private HtmlDrawable _header;
private void setHeader(HtmlDrawable drawable) { _header = drawable; }
public void setSorted(boolean sorted) {
_map = sorted ? new TreeMap() : new LinkedHashMap();
_maxCounterValue = 0L;
}
public void setKeyType(String keyType) { _tableTitles[0] = keyType; }
public void setPrintWriter(PrintWriter pw) { _pw = pw; }
public void reset() {
_maxCounterValue = 0L;
}
public void add(String title, String link, long [] counter) {
Object [] o = new Object[3];
o[0] = title;
o[1] = link;
o[2] = counter;
_maxCounterValue = Math.max(_maxCounterValue, getNorm(counter));
_map.put(title, o);
}
public void setTitle(String title) { _title = title; }
public void setAuthor(String author) { _author = author; }
public void dump() {
printHeader(_title);
if (_header != null) {
_header.draw(_pw);
}
printTitle(_title);
printTableHeader();
_absoluteNorm = _maxCounterValue;
Iterator<Object[]> it = _map.values().iterator();
for(int i = 0; it.hasNext(); i++) {
Object [] o = it.next();
printRow(
(String)o[0],
(String)o[1],
(long [])o[2],
_bgcolor[i%2]
);
}
printTableTrailer();
printTrailer(_author+"; Created : "+new Date().toString());
}
private void printTableHeader() {
_pw.println("<center><table border=1 cellpadding=0 cellspacing=0 width=\"90%\">");
_pw.print("<tr>");
for (String title : _tableTitles) {
_pw.print("<th bgcolor=\"");
_pw.print(_tableTitleColor);
_pw.print("\"><font color=white>");
_pw.print(title);
_pw.println("</font></th>");
}
_pw.println("</tr>");
}
private void printTableTrailer() {
_pw.println("</table></center>");
_pw.flush();
}
private void printHeader(String title) {
_pw.print("<html><head><title>");
_pw.print(title);
_pw.println("</title></head>"+_bodyString);
_pw.print("<center><h2>");
_pw.print(_domainName);
_pw.println("</h2></center>");
}
private void printTitle(String title) {
_pw.print("<center><h1>");
_pw.print(title);
_pw.println("</h1></center>");
}
private void printTrailer(String author) {
if (author != null) {
_pw.print("<hr><address>");
_pw.print(author);
_pw.println("</address>");
}
_pw.println("</body></html>");
_pw.flush();
}
private long getNorm(long [] counter) {
return Math.max(
counter[YESTERDAY] + counter[RESTORE] + counter[TRANSFER_IN],
Math.max(
counter[TODAY],
counter[STORE] + counter[TRANSFER_OUT]
));
}
private String printUnit(long counter) {
if (counter <= 0) {
return "0";
}
String unit = String.valueOf(BYTES.toMiB(counter));
StringBuilder sb = new StringBuilder();
for(int j = unit.length() - 1, c = 0; j >= 0; j--, c++) {
if ((c > 0) && (c%3 == 0)) {
sb.append('.');
}
sb.append(unit.charAt(j));
}
return sb.reverse().toString();
}
private void printRow(String title, String link, long [] counter, String bgColor) {
int value;
long norm = getNorm(counter);
int content;
printTR();
_pw.print("<th rowspan=3 align=center bgcolor=\"");
_pw.print(bgColor);
_pw.print("\">");
printHREF(title, link);
_pw.println("</th>");
printTD(bgColor, false);
content = 0;
value = (int) (((double)counter[YESTERDAY])/((double)_absoluteNorm)*((double)_absoluteWidth));
content += printImage(_poolYesterday, value, _height);
value = (int) (((double)counter[RESTORE])/((double)_absoluteNorm)*((double)_absoluteWidth));
content += printImage(_restore, value, _height);
value = (int) (((double)counter[TRANSFER_IN])/((double)_absoluteNorm)*((double)_absoluteWidth));
content += printImage(_transferIn, value, _height);
printTDEnd(content);
printTD(bgColor, true);
print(printUnit(counter[YESTERDAY]));
print(" + "+printUnit(counter[RESTORE]), "red");
print(" + "+printUnit(counter[TRANSFER_IN]), "red");
printTDEnd(1);
printTD(bgColor, false);
content = 0;
value = (int) (((double)counter[YESTERDAY])/((double)norm)*((double)_relativeWidth));
content += printImage(_poolYesterday, value, _height);
value = (int) (((double)counter[RESTORE])/((double)norm)*((double)_relativeWidth));
content += printImage(_restore, value, _height);
value = (int) (((double)counter[TRANSFER_IN])/((double)norm)*((double)_relativeWidth));
content += printImage(_transferIn, value, _height);
printTDEnd(content);
printTREnd();
printTR();
printTD(bgColor, false);
content = 0;
value = (int) (((double)counter[TODAY])/((double)_absoluteNorm)*((double)_absoluteWidth));
content += printImage(_poolToday, value, _height);
printTDEnd(content);
printTD(bgColor, true);
print(printUnit(counter[TODAY]));
printTDEnd(1);
printTD(bgColor, false);
content = 0;
value = (int) (((double)counter[TODAY])/((double)norm)*((double)_relativeWidth));
content += printImage(_poolToday, value, _height);
printTDEnd(content);
printTREnd();
printTR();
printTD(bgColor, false);
content = 0;
value = (int) (((double)counter[TRANSFER_OUT])/((double)_absoluteNorm)*((double)_absoluteWidth));
content += printImage(_transferOut, value, _height);
value = (int) (((double)counter[STORE])/((double)_absoluteNorm)*((double)_absoluteWidth));
content += printImage(_store, value, _height);
printTDEnd(content);
printTD(bgColor, true);
print(printUnit(counter[TRANSFER_OUT]));
print(" + "+printUnit(counter[STORE]));
printTDEnd(1);
printTD(bgColor, false);
content = 0;
value = (int) (((double)counter[TRANSFER_OUT])/((double)norm)*((double)_relativeWidth));
content += printImage(_transferOut, value, _height);
value = (int) (((double)counter[STORE])/((double)norm)*((double)_relativeWidth));
content += printImage(_store, value, _height);
printTDEnd(content);
printTREnd();
}
private void printTR() { _pw.println("<tr>"); }
private void printTREnd() { _pw.println("</tr>"); }
private int printImage(String imageFile, int width, int height) {
if (width <= 0) { return 0; }
_pw.print("<img src=\"");
_pw.print(imageFile);
_pw.print("\" width=");
_pw.print(Math.max(width,0));
_pw.print(" height=");
_pw.print(height);
_pw.print(">");
return 1;
}
private void printTD(String bgcolor, boolean center) {
_pw.print("<td ");
if (center) {
_pw.print("align=center ");
}
_pw.print("bgcolor=\"");
_pw.print(bgcolor);
_pw.print("\">");
}
private void print(String string) {
print(string, null);
}
private void print(String string, String color) {
if (color != null) {
_pw.print("<font color=\"");
_pw.print(color);
_pw.print("\">");
}
_pw.print(string);
if (color != null) {
_pw.print("</font>");
}
}
private void printTDEnd(int content) {
if (content == 0) {
_pw.print(" ");
}
_pw.println("</td>");
}
private void printHREF(String title, String link) {
_pw.print("<a href=\"");
_pw.print(link);
_pw.print("\">");
_pw.print(title);
_pw.println("</a>");
}
}
}