* {font-family:Arial}
div {float: left; margin: 0 0 0 0; }
</style>
-<title>netdata</title>
+<title>box.home.tsaousis.gr netdata</title>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<!--Load the AJAX API-->
<script type="text/javascript" src="https://www.google.com/jsapi"></script>
- <script type="text/javascript" src="jquery-1.10.1.min.js"></script>
+ <script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<script type="text/javascript" src="netdata.js"></script>
<script type="text/javascript">
// Set a callback to run when the Google Visualization API is loaded.
google.setOnLoadCallback(drawCharts);
-
+
function drawCharts() {
- var width = (window.innerWidth - 50) / 2;
- var height = 260;
-
- if(width < height) width = height; //>
+ var width = 3; // if zero, auto-adjusts to 50% of screen, 1-10 goes 1/width of screen
+ var height = 4;
// EDIT: add one line per interface you have
// EDIT:
- // EDIT: name div id json data graph title
- // EDIT: --------------------------------------------------------------------------------
- drawChart('eth0', 'eth0_div', width, height, "eth0.json", "Live Network Usage for eth0");
- drawChart('tun0', 'tun0_div', width, height, "tun0.json", "Live Network Usage for tun0");
- drawChart('ppp0', 'ppp0_div', width, height, "ppp0.json", "Live Network Usage for ppp0");
+ // EDIT: name div id json data graph vertical axis title
+ // EDIT: --------------------------------------------------------------------------------------------------
+ drawChart('disk.sde', 'disk_sde_div', width, height, "data/disk.sde/120/2/average/", "Live Disk I/O for USB HD (disk.sde)", "I/O in kilobytes/s");
+ drawChart('disk.md125', 'disk_md125_div', width, height, "data/disk.md125/120/2/average/", "Live Disk I/O for rootfs (disk.md125)", "I/O in kilobytes/s");
+ drawChart('disk.md126', 'disk_md126_div', width, height, "data/disk.md126/120/2/average/", "Live Disk I/O for swap (disk.md126)", "I/O in kilobytes/s");
+ drawChart('disk.md127', 'disk_md127_div', width, height, "data/disk.md127/120/2/average/", "Live Disk I/O for /data (disk.md127)", "I/O in kilobytes/s");
+ drawChart('net.dsl0', 'net_dsl0_div', width, height, "data/net.dsl0/120/2/average/", "Live Network Usage for ADSL (net.dsl0)", "bandwidth in kilobits/s");
+ drawChart('net.eth3', 'net_eth3_div', width, height, "data/net.eth3/120/2/average/", "Live Network Usage for AWMN (net.eth3)", "bandwidth in kilobits/s");
+ drawChart('net.eth0', 'net_eth0_div', width, height, "data/net.eth0/120/2/average/", "Live Network Usage for LAN (net.eth0)", "bandwidth in kilobits/s");
+ drawChart('net.exohiko', 'net_exohiko_div', width, height, "data/net.exohiko/120/2/average/", "Live Network Usage for Exohiko (net.exohiko)", "bandwidth in kilobits/s");
+ drawChart('net.tun2', 'net_tun2_div', width, height, "data/net.tun2/120/2/average/", "Live Network Usage for Gregory (net.tun2)", "bandwidth in kilobits/s");
+ drawChart('net.tun0', 'net_tun0_div', width, height, "data/net.tun0/120/2/average/", "Live Network Usage for Realize (net.tun0)", "bandwidth in kilobits/s");
}
- function myRefresh() {
- // refresh up to 4 charts every time
- refreshCharts(4);
- }
+ var refreshCount = 0;
+ function myChartsRefresh() {
+ refreshCount++;
+ if(refreshCount > 500) location.reload();
+
+ // refresh up to 1 charts per second
+ refreshCharts(1);
+ }
- // EDIT: how often the charts are updated, in milliseconds
- setInterval(myRefresh, 1000);
+ setInterval(myChartsRefresh, 400);
+
+ window.onresize = function(event) {
+ refreshCharts(999999);
+ };
</script>
-</head>
+
+ </head>
<body>
<!--
EDIT: add one div per interface you have
EDIT: use the same id above and bellow!
-->
-
- <div id="eth0_div"></div>
- <div id="tun0_div"></div>
- <div id="ppp0_div"></div>
+ <div id="net_dsl0_div"></div>
+ <div id="net_eth3_div"></div>
+ <div id="net_eth0_div"></div>
+ <div id="net_exohiko_div"></div>
+ <div id="net_tun0_div"></div>
+ <div id="net_tun2_div"></div>
+ <div id="disk_md125_div"></div>
+ <div id="disk_md126_div"></div>
+ <div id="disk_md127_div"></div>
+ <div id="disk_sde_div"></div>
</body>
</html>
#define MAX_PROC_NET_DEV_LINE 4096\r
#define MAX_PROC_NET_DEV_IFACE_NAME 1024\r
\r
+#define MAX_PROC_DISKSTATS_LINE 4096\r
+#define MAX_PROC_DISKSTATS_DISK_NAME 1024\r
+\r
+\r
int silent = 0;\r
int save_history = HISTORY;\r
int update_every = UPDATE_EVERY;\r
typedef struct rrd_dimension RRD_DIMENSION;\r
\r
struct rrd_stats {\r
+ pthread_mutex_t mutex;\r
+\r
char name[RRD_STATS_NAME_MAX + 1];\r
\r
size_t entries;\r
typedef struct rrd_stats RRD_STATS;\r
\r
RRD_STATS *root = NULL;\r
+pthread_mutex_t root_mutex = PTHREAD_MUTEX_INITIALIZER;\r
\r
RRD_STATS *rrd_stats_create(const char *name, unsigned long entries)\r
{\r
\r
st->entries = entries;\r
st->last_entry = 0;\r
-\r
st->dimensions = NULL;\r
+\r
+ pthread_mutex_init(&st->mutex, NULL);\r
+ pthread_mutex_lock(&st->mutex);\r
+ pthread_mutex_lock(&root_mutex);\r
+\r
st->next = root;\r
root = st;\r
\r
+ pthread_mutex_unlock(&root_mutex);\r
+ // leave st->mutex locked\r
+\r
return(st);\r
}\r
\r
\r
RRD_STATS *rrd_stats_find(const char *name)\r
{\r
+ pthread_mutex_lock(&root_mutex);\r
+\r
RRD_STATS *st = root;\r
\r
for ( ; st ; st = st->next ) {\r
if(strcmp(st->name, name) == 0) break;\r
}\r
\r
+ pthread_mutex_unlock(&root_mutex);\r
return(st);\r
}\r
\r
{\r
struct timeval *now;\r
\r
+ pthread_mutex_lock(&st->mutex);\r
+\r
// st->last_entry should never be outside the array\r
// or, the parallel threads may end up crashing\r
st->last_entry = ((st->last_entry + 1) >= st->entries) ? 0 : st->last_entry + 1;\r
\r
now = &st->times[st->last_entry];\r
gettimeofday(now, NULL);\r
+\r
+ // leave mutex locked\r
+}\r
+\r
+void rrd_stats_done(RRD_STATS *st)\r
+{\r
+ pthread_mutex_unlock(&st->mutex);\r
}\r
\r
void rrd_stats_dimension_set(RRD_STATS *st, const char *dimension, void *data)\r
\r
size_t rrd_stats_json(RRD_STATS *st, char *b, size_t length, size_t entries_to_show, size_t group_count, int group_method)\r
{\r
+ pthread_mutex_lock(&st->mutex);\r
+\r
// check the options\r
if(entries_to_show <= 0) entries_to_show = 1;\r
if(group_count <= 0) group_count = 1;\r
for( rd = st->dimensions ; rd ; rd = rd->next)\r
dimensions++;\r
\r
- if(!dimensions)\r
+ if(!dimensions) {\r
+ pthread_mutex_unlock(&st->mutex);\r
return sprintf(b, "No dimensions yet.");\r
+ }\r
\r
// temporary storage to keep track of group values and counts\r
long long group_values[dimensions];\r
struct tm *tm = localtime(&st->times[t].tv_sec);\r
if(!tm) { error("localtime() failed."); continue; }\r
\r
- strftime(dtm, 200, "[%H, %M, %S, 0]", tm);\r
+ // strftime(dtm, 200, "[%Y, %m, %d, %H, %M, %S, 0]", tm); // datetime\r
+ strftime(dtm, 200, "[%H, %M, %S, 0]", tm); // timeofday\r
i += sprintf(&b[i], "%s {\"c\":[{\"v\":%s},", printed?"]},\n":"", dtm);\r
\r
printed++;\r
if(printed) i += sprintf(&b[i], "]}");\r
i += sprintf(&b[i], "\n ]\n}\n");\r
\r
+ pthread_mutex_unlock(&st->mutex);\r
return(i);\r
}\r
\r
// ----------------------------------------------------------------------------\r
// /proc/net/dev processor\r
\r
-void update_iface_history(char *name, unsigned long long rbytes, unsigned long long tbytes) {\r
-\r
- RRD_STATS *st = rrd_stats_find(name);\r
- if(!st) {\r
- st = rrd_stats_create(name, save_history);\r
- if(!st) {\r
- error("Cannot create RRD_STATS for interface %s.", name);\r
- return;\r
- }\r
-\r
- if(!rrd_stats_dimension_add(st, "received", sizeof(unsigned long long), 8, 1024)) {\r
- error("Cannot add RRD_STATS dimension %s.", "received");\r
- return;\r
- }\r
-\r
- if(!rrd_stats_dimension_add(st, "sent", sizeof(unsigned long long), 8, 1024)) {\r
- error("Cannot add RRD_STATS dimension %s.", "sent");\r
- return;\r
- }\r
- }\r
-\r
- rrd_stats_next(st);\r
- rrd_stats_dimension_set(st, "received", &rbytes);\r
- rrd_stats_dimension_set(st, "sent", &tbytes);\r
-}\r
-\r
int do_proc_net_dev() {\r
char buffer[MAX_PROC_NET_DEV_LINE+1] = "";\r
- char iface[MAX_PROC_NET_DEV_IFACE_NAME + 1] = "net.";\r
+ char name[MAX_PROC_NET_DEV_IFACE_NAME + 1] = "net.";\r
unsigned long long rbytes, rpackets, rerrors, rdrops, rfifo, rframe, rcompressed, rmulticast;\r
unsigned long long tbytes, tpackets, terrors, tdrops, tfifo, tcollisions, tcarrier, tcompressed;\r
\r
\r
// if(DEBUG) printf("%s\n", buffer);\r
r = sscanf(buffer, "%s\t%llu\t%llu\t%llu\t%llu\t%llu\t%llu\t%llu\t%llu\t%llu\t%llu\t%llu\t%llu\t%llu\t%llu\t%llu\t%llu\n",\r
- &iface[4],\r
+ &name[4],\r
&rbytes, &rpackets, &rerrors, &rdrops, &rfifo, &rframe, &rcompressed, &rmulticast,\r
&tbytes, &tpackets, &terrors, &tdrops, &tfifo, &tcollisions, &tcarrier, &tcompressed);\r
if(r == EOF) break;\r
if(r != 17) error("Cannot read /proc/net/dev line. Expected 17 params, read %d.", r);\r
- else update_iface_history(iface, rbytes, tbytes);\r
+ else {\r
+ RRD_STATS *st = rrd_stats_find(name);\r
+\r
+ if(!st) {\r
+ st = rrd_stats_create(name, save_history);\r
+ if(!st) {\r
+ error("Cannot create RRD_STATS for interface %s.", name);\r
+ continue;\r
+ }\r
+\r
+ if(!rrd_stats_dimension_add(st, "sent", sizeof(unsigned long long), 8, 1024))\r
+ error("Cannot add RRD_STATS dimension %s.", "sent");\r
+\r
+ if(!rrd_stats_dimension_add(st, "received", sizeof(unsigned long long), 8, 1024))\r
+ error("Cannot add RRD_STATS dimension %s.", "received");\r
+\r
+ }\r
+ else rrd_stats_next(st);\r
+\r
+ rrd_stats_dimension_set(st, "received", &rbytes);\r
+ rrd_stats_dimension_set(st, "sent", &tbytes);\r
+ rrd_stats_done(st);\r
+ }\r
+ }\r
+ \r
+ fclose(fp);\r
+ return 0;\r
+}\r
+\r
+int do_proc_diskstats() {\r
+ char buffer[MAX_PROC_DISKSTATS_LINE+1] = "";\r
+ char name[MAX_PROC_DISKSTATS_DISK_NAME + 1] = "disk.";\r
+ // 1 2 3 4 5 6 7 8 9 10 11\r
+ unsigned long long major, minor, reads, reads_merged, readsectors, readms, writes, writes_merged, writesectors, writems, currentios, iosms, wiosms;\r
+ \r
+ int r;\r
+ char *p;\r
+ \r
+ FILE *fp = fopen("/proc/diskstats", "r");\r
+ if(!fp) {\r
+ error("Cannot read /proc/diskstats.");\r
+ return 1;\r
+ }\r
+ \r
+ for(;1;) {\r
+ p = fgets(buffer, MAX_PROC_DISKSTATS_LINE, fp);\r
+ if(!p) break;\r
+ \r
+ // if(DEBUG) printf("%s\n", buffer);\r
+ r = sscanf(buffer, "%llu %llu %s %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu\n",\r
+ &major, &minor, &name[5],\r
+ &reads, &reads_merged, &readsectors, &readms, &writes, &writes_merged, &writesectors, &writems, ¤tios, &iosms, &wiosms\r
+ );\r
+ if(r == EOF) break;\r
+ if(r != 14) error("Cannot read /proc/diskstats line. Expected 14 params, read %d.", r);\r
+ else {\r
+ switch(major) {\r
+ case 1: // ram drives\r
+ continue;\r
+\r
+ case 7: // loops\r
+ continue;\r
+\r
+ case 8: // disks\r
+ if(minor % 16) continue; // partitions\r
+ break;\r
+\r
+ case 9: // MDs\r
+ break;\r
+\r
+ case 11: // CDs\r
+ continue;\r
+\r
+ default:\r
+ continue;\r
+ }\r
+\r
+ RRD_STATS *st = rrd_stats_find(name);\r
+\r
+ if(!st) {\r
+ char ssfilename[FILENAME_MAX + 1];\r
+ int sector_size = 512;\r
+\r
+ sprintf(ssfilename, "/sys/block/%s/queue/hw_sector_size", &name[5]);\r
+ FILE *fpss = fopen(ssfilename, "r");\r
+ if(fpss) {\r
+ char ssbuffer[1025];\r
+ char *tmp = fgets(ssbuffer, 1024, fpss);\r
+\r
+ if(tmp) {\r
+ sector_size = atoi(tmp);\r
+ if(sector_size <= 0) {\r
+ error("Invalid sector size %d for device %s in %s. Assuming 512.", sector_size, name, ssfilename);\r
+ sector_size = 512;\r
+ }\r
+ }\r
+ else error("Cannot read data for sector size for device %s from %s. Assuming 512.", name, ssfilename);\r
+\r
+ fclose(fpss);\r
+ }\r
+ else error("Cannot read sector size for device %s from %s. Assuming 512.", name, ssfilename);\r
+\r
+ st = rrd_stats_create(name, save_history);\r
+ if(!st) {\r
+ error("Cannot create RRD_STATS for disk %s.", name);\r
+ continue;\r
+ }\r
+\r
+ if(!rrd_stats_dimension_add(st, "reads", sizeof(unsigned long long), sector_size, 1024))\r
+ error("Cannot add RRD_STATS dimension %s.", "reads");\r
+\r
+ if(!rrd_stats_dimension_add(st, "writes", sizeof(unsigned long long), sector_size, 1024))\r
+ error("Cannot add RRD_STATS dimension %s.", "writes");\r
+\r
+ }\r
+ else rrd_stats_next(st);\r
+\r
+ rrd_stats_dimension_set(st, "reads", &readsectors);\r
+ rrd_stats_dimension_set(st, "writes", &writesectors);\r
+ rrd_stats_done(st);\r
+ }\r
}\r
\r
fclose(fp);\r
return 0;\r
}\r
\r
-void *proc_net_dev_main(void *ptr)\r
+void *proc_main(void *ptr)\r
{\r
struct timeval last, now, tmp;\r
\r
debug(D_PROCNETDEV_LOOP, "PROCNETDEV: Last loop took %llu usec.", usec);\r
\r
do_proc_net_dev(usec);\r
+ do_proc_diskstats(usec);\r
\r
// find the time to sleep in order to wait exactly update_every seconds\r
gettimeofday(&tmp, NULL);\r
silent = 1;\r
}\r
\r
- pthread_t p_proc_net_dev;\r
- int r_proc_net_dev;\r
+ pthread_t p_proc;\r
+ int r_proc;\r
\r
// spawn a child to collect data\r
- r_proc_net_dev = pthread_create(&p_proc_net_dev, NULL, proc_net_dev_main, NULL);\r
+ r_proc = pthread_create(&p_proc, NULL, proc_main, NULL);\r
\r
// the main process - the web server listener\r
//sleep(1);\r
socket_listen_main(NULL);\r
\r
// wait for the childs to finish\r
- pthread_join(p_proc_net_dev, NULL);\r
+ pthread_join(p_proc, NULL);\r
\r
- printf("PROC NET DEV thread returns: %d\n", r_proc_net_dev);\r
+ printf("PROC NET DEV thread returns: %d\n", r_proc);\r
\r
exit(0);\r
}\r
if(index >= charts.length) return;
var jsonData = $.ajax({
- url: charts_urls[index],
+ url: charts[index].url,
dataType:"json",
async: false,
cache: false
if(!jsonData || jsonData.length == 0) return;
// Create our data table out of JSON data loaded from server.
- charts_data[index] = null;
- charts_data[index] = new google.visualization.DataTable(jsonData);
+ charts[index].data = new google.visualization.DataTable(jsonData);
// Instantiate and draw our chart, passing in some options.
- if(!charts[index]) {
- console.log('Creating new chart for ' + charts_urls[index]);
- charts[index] = new google.visualization.AreaChart(document.getElementById(charts_divs[index]));
+ if(!charts[index].chart) {
+ console.log('Creating new chart for ' + charts[index].url);
+ charts[index].chart = new google.visualization.AreaChart(document.getElementById(charts[index].div));
}
- var width = charts_widths[index];
+ var width = charts[index].width;
if(width == 0) width = (window.innerWidth - 40) / 2;
if(width <= 10) width = (window.innerWidth - 40) / width;
if(width < 200) width = 200;
- var height = charts_heights[index];
+ var height = charts[index].height;
if(height == 0) height = (window.innerHeight - 20) / 2;
if(height <= 10) height = (window.innerHeight - 20) / height;
if(height < 100) height = 100;
var hAxisTitle = null;
var vAxisTitle = null;
if(height >= 200) hAxisTitle = "Time of Day";
- if(width >= 400) vAxisTitle = "Bandwidth in kbps";
+ if(width >= 400) vAxisTitle = charts[index].vtitle;
- if(charts[index]) charts[index].draw(charts_data[index], {width: width, height: height, title: charts_titles[index], hAxis: {title: hAxisTitle}, vAxis: {title: vAxisTitle, minValue: 10}});
- else console.log('Cannot create chart for ' + charts_urls[index]);
+ if(charts[index].chart) charts[index].chart.draw(charts[index].data, {width: width, height: height, title: charts[index].title, hAxis: {title: hAxisTitle}, vAxis: {title: vAxisTitle, minValue: 10}});
+ else console.log('Cannot create chart for ' + charts[index].url);
}
var charts = new Array();
- var charts_data = new Array();
- var charts_names = new Array();
- var charts_divs = new Array();
- var charts_widths = new Array();
- var charts_heights = new Array();
- var charts_urls = new Array();
- var charts_titles = new Array();
-
- function drawChart(name, div, width, height, jsonurl, title) {
+ function drawChart(name, div, width, height, jsonurl, title, vtitle) {
var i;
- for(i = 0; i < charts_names.length; i++) //>
- if(charts_names[i] == name) break;
+ for(i = 0; i < charts.length; i++) //>
+ if(charts[i].name == name) break;
- if(i >= charts_names.length) { //>
+ if(i >= charts.length) { //>
console.log('Creating new objects for chart ' + name);
- charts[i] = null;
- charts_data[i] = null;
- charts_names[i] = name;
- charts_divs[i] = div;
- charts_widths[i] = width;
- charts_heights[i] = height;
- charts_urls[i] = jsonurl;
- charts_titles[i] = title;
+ charts[i] = [];
+ charts[i].chart = null;
+ charts[i].data = null;
+ charts[i].name = name;
+ charts[i].div = div;
+ charts[i].width = width;
+ charts[i].height = height;
+ charts[i].url = jsonurl;
+ charts[i].title = title;
+ charts[i].vtitle = vtitle;
}
try {