summaryrefslogtreecommitdiffstats
path: root/web
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--web/README.md2
-rw-r--r--web/api/badges/README.md8
-rw-r--r--web/api/badges/web_buffer_svg.c102
-rw-r--r--web/api/badges/web_buffer_svg.h2
-rw-r--r--web/api/exporters/shell/allmetrics_shell.c2
-rw-r--r--web/api/formatters/json/README.md2
-rw-r--r--web/api/queries/query.c87
-rw-r--r--web/gui/README.md18
-rw-r--r--web/gui/dash-example.html2
-rw-r--r--web/gui/dashboard_info.js103
-rw-r--r--web/gui/goto-host-from-alarm.html2
-rw-r--r--web/gui/main.css4
-rw-r--r--web/gui/main.js55
-rw-r--r--web/server/README.md16
-rw-r--r--web/server/static/static-threaded.c31
-rw-r--r--web/server/web_client.c8
-rw-r--r--web/server/web_client.h7
-rw-r--r--web/server/web_server.c24
18 files changed, 335 insertions, 140 deletions
diff --git a/web/README.md b/web/README.md
index 2636323a..f37d7f51 100644
--- a/web/README.md
+++ b/web/README.md
@@ -43,7 +43,7 @@ analysis.
Whenever you use a chart in this way, Netdata synchronizes all the other charts
to match it. Chart synchronization even works between separate Netdata agents if you connect
-them using the [node menu](../registry)!
+them using the [**My nodes** menu](../registry)!
You can change how charts show their metrics in a few different ways, each of
which have a few methods:
diff --git a/web/api/badges/README.md b/web/api/badges/README.md
index 75c30abf..f199c580 100644
--- a/web/api/badges/README.md
+++ b/web/api/badges/README.md
@@ -214,6 +214,14 @@ These are options dedicated to badges:
<img src="https://registry.my-netdata.io/api/v1/badge.svg?chart=system.cpu&after=-60&scale=175"></img> `scale=175`<br/>
<img src="https://registry.my-netdata.io/api/v1/badge.svg?chart=system.cpu&after=-60&scale=200"></img> `scale=200`
+- `fixed_width_lbl=NUMBER` and `fixed_width_val=NUMBER`
+
+ This parameter overrides auto-sizing of badges and displays them at fixed widths. `fixed_width_lbl` determines the size of the label's left side (label/name). `fixed_width_val` determines the size of the the label's right side (value). You must set both parameters together, or they will be ignored.
+
+ You should set the label/value widths wide enough to provide space for all the possible values/contents of the badge you're requesting. In case the text cannot fit the space given it will be clipped.
+
+ The `scale` parameter still applies on the values you give to `fixed_width_lbl` and `fixed_width_val`.
+
- `refresh=auto` or `refresh=SECONDS`
This option enables auto-refreshing of images. Netdata will send the HTTP header `Refresh: SECONDS` to the web browser, thus requesting automatic refresh of the images at regular intervals.
diff --git a/web/api/badges/web_buffer_svg.c b/web/api/badges/web_buffer_svg.c
index 143fc0f3..ea07c894 100644
--- a/web/api/badges/web_buffer_svg.c
+++ b/web/api/badges/web_buffer_svg.c
@@ -688,7 +688,7 @@ static inline void calc_colorz(const char *color, char *final, size_t len, calcu
// colors
#define COLOR_STRING_SIZE 100
-void buffer_svg(BUFFER *wb, const char *label, calculated_number value, const char *units, const char *label_color, const char *value_color, int precision, int scale, uint32_t options) {
+void buffer_svg(BUFFER *wb, const char *label, calculated_number value, const char *units, const char *label_color, const char *value_color, int precision, int scale, uint32_t options, int fixed_width_lbl, int fixed_width_val) {
char value_color_buffer[COLOR_STRING_SIZE + 1]
, value_string[VALUE_STRING_SIZE + 1]
, label_escaped[LABEL_STRING_SIZE + 1]
@@ -696,7 +696,8 @@ void buffer_svg(BUFFER *wb, const char *label, calculated_number value, const ch
, label_color_escaped[COLOR_STRING_SIZE + 1]
, value_color_escaped[COLOR_STRING_SIZE + 1];
- double label_width, value_width, total_width, height = 20.0, font_size = 11.0, text_offset = 5.8, round_corner = 3.0;
+ double label_width = (double)fixed_width_lbl, value_width = (double)fixed_width_val, total_width;
+ double height = 20.0, font_size = 11.0, text_offset = 5.8, round_corner = 3.0;
if(scale < 100) scale = 100;
@@ -709,8 +710,10 @@ void buffer_svg(BUFFER *wb, const char *label, calculated_number value, const ch
calc_colorz(value_color, value_color_buffer, COLOR_STRING_SIZE, value);
format_value_and_unit(value_string, VALUE_STRING_SIZE, (options & RRDR_OPTION_DISPLAY_ABS)?calculated_number_fabs(value):value, units, precision);
- label_width = verdana11_width(label, font_size) + (BADGE_HORIZONTAL_PADDING * 2);
- value_width = verdana11_width(value_string, font_size) + (BADGE_HORIZONTAL_PADDING * 2);
+ if(fixed_width_lbl <= 0 || fixed_width_val <= 0) {
+ label_width = verdana11_width(label, font_size) + (BADGE_HORIZONTAL_PADDING * 2);
+ value_width = verdana11_width(value_string, font_size) + (BADGE_HORIZONTAL_PADDING * 2);
+ }
total_width = label_width + value_width;
escape_xmlz(label_escaped, label, LABEL_STRING_SIZE);
@@ -740,16 +743,49 @@ void buffer_svg(BUFFER *wb, const char *label, calculated_number value, const ch
"<rect class=\"bdge-ttl-width\" width=\"%0.2f\" height=\"%0.2f\" rx=\"%0.2f\" fill=\"#fff\"/>"
"</mask>"
"<g mask=\"url(#round)\">"
- "<rect class=\"bdge-rect-lbl\" width=\"%0.2f\" height=\"%0.2f\" fill=\"%s\"/>"
- "<rect class=\"bdge-rect-val\" x=\"%0.2f\" width=\"%0.2f\" height=\"%0.2f\" fill=\"%s\"/>"
+ "<rect class=\"bdge-rect-lbl\" width=\"%0.2f\" height=\"%0.2f\" fill=\"%s\"/>",
+ total_width, height,
+ total_width, height, round_corner,
+ label_width, height, label_color_escaped); //<rect class="bdge-rect-lbl"
+
+ if(fixed_width_lbl > 0 && fixed_width_val > 0) {
+ buffer_sprintf(wb,
+ "<clipPath id=\"lbl-rect\">"
+ "<rect class=\"bdge-rect-lbl\" width=\"%0.2f\" height=\"%0.2f\"/>"
+ "</clipPath>",
+ label_width, height); //<clipPath id="lbl-rect"> <rect class="bdge-rect-lbl"
+ }
+
+ buffer_sprintf(wb,
+ "<rect class=\"bdge-rect-val\" x=\"%0.2f\" width=\"%0.2f\" height=\"%0.2f\" fill=\"%s\"/>",
+ label_width, value_width, height, value_color_escaped);
+
+ if(fixed_width_lbl > 0 && fixed_width_val > 0) {
+ buffer_sprintf(wb,
+ "<clipPath id=\"val-rect\">"
+ "<rect class=\"bdge-rect-val\" x=\"%0.2f\" width=\"%0.2f\" height=\"%0.2f\"/>"
+ "</clipPath>",
+ label_width, value_width, height);
+ }
+
+ buffer_sprintf(wb,
"<rect class=\"bdge-ttl-width\" width=\"%0.2f\" height=\"%0.2f\" fill=\"url(#smooth)\"/>"
"</g>"
"<g fill=\"#fff\" text-anchor=\"middle\" font-family=\"DejaVu Sans,Verdana,Geneva,sans-serif\" font-size=\"%0.2f\">"
- "<text class=\"bdge-lbl-lbl\" x=\"%0.2f\" y=\"%0.0f\" fill=\"#010101\" fill-opacity=\".3\">%s</text>"
- "<text class=\"bdge-lbl-lbl\" x=\"%0.2f\" y=\"%0.0f\">%s</text>"
- "<text class=\"bdge-lbl-val\" x=\"%0.2f\" y=\"%0.0f\" fill=\"#010101\" fill-opacity=\".3\">%s</text>"
- "<text class=\"bdge-lbl-val\" x=\"%0.2f\" y=\"%0.0f\">%s</text>"
- "</g>"
+ "<text class=\"bdge-lbl-lbl\" x=\"%0.2f\" y=\"%0.0f\" fill=\"#010101\" fill-opacity=\".3\" clip-path=\"url(#lbl-rect)\">%s</text>"
+ "<text class=\"bdge-lbl-lbl\" x=\"%0.2f\" y=\"%0.0f\" clip-path=\"url(#lbl-rect)\">%s</text>"
+ "<text class=\"bdge-lbl-val\" x=\"%0.2f\" y=\"%0.0f\" fill=\"#010101\" fill-opacity=\".3\" clip-path=\"url(#val-rect)\">%s</text>"
+ "<text class=\"bdge-lbl-val\" x=\"%0.2f\" y=\"%0.0f\" clip-path=\"url(#val-rect)\">%s</text>"
+ "</g>",
+ total_width, height,
+ font_size,
+ label_width / 2, ceil(height - text_offset), label_escaped,
+ label_width / 2, ceil(height - text_offset - 1.0), label_escaped,
+ label_width + value_width / 2 -1, ceil(height - text_offset), value_escaped,
+ label_width + value_width / 2 -1, ceil(height - text_offset - 1.0), value_escaped);
+
+ if(fixed_width_lbl <= 0 || fixed_width_val <= 0){
+ buffer_sprintf(wb,
"<script type=\"text/javascript\">"
"var bdg_horiz_padding = %d;"
"function netdata_bdge_each(list, attr, value){"
@@ -773,19 +809,10 @@ void buffer_svg(BUFFER *wb, const char *label, calculated_number value, const ch
"var width_update_elems = this_svg.getElementsByClassName(\"bdge-ttl-width\");"
"netdata_bdge_each(width_update_elems, \"width\", width_total);"
"this_svg.setAttribute(\"width\", width_total);"
- "</script>"
- "</svg>",
- total_width, height,
- total_width, height, round_corner,
- label_width, height, label_color_escaped,
- label_width, value_width, height, value_color_escaped,
- total_width, height,
- font_size,
- label_width / 2, ceil(height - text_offset), label_escaped,
- label_width / 2, ceil(height - text_offset - 1.0), label_escaped,
- label_width + value_width / 2 -1, ceil(height - text_offset), value_escaped,
- label_width + value_width / 2 -1, ceil(height - text_offset - 1.0), value_escaped,
- BADGE_HORIZONTAL_PADDING );
+ "</script>",
+ BADGE_HORIZONTAL_PADDING);
+ }
+ buffer_sprintf(wb, "</svg>");
}
int web_client_api_request_v1_badge(RRDHOST *host, struct web_client *w, char *url) {
@@ -807,7 +834,9 @@ int web_client_api_request_v1_badge(RRDHOST *host, struct web_client *w, char *u
, *refresh_str = NULL
, *precision_str = NULL
, *scale_str = NULL
- , *alarm = NULL;
+ , *alarm = NULL
+ , *fixed_width_lbl_str = NULL
+ , *fixed_width_val_str = NULL;
int group = RRDR_GROUPING_AVERAGE;
uint32_t options = 0x00000000;
@@ -851,9 +880,20 @@ int web_client_api_request_v1_badge(RRDHOST *host, struct web_client *w, char *u
else if(!strcmp(name, "refresh")) refresh_str = value;
else if(!strcmp(name, "precision")) precision_str = value;
else if(!strcmp(name, "scale")) scale_str = value;
+ else if(!strcmp(name, "fixed_width_lbl")) fixed_width_lbl_str = value;
+ else if(!strcmp(name, "fixed_width_val")) fixed_width_val_str = value;
else if(!strcmp(name, "alarm")) alarm = value;
}
+ int fixed_width_lbl = -1;
+ int fixed_width_val = -1;
+
+ if(fixed_width_lbl_str && *fixed_width_lbl_str
+ && fixed_width_val_str && *fixed_width_val_str) {
+ fixed_width_lbl = str2i(fixed_width_lbl_str);
+ fixed_width_val = str2i(fixed_width_val_str);
+ }
+
if(!chart || !*chart) {
buffer_no_cacheable(w->response.data);
buffer_sprintf(w->response.data, "No chart id is given at the request.");
@@ -866,7 +906,7 @@ int web_client_api_request_v1_badge(RRDHOST *host, struct web_client *w, char *u
if(!st) st = rrdset_find_byname(host, chart);
if(!st) {
buffer_no_cacheable(w->response.data);
- buffer_svg(w->response.data, "chart not found", NAN, "", NULL, NULL, -1, scale, 0);
+ buffer_svg(w->response.data, "chart not found", NAN, "", NULL, NULL, -1, scale, 0, -1, -1);
ret = HTTP_RESP_OK;
goto cleanup;
}
@@ -877,7 +917,7 @@ int web_client_api_request_v1_badge(RRDHOST *host, struct web_client *w, char *u
rc = rrdcalc_find(st, alarm);
if (!rc) {
buffer_no_cacheable(w->response.data);
- buffer_svg(w->response.data, "alarm not found", NAN, "", NULL, NULL, -1, scale, 0);
+ buffer_svg(w->response.data, "alarm not found", NAN, "", NULL, NULL, -1, scale, 0, -1, -1);
ret = HTTP_RESP_OK;
goto cleanup;
}
@@ -995,7 +1035,9 @@ int web_client_api_request_v1_badge(RRDHOST *host, struct web_client *w, char *u
value_color,
precision,
scale,
- options
+ options,
+ fixed_width_lbl,
+ fixed_width_val
);
ret = HTTP_RESP_OK;
}
@@ -1032,7 +1074,9 @@ int web_client_api_request_v1_badge(RRDHOST *host, struct web_client *w, char *u
value_color,
precision,
scale,
- options
+ options,
+ fixed_width_lbl,
+ fixed_width_val
);
}
diff --git a/web/api/badges/web_buffer_svg.h b/web/api/badges/web_buffer_svg.h
index f75677bf..f0558c07 100644
--- a/web/api/badges/web_buffer_svg.h
+++ b/web/api/badges/web_buffer_svg.h
@@ -6,7 +6,7 @@
#include "libnetdata/libnetdata.h"
#include "web/server/web_client.h"
-extern void buffer_svg(BUFFER *wb, const char *label, calculated_number value, const char *units, const char *label_color, const char *value_color, int precision, int scale, uint32_t options);
+extern void buffer_svg(BUFFER *wb, const char *label, calculated_number value, const char *units, const char *label_color, const char *value_color, int precision, int scale, uint32_t options, int fixed_width_lbl, int fixed_width_val);
extern char *format_value_and_unit(char *value_string, size_t value_string_len, calculated_number value, const char *units, int precision);
extern int web_client_api_request_v1_badge(struct rrdhost *host, struct web_client *w, char *url);
diff --git a/web/api/exporters/shell/allmetrics_shell.c b/web/api/exporters/shell/allmetrics_shell.c
index 9a18b92d..90c63d44 100644
--- a/web/api/exporters/shell/allmetrics_shell.c
+++ b/web/api/exporters/shell/allmetrics_shell.c
@@ -108,6 +108,7 @@ void rrd_stats_api_v1_charts_allmetrics_json(RRDHOST *host, BUFFER *wb) {
buffer_sprintf(wb, "%s\n"
"\t\"%s\": {\n"
"\t\t\"name\":\"%s\",\n"
+ "\t\t\"family\":\"%s\",\n"
"\t\t\"context\":\"%s\",\n"
"\t\t\"units\":\"%s\",\n"
"\t\t\"last_updated\": %ld,\n"
@@ -115,6 +116,7 @@ void rrd_stats_api_v1_charts_allmetrics_json(RRDHOST *host, BUFFER *wb) {
, chart_counter?",":""
, st->id
, st->name
+ , st->family
, st->context
, st->units
, rrdset_last_entry_t(st)
diff --git a/web/api/formatters/json/README.md b/web/api/formatters/json/README.md
index 8a0e37bf..2bd37bc7 100644
--- a/web/api/formatters/json/README.md
+++ b/web/api/formatters/json/README.md
@@ -103,7 +103,7 @@ callback({
> Using `format=datatable` and `options=`
```bash
-$ curl -Ss 'https://registry.my-netdata.io/api/v1/data?chart=nginx_local.connections&after=-3600&points=6&group=average&formdatatable&options='
+curl -Ss 'https://registry.my-netdata.io/api/v1/data?chart=nginx_local.connections&after=-3600&points=6&group=average&formdatatable&options='
{
"cols":
[
diff --git a/web/api/queries/query.c b/web/api/queries/query.c
index af3bcfe3..6f186d3a 100644
--- a/web/api/queries/query.c
+++ b/web/api/queries/query.c
@@ -731,6 +731,7 @@ static void rrd2rrdr_log_request_response_metdata(RRDR *r
static int rrdr_convert_before_after_to_absolute(
long long *after_requestedp
, long long *before_requestedp
+ , int update_every
, time_t first_entry_t
, time_t last_entry_t
) {
@@ -749,6 +750,12 @@ static int rrdr_convert_before_after_to_absolute(
// allow relative for before (smaller than API_RELATIVE_TIME_MAX)
if(abs(before_requested) <= API_RELATIVE_TIME_MAX) {
+ if(abs(before_requested) % update_every) {
+ // make sure it is multiple of st->update_every
+ if(before_requested < 0) before_requested = before_requested - update_every -
+ before_requested % update_every;
+ else before_requested = before_requested + update_every - before_requested % update_every;
+ }
if(before_requested > 0) before_requested = first_entry_t + before_requested;
else before_requested = last_entry_t + before_requested; //last_entry_t is not really now_t
//TODO: fix before_requested to be relative to now_t
@@ -757,6 +764,12 @@ static int rrdr_convert_before_after_to_absolute(
// allow relative for after (smaller than API_RELATIVE_TIME_MAX)
if(abs(after_requested) <= API_RELATIVE_TIME_MAX) {
+ if(after_requested == 0) after_requested = -update_every;
+ if(abs(after_requested) % update_every) {
+ // make sure it is multiple of st->update_every
+ if(after_requested < 0) after_requested = after_requested - update_every - after_requested % update_every;
+ else after_requested = after_requested + update_every - after_requested % update_every;
+ }
after_requested = before_requested + after_requested;
absolute_period_requested = 0;
}
@@ -800,28 +813,6 @@ static RRDR *rrd2rrdr_fixedstep(
) {
int aligned = !(options & RRDR_OPTION_NOT_ALIGNED);
- if(!absolute_period_requested) {
- if(before_requested % update_every) {
- // make sure it is multiple of update_every
- if(before_requested > 0)
- before_requested = before_requested - update_every + before_requested % update_every;
- #ifdef NETDATA_INTERNAL_CHECKS
- else
- error("INTERNAL ERROR: rrd2rrdr() on %s, negative or zero before_requested", st->name);
- #endif
- }
- if(after_requested % update_every) {
- // make sure it is multiple of update_every
- if(after_requested < 0)
- after_requested = after_requested - update_every + after_requested % update_every;
- #ifdef NETDATA_INTERNAL_CHECKS
- else
- error("INTERNAL ERROR: rrd2rrdr() on %s, negative or zero after_requested", st->name);
- #endif
- }
- if(after_requested == before_requested) after_requested -= update_every;
- }
-
// the duration of the chart
time_t duration = before_requested - after_requested;
long available_points = duration / update_every;
@@ -1190,28 +1181,6 @@ static RRDR *rrd2rrdr_variablestep(
) {
int aligned = !(options & RRDR_OPTION_NOT_ALIGNED);
- if(!absolute_period_requested) {
- if(before_requested % update_every) {
- // make sure it is multiple of update_every
- if(before_requested > 0)
- before_requested = before_requested - before_requested % update_every;
- #ifdef NETDATA_INTERNAL_CHECKS
- else
- error("INTERNAL ERROR: rrd2rrdr() on %s, negative or zero before_requested", st->name);
- #endif
- }
- if(after_requested % update_every) {
- // make sure it is multiple of update_every
- if(after_requested < 0)
- after_requested = after_requested - after_requested % update_every;
- #ifdef NETDATA_INTERNAL_CHECKS
- else
- error("INTERNAL ERROR: rrd2rrdr() on %s, negative or zero after_requested", st->name);
- #endif
- }
- if(after_requested == before_requested) after_requested -= update_every;
- }
-
// the duration of the chart
time_t duration = before_requested - after_requested;
long available_points = duration / update_every;
@@ -1585,9 +1554,10 @@ RRDR *rrd2rrdr(
time_t first_entry_t = rrdset_first_entry_t(st);
time_t last_entry_t = rrdset_last_entry_t(st);
+ rrd_update_every = st->update_every;
absolute_period_requested = rrdr_convert_before_after_to_absolute(&after_requested, &before_requested,
- first_entry_t, last_entry_t);
-
+ rrd_update_every, first_entry_t,
+ last_entry_t);
#ifdef ENABLE_DBENGINE
if ((st->rrd_memory_mode == RRD_MEMORY_MODE_DBENGINE)) {
struct rrdeng_region_info *region_info_array;
@@ -1597,24 +1567,33 @@ RRDR *rrd2rrdr(
regions = rrdeng_variable_step_boundaries(st, after_requested, before_requested,
&region_info_array, &max_interval);
if (1 == regions) {
- if (region_info_array)
- rrd_update_every = region_info_array[0].update_every;
- else
- rrd_update_every = st->update_every;
- if (region_info_array)
- freez(region_info_array);
+ if (region_info_array) {
+ if (rrd_update_every != region_info_array[0].update_every) {
+ rrd_update_every = region_info_array[0].update_every;
+ /* recalculate query alignment */
+ absolute_period_requested =
+ rrdr_convert_before_after_to_absolute(&after_requested, &before_requested, rrd_update_every,
+ first_entry_t, last_entry_t);
+ }
+ freez(region_info_array);
+ }
return rrd2rrdr_fixedstep(st, points_requested, after_requested, before_requested, group_method,
resampling_time_requested, options, dimensions, rrd_update_every,
first_entry_t, last_entry_t, absolute_period_requested);
} else {
- rrd_update_every = (uint16_t)max_interval;
+ if (rrd_update_every != (uint16_t)max_interval) {
+ rrd_update_every = (uint16_t) max_interval;
+ /* recalculate query alignment */
+ absolute_period_requested = rrdr_convert_before_after_to_absolute(&after_requested, &before_requested,
+ rrd_update_every, first_entry_t,
+ last_entry_t);
+ }
return rrd2rrdr_variablestep(st, points_requested, after_requested, before_requested, group_method,
resampling_time_requested, options, dimensions, rrd_update_every,
first_entry_t, last_entry_t, absolute_period_requested, region_info_array);
}
}
#endif
- rrd_update_every = st->update_every;
return rrd2rrdr_fixedstep(st, points_requested, after_requested, before_requested, group_method,
resampling_time_requested, options, dimensions,
rrd_update_every, first_entry_t, last_entry_t, absolute_period_requested);
diff --git a/web/gui/README.md b/web/gui/README.md
index 876d499f..cb7dc898 100644
--- a/web/gui/README.md
+++ b/web/gui/README.md
@@ -7,7 +7,7 @@ toolkit. You've probably seen it before:
dashboard](https://user-images.githubusercontent.com/2662304/48307727-9175c800-e55b-11e8-92d8-a581d60a4889.gif)
Learn more about how dashboards work and how they're populated using the
-`dashboards.js` file in our [web dashboards overview](../README.md).
+`dashboards.js` file in our [web dashboards overview](../).
By default, Netdata starts a web server for its dashboard at port `19999`. Open
up your web browser of choice and navigate to `http://SERVER-IP:19999`, or
@@ -34,8 +34,8 @@ dashboard](https://user-images.githubusercontent.com/1153921/62810777-ef681980-b
Netdata is broken up into multiple **sections**, such as **System Overview**,
**CPU**, **Disk**, and more. Inside each section you'll find a number of charts,
-broken down into [contexts](../README.md#contexts) and
-[families](../README.md#families).
+broken down into [contexts](../#contexts) and
+[families](../#families).
An example of the **Memory** section on a Linux desktop system.
@@ -56,7 +56,7 @@ associated with.
menu](https://user-images.githubusercontent.com/1153921/62811361-38b96880-bab6-11e9-8d41-4d9b29778e86.png)
Most menu items will contain several **submenu** entries, which represent any
-[families](../README.md#families) from that section. Netdata automatically
+[families](../#families) from that section. Netdata automatically
generates these submenu entries.
Here's a **Disks** menu with several submenu entries for each disk drive and
@@ -129,11 +129,11 @@ Edit the file with your customizations. For example:
```javascript
customDashboard.menu = {
- 'system': {
- title: 'Testing, testing, 1 2 3',
+ system: {
+ title: "Testing, testing, 1 2 3",
icon: '<i class="fas fa-thumbs-up"></i>',
- info: 'This is overwritten info for the system overview section!'
- },
+ info: "This is overwritten info for the system overview section!"
+ }
};
```
@@ -156,4 +156,4 @@ file](https://user-images.githubusercontent.com/1153921/62798924-570e6c80-ba94-1
For information on creating custom dashboards from scratch, see the [custom
dashboards](custom/) or [Atlassian Confluence dashboards](confluence/) guides.
-[![analytics](https://www.google-analytics.com/collect?v=1&aip=1&t=pageview&_s=1&ds=github&dr=https%3A%2F%2Fgithub.com%2Fnetdata%2Fnetdata&dl=https%3A%2F%2Fmy-netdata.io%2Fgithub%2Fweb%2Fgui%2FREADME&_u=MAC~&cid=5792dfd7-8dc4-476b-af31-da2fdb9f93d2&tid=UA-64295674-3)](<>)
+[![analytics](https://www.google-analytics.com/collect?v=1&aip=1&t=pageview&_s=1&ds=github&dr=https%3A%2F%2Fgithub.com%2Fnetdata%2Fnetdata&dl=https%3A%2F%2Fmy-netdata.io%2Fgithub%2Fweb%2Fgui%2FREADME&_u=MAC~&cid=5792dfd7-8dc4-476b-af31-da2fdb9f93d2&tid=UA-64295674-3)]()
diff --git a/web/gui/dash-example.html b/web/gui/dash-example.html
index 55cd6400..231fec8c 100644
--- a/web/gui/dash-example.html
+++ b/web/gui/dash-example.html
@@ -412,8 +412,6 @@ class PickNSort {
constructor (base_url, link_base_url) {
this.base_url = base_url; // URL of netdata host, with port
this.link_base_url = link_base_url || base_url; // Reverse proxy URL (Optional)
- this.link_base_url;
- this.netdata_info;
this.current_alarms = {};
this.new_alarms = {};
this.first_build = true;
diff --git a/web/gui/dashboard_info.js b/web/gui/dashboard_info.js
index 130162be..774577a0 100644
--- a/web/gui/dashboard_info.js
+++ b/web/gui/dashboard_info.js
@@ -213,6 +213,12 @@ netdataDashboard.menu = {
info: 'Network latency statistics, via <b>fping</b>. <b>fping</b> is a program to send ICMP echo probes to network hosts, similar to <code>ping</code>, but much better performing when pinging multiple hosts. fping versions after 3.15 can be directly used as netdata plugins.'
},
+ 'gearman': {
+ title: 'Gearman',
+ icon: '<i class="fas fa-tasks"></i>',
+ info: 'Gearman is a job server that allows you to do work in parallel, to load balance processing, and to call functions between languages.'
+ },
+
'ioping': {
title: 'ioping',
icon: '<i class="fas fa-exchange-alt"></i>',
@@ -492,6 +498,24 @@ netdataDashboard.menu = {
title: 'vSphere',
icon: '<i class="fas fa-server"></i>',
info: 'Performance statistics for ESXI hosts and virtual machines. Data collected from <a href="https://www.vmware.com/products/vcenter-server.html">VMware vCenter Server</a> using <code><a href="https://github.com/vmware/govmomi"> govmomi</a></code> library.'
+ },
+
+ 'vcsa': {
+ title: 'VCSA',
+ icon: '<i class="fas fa-server"></i>',
+ info: 'vCenter Server Appliance health statistics. Data collected from <a href="https://vmware.github.io/vsphere-automation-sdk-rest/vsphere/index.html#SVC_com.vmware.appliance.health">Health API</a>.'
+ },
+
+ 'zookeeper': {
+ title: 'Zookeeper',
+ icon: '<i class="fas fa-database"></i>',
+ info: 'Provides health statistics for <b><a href="https://zookeeper.apache.org/">Zookeeper</a></b> server. Data collected through the command port using <code><a href="https://zookeeper.apache.org/doc/r3.5.5/zookeeperAdmin.html#sc_zkCommands">mntr</a></code> command.'
+ },
+
+ 'hdfs': {
+ title: 'HDFS',
+ icon: '<i class="fas fa-folder-open"></i>',
+ info: 'Provides <b><a href="https://hadoop.apache.org/docs/r3.2.0/hadoop-project-dist/hadoop-hdfs/HdfsDesign.html">Hadoop Distributed File System</a></b> performance statistics. Module collects metrics over <code>Java Management Extensions</code> through the web interface of an <code>HDFS</code> daemon.'
}
};
@@ -1236,6 +1260,39 @@ netdataDashboard.context = {
info: 'A deadlock happens when two or more transactions mutually hold and request for locks, creating a cycle of dependencies. For more information about <a href="https://dev.mysql.com/doc/refman/5.7/en/innodb-deadlocks-handling.html" target="_blank">how to minimize and handle deadlocks</a>.'
},
+ 'mysql.galera_cluster_status': {
+ info:
+ '<code>-1</code>: unknown, ' +
+ '<code>0</code>: primary (primary group configuration, quorum present), ' +
+ '<code>1</code>: non-primary (non-primary group configuration, quorum lost), ' +
+ '<code>2</code>: disconnected(not connected to group, retrying).'
+ },
+
+ 'mysql.galera_cluster_state': {
+ info:
+ '<code>0</code>: undefined, ' +
+ '<code>1</code>: joining, ' +
+ '<code>2</code>: donor/desynced, ' +
+ '<code>3</code>: joined, ' +
+ '<code>4</code>: synced.'
+ },
+
+ 'mysql.galera_cluster_weight': {
+ info: 'The value is counted as a sum of <code>pc.weight</code> of the nodes in the current Primary Component.'
+ },
+
+ 'mysql.galera_connected': {
+ info: '<code>0</code> means that the node has not yet connected to any of the cluster components. ' +
+ 'This may be due to misconfiguration.'
+ },
+
+ 'mysql.open_transactions': {
+ info: 'The number of locally running transactions which have been registered inside the wsrep provider. ' +
+ 'This means transactions which have made operations which have caused write set population to happen. ' +
+ 'Transactions which are read only are not counted.'
+ },
+
+
// ------------------------------------------------------------------------
// POSTGRESQL
@@ -2581,5 +2638,49 @@ netdataDashboard.context = {
'vsphere.overall_status': {
info: '<code>0</code> is unknown, <code>1</code> is OK, <code>2</code> is might have a problem, <code>3</code> is definitely has a problem.'
- }
+ },
+
+ // ------------------------------------------------------------------------
+ // VCSA
+ 'vcsa.system_health': {
+ info:
+ '<code>-1</code>: unknown; ' +
+ '<code>0</code>: all components are healthy; ' +
+ '<code>1</code>: one or more components might become overloaded soon; ' +
+ '<code>2</code>: one or more components in the appliance might be degraded; ' +
+ '<code>3</code>: one or more components might be in an unusable status and the appliance might become unresponsive soon; ' +
+ '<code>4</code>: no health data is available.'
+ },
+
+ 'vcsa.components_health': {
+ info:
+ '<code>-1</code>: unknown; ' +
+ '<code>0</code>: healthy; ' +
+ '<code>1</code>: healthy, but may have some problems; ' +
+ '<code>2</code>: degraded, and may have serious problems; ' +
+ '<code>3</code>: unavailable, or will stop functioning soon; ' +
+ '<code>4</code>: no health data is available.'
+ },
+
+ 'vcsa.software_updates_health': {
+ info:
+ '<code>softwarepackages</code> represents information on available software updates available in the remote vSphere Update Manager repository.<br>' +
+ '<code>-1</code>: unknown; ' +
+ '<code>0</code>: no updates available; ' +
+ '<code>2</code>: non-security updates are available; ' +
+ '<code>3</code>: security updates are available; ' +
+ '<code>4</code>: an error retrieving information on software updates.'
+ },
+
+ // ------------------------------------------------------------------------
+ // Zookeeper
+
+ 'zookeeper.server_state': {
+ info:
+ '<code>0</code>: unknown, ' +
+ '<code>1</code>: leader, ' +
+ '<code>2</code>: follower, ' +
+ '<code>3</code>: observer, ' +
+ '<code>4</code>: standalone.'
+ }
};
diff --git a/web/gui/goto-host-from-alarm.html b/web/gui/goto-host-from-alarm.html
index ec53df08..7d12f848 100644
--- a/web/gui/goto-host-from-alarm.html
+++ b/web/gui/goto-host-from-alarm.html
@@ -69,6 +69,7 @@ var urlOptions = {
alarm_unique_id: 0,
alarm_id: 0,
alarm_event_id: 0,
+ alarm_when: 0,
hasProperty: function(property) {
return typeof this[property] !== 'undefined';
}
@@ -101,6 +102,7 @@ function netdataURL(url) {
+ ';alarm_unique_id=' + urlOptions.alarm_unique_id.toString()
+ ';alarm_id=' + urlOptions.alarm_id.toString()
+ ';alarm_event_id=' + urlOptions.alarm_event_id.toString()
+ + ';alarm_when=' + urlOptions.alarm_when.toString()
;
}
diff --git a/web/gui/main.css b/web/gui/main.css
index b6ba9591..870a1fdd 100644
--- a/web/gui/main.css
+++ b/web/gui/main.css
@@ -383,6 +383,10 @@ body.modal-open {
/* -------------------------------------------------------------------------- */
+#alarms_log_table tbody tr {
+ cursor: pointer;
+}
+
#my-netdata-dropdown-content {
width: 500px;
}
diff --git a/web/gui/main.js b/web/gui/main.js
index 71af6b59..0635b07a 100644
--- a/web/gui/main.js
+++ b/web/gui/main.js
@@ -71,6 +71,7 @@ var urlOptions = {
alarm_unique_id: 0,
alarm_id: 0,
alarm_event_id: 0,
+ alarm_when: 0,
hasProperty: function (property) {
// console.log('checking property ' + property + ' of type ' + typeof(this[property]));
@@ -139,7 +140,7 @@ var urlOptions = {
}
}
- var numeric = ['after', 'before', 'highlight_after', 'highlight_before'];
+ var numeric = ['after', 'before', 'highlight_after', 'highlight_before', 'alarm_when'];
len = numeric.length;
while (len--) {
if (typeof urlOptions[numeric[len]] === 'string') {
@@ -153,6 +154,22 @@ var urlOptions = {
}
}
+ if (urlOptions.alarm_when) {
+ // if alarm_when exists, create after/before params
+ // -/+ 2 minutes from the alarm, and reload the page
+ const alarmTime = new Date(urlOptions.alarm_when * 1000).valueOf();
+ const timeMarginMs = 120000; // 2 mins
+
+ const after = alarmTime - timeMarginMs;
+ const before = alarmTime + timeMarginMs;
+ const newHash = document.location.hash.replace(
+ /;alarm_when=[0-9]*/i,
+ ";after=" + after + ";before=" + before,
+ );
+ history.replaceState(null, '', newHash);
+ location.reload();
+ }
+
if (urlOptions.server !== null && urlOptions.server !== '') {
netdataServerStatic = document.location.origin.toString() + document.location.pathname.toString();
netdataServer = urlOptions.server;
@@ -708,7 +725,7 @@ function openAuthenticatedUrl(url) {
if (isSignedIn()) {
window.open(url);
} else {
- window.open(`${NETDATA.registry.cloudBaseURL}/account/sign-in-agent?id=${NETDATA.registry.machine_guid}&name=${encodeURIComponent(NETDATA.registry.hostname)}&origin=${encodeURIComponent(window.location.origin + "/")}&redirectUrl=${encodeURIComponent(window.location.origin + "/" + url)}`);
+ window.open(`${NETDATA.registry.cloudBaseURL}/account/sign-in-agent?id=${NETDATA.registry.machine_guid}&name=${encodeURIComponent(NETDATA.registry.hostname)}&origin=${encodeURIComponent(window.location.origin + "/")}&redirect_uri=${encodeURIComponent(window.location.origin + "/" + url)}`);
}
}
@@ -2094,7 +2111,7 @@ function alarmsUpdateModal() {
var badge_url = NETDATA.alarms.server + '/api/v1/badge.svg?chart=' + alarm.chart + '&alarm=' + alarm.name + '&refresh=auto';
var action_buttons = '<br/>&nbsp;<br/>role: <b>' + alarm.recipient + '</b><br/>&nbsp;<br/>'
- + '<div class="action-button ripple" title="click to scroll the dashboard to the chart of this alarm" data-toggle="tooltip" data-placement="bottom" onClick="scrollToChartAfterHidingModal(\'' + alarm.chart + '\'); $(\'#alarmsModal\').modal(\'hide\'); return false;"><i class="fab fa-periscope"></i></div>'
+ + '<div class="action-button ripple" title="click to scroll the dashboard to the chart of this alarm" data-toggle="tooltip" data-placement="bottom" onClick="scrollToChartAfterHidingModal(\'' + alarm.chart + '\', ' + alarm.last_status_change * 1000 + ', \'' + alarm.status + '\'); $(\'#alarmsModal\').modal(\'hide\'); return false;"><i class="fab fa-periscope"></i></div>'
+ '<div class="action-button ripple" title="click to copy to the clipboard the URL of this badge" data-toggle="tooltip" data-placement="bottom" onClick="clipboardCopy(\'' + badge_url + '\'); return false;"><i class="far fa-copy"></i></div>'
+ '<div class="action-button ripple" title="click to copy to the clipboard an auto-refreshing <code>embed</code> html element for this badge" data-toggle="tooltip" data-placement="bottom" onClick="clipboardCopyBadgeEmbed(\'' + badge_url + '\'); return false;"><i class="fas fa-copy"></i></div>';
@@ -2335,6 +2352,18 @@ function alarmsUpdateModal() {
exportOptions: {
fileName: 'netdata_alarm_log'
},
+ onClickRow: function (row, $element,field) {
+ void (field);
+ void ($element);
+ let main_url;
+ let common_url = "&host=" + encodeURIComponent(row['hostname']) + "&chart=" + encodeURIComponent(row['chart']) + "&family=" + encodeURIComponent(row['family']) + "&alarm=" + encodeURIComponent(row['name']) + "&alarm_unique_id=" + row['unique_id'] + "&alarm_id=" + row['alarm_id'] + "&alarm_event_id=" + row['alarm_event_id'] + "&alarm_when=" + row['when'];
+ if (NETDATA.registry.isUsingGlobalRegistry() && NETDATA.registry.machine_guid != null) {
+ main_url = "https://netdata.cloud/alarms/redirect?agentID=" + NETDATA.registry.machine_guid + common_url;
+ } else {
+ main_url = NETDATA.registry.server + "/goto-host-from-alarm.html?" + common_url ;
+ }
+ window.open(main_url,"_blank");
+ },
rowStyle: function (row, index) {
void (index);
@@ -3965,9 +3994,21 @@ function scrollDashboardTo() {
var modalHiddenCallback = null;
-function scrollToChartAfterHidingModal(chart) {
+function scrollToChartAfterHidingModal(chart, alarmDate, alarmStatus) {
modalHiddenCallback = function () {
- NETDATA.alarms.scrollToChart(chart);
+ NETDATA.alarms.scrollToChart(chart, alarmDate);
+
+ if (['WARNING', 'CRITICAL'].includes(alarmStatus)) {
+ const currentChartState = NETDATA.options.targets.find(
+ (chartState) => chartState.id === chart,
+ )
+ const twoMinutes = 2 * 60 * 1000
+ NETDATA.globalPanAndZoom.setMaster(
+ currentChartState,
+ alarmDate - twoMinutes,
+ alarmDate + twoMinutes,
+ )
+ }
};
}
@@ -4914,8 +4955,8 @@ function handleSignInMessage(e) {
cloudToken = e.data.token;
netdataRegistryCallback(registryAgents);
- if (e.data.redirectUrl) {
- window.location.replace(e.data.redirectUrl);
+ if (e.data.redirectURI && !window.location.href.includes(e.data.redirectURI)) {
+ window.location.replace(e.data.redirectURI);
}
}
diff --git a/web/server/README.md b/web/server/README.md
index 30cb0069..9f47cb8d 100644
--- a/web/server/README.md
+++ b/web/server/README.md
@@ -33,7 +33,7 @@ The ports to bind are controlled via `[web].bind to`, like this:
```
[web]
default port = 19999
- bind to = 127.0.0.1=dashboard^SSL=optional 10.1.1.1:19998=management|netdata.conf hostname:19997=badges [::]:19996=streaming^SSL=force localhost:19995=registry *:http=dashboard unix:/tmp/netdata.sock
+ bind to = 127.0.0.1=dashboard^SSL=optional 10.1.1.1:19998=management|netdata.conf hostname:19997=badges [::]:19996=streaming^SSL=force localhost:19995=registry *:http=dashboard unix:/run/netdata/netdata.sock
```
Using the above, Netdata will bind to:
@@ -44,7 +44,7 @@ Using the above, Netdata will bind to:
- All IPv6 IPs at port 19996. Only metric streaming requests from other Netdata agents will be accepted on this port. Only encrypted streams will be allowed (i.e. slaves also need to be [configured for TLS](../../streaming).
- All the IPs `localhost` resolves to (both IPv4 and IPv6 depending the resolved IPs) at port 19996. This port will only accept registry API requests.
- All IPv4 and IPv6 IPs at port `http` as set in `/etc/services`. Only the UI (dashboard) and the read API will be accessible on this port.
-- Unix domain socket `/tmp/netdata.sock`. All requests are serviceable on this socket.
+- Unix domain socket `/run/netdata/netdata.sock`. All requests are serviceable on this socket. Note that in some OSs like Fedora, every service sees a different `/tmp`, so don't create a Unix socket under `/tmp`. `/run` or `/var/run` is suggested.
The option `[web].default port` is used when an entries in `[web].bind to` do not specify a port.
@@ -79,14 +79,14 @@ Both files must be readable by the `netdata` user. If either of these files do n
For test purposes, you can generate self-signed certificates with the following command:
```bash
-$ openssl req -newkey rsa:2048 -nodes -sha512 -x509 -days 365 -keyout key.pem -out cert.pem
+openssl req -newkey rsa:2048 -nodes -sha512 -x509 -days 365 -keyout key.pem -out cert.pem
```
!!! note
If you use 4096 bits for your key and the certificate, Netdata will need more CPU to process the communication. `rsa4096` can be up to 4 times slower than `rsa2048`, so we recommend using 2048 bits. You can verify the difference by running:
```sh
-$ openssl speed rsa2048 rsa4096
+openssl speed rsa2048 rsa4096
```
#### TLS/SSL enforcement
@@ -98,11 +98,11 @@ When the certificates are defined and unless any other options are provided, a N
To change this behavior, you need to modify the `bind to` setting in the `[web]` section of `netdata.conf`. At the end of each port definition, you can append `^SSL=force` or `^SSL=optional`. What happens with these settings differs, depending on whether the port is used for HTTP/S requests, or for streaming.
-|SSL setting|HTTP requests|HTTPS requests|Unencrypted Streams|Encrypted Streams|
+| SSL setting | HTTP requests|HTTPS requests|Unencrypted Streams|Encrypted Streams|
|:---------:|:-----------:|:------------:|:-----------------:|:----------------|
-|none|Redirected to HTTPS|Accepted|Accepted|Accepted|
-|`force`|Redirected to HTTPS|Accepted|Denied|Accepted|
-|`optional`|Accepted|Accepted|Accepted|Accepted|
+| none | Redirected to HTTPS|Accepted|Accepted|Accepted|
+| `force`| Redirected to HTTPS|Accepted|Denied|Accepted|
+| `optional`| Accepted|Accepted|Accepted|Accepted|
Example:
diff --git a/web/server/static/static-threaded.c b/web/server/static/static-threaded.c
index 5dda2700..c432b9bf 100644
--- a/web/server/static/static-threaded.c
+++ b/web/server/static/static-threaded.c
@@ -7,23 +7,26 @@ int web_client_timeout = DEFAULT_DISCONNECT_IDLE_WEB_CLIENTS_AFTER_SECONDS;
int web_client_first_request_timeout = DEFAULT_TIMEOUT_TO_RECEIVE_FIRST_WEB_REQUEST;
long web_client_streaming_rate_t = 0L;
-// ----------------------------------------------------------------------------
-// high level web clients connection management
-
-static struct web_client *web_client_create_on_fd(int fd, const char *client_ip, const char *client_port, int port_acl) {
+/*
+ * --------------------------------------------------------------------------------------------------------------------
+ * Build web_client state from the pollinfo that describes an accepted connection.
+ */
+static struct web_client *web_client_create_on_fd(POLLINFO *pi) {
struct web_client *w;
w = web_client_get_from_cache_or_allocate();
- w->ifd = w->ofd = fd;
+ w->ifd = w->ofd = pi->fd;
- strncpyz(w->client_ip, client_ip, sizeof(w->client_ip) - 1);
- strncpyz(w->client_port, client_port, sizeof(w->client_port) - 1);
+ strncpyz(w->client_ip, pi->client_ip, sizeof(w->client_ip) - 1);
+ strncpyz(w->client_port, pi->client_port, sizeof(w->client_port) - 1);
+ strncpyz(w->client_host, pi->client_host, sizeof(w->client_host) - 1);
if(unlikely(!*w->client_ip)) strcpy(w->client_ip, "-");
if(unlikely(!*w->client_port)) strcpy(w->client_port, "-");
- w->port_acl = port_acl;
+ w->port_acl = pi->port_acl;
web_client_initialize_connection(w);
+ w->pollinfo_slot = pi->slot;
return(w);
}
@@ -76,7 +79,7 @@ static void *web_server_file_add_callback(POLLINFO *pi, short int *events, void
return w;
}
-static void web_werver_file_del_callback(POLLINFO *pi) {
+static void web_server_file_del_callback(POLLINFO *pi) {
struct web_client *w = (struct web_client *)pi->data;
debug(D_WEB_CLIENT, "%llu: RELEASE FILE READ ON FD %d", w->id, pi->fd);
@@ -138,7 +141,7 @@ static int web_server_file_write_callback(POLLINFO *pi, short int *events) {
// web server clients
static void *web_server_add_callback(POLLINFO *pi, short int *events, void *data) {
- (void)data;
+ (void)data; // Supress warning on unused argument
worker_private->connected++;
@@ -149,10 +152,9 @@ static void *web_server_add_callback(POLLINFO *pi, short int *events, void *data
*events = POLLIN;
debug(D_WEB_CLIENT_ACCESS, "LISTENER on %d: new connection.", pi->fd);
- struct web_client *w = web_client_create_on_fd(pi->fd, pi->client_ip, pi->client_port, pi->port_acl);
- w->pollinfo_slot = pi->slot;
+ struct web_client *w = web_client_create_on_fd(pi);
- if ( !strncmp(pi->client_port,"UNIX",4)){
+ if (!strncmp(pi->client_port, "UNIX", 4)) {
web_client_set_unix(w);
} else {
web_client_set_tcp(w);
@@ -270,8 +272,9 @@ static int web_server_rcv_callback(POLLINFO *pi, short int *events) {
, POLLINFO_FLAG_CLIENT_SOCKET
, "FILENAME"
, ""
+ , ""
, web_server_file_add_callback
- , web_werver_file_del_callback
+ , web_server_file_del_callback
, web_server_file_read_callback
, web_server_file_write_callback
, (void *) w
diff --git a/web/server/web_client.c b/web/server/web_client.c
index 908e3a6a..53a5944c 100644
--- a/web/server/web_client.c
+++ b/web/server/web_client.c
@@ -791,7 +791,7 @@ static inline char *http_header_parse(struct web_client *w, char *s, int parse_u
w->auth_bearer_token = strdupz(v);
}
else if(hash == hash_host && !strcasecmp(s, "Host")){
- strncpyz(w->host, v, (ve - v));
+ strncpyz(w->server_host, v, ((size_t)(ve - v) < sizeof(w->server_host)-1 ? (size_t)(ve - v) : sizeof(w->server_host)-1));
}
#ifdef NETDATA_WITH_ZLIB
else if(hash == hash_accept_encoding && !strcasecmp(s, "Accept-Encoding")) {
@@ -1147,8 +1147,8 @@ static inline void web_client_send_http_header(struct web_client *w) {
char headerbegin[8328];
if (w->response.code == HTTP_RESP_MOVED_PERM) {
memcpy(headerbegin,"\r\nLocation: https://",20);
- size_t headerlength = strlen(w->host);
- memcpy(&headerbegin[20],w->host,headerlength);
+ size_t headerlength = strlen(w->server_host);
+ memcpy(&headerbegin[20],w->server_host,headerlength);
headerlength += 20;
size_t tmp = strlen(w->last_url);
memcpy(&headerbegin[headerlength],w->last_url,tmp);
@@ -1212,7 +1212,7 @@ static inline void web_client_send_http_header(struct web_client *w) {
if(w->mode == WEB_CLIENT_MODE_OPTIONS) {
buffer_strcat(w->response.header_output,
"Access-Control-Allow-Methods: GET, OPTIONS\r\n"
- "Access-Control-Allow-Headers: accept, x-requested-with, origin, content-type, cookie, pragma, cache-control\r\n"
+ "Access-Control-Allow-Headers: accept, x-requested-with, origin, content-type, cookie, pragma, cache-control, x-auth-token\r\n"
"Access-Control-Max-Age: 1209600\r\n" // 86400 * 14
);
}
diff --git a/web/server/web_client.h b/web/server/web_client.h
index 38a43352..c785a7fd 100644
--- a/web/server/web_client.h
+++ b/web/server/web_client.h
@@ -154,13 +154,14 @@ struct web_client {
int ifd;
int ofd;
- char client_ip[NI_MAXHOST+1];
- char client_port[NI_MAXSERV+1];
+ char client_ip[INET6_ADDRSTRLEN]; // Defined buffer sizes include null-terminators
+ char client_port[NI_MAXSERV];
+ char server_host[NI_MAXHOST];
+ char client_host[NI_MAXHOST];
char decoded_url[NETDATA_WEB_REQUEST_URL_SIZE + 1]; // we decode the URL in this buffer
char decoded_query_string[NETDATA_WEB_REQUEST_URL_SIZE + 1]; // we decode the Query String in this buffer
char last_url[NETDATA_WEB_REQUEST_URL_SIZE+1]; // we keep a copy of the decoded URL here
- char host[256];
size_t url_path_length;
char separator; // This value can be either '?' or 'f'
char *url_search_path; //A pointer to the search path sent by the client
diff --git a/web/server/web_server.c b/web/server/web_server.c
index 9e51c81f..b8b84bc3 100644
--- a/web/server/web_server.c
+++ b/web/server/web_server.c
@@ -86,22 +86,34 @@ SIMPLE_PATTERN *web_allow_netdataconf_from = NULL;
void web_client_update_acl_matches(struct web_client *w) {
w->acl = WEB_CLIENT_ACL_NONE;
- if(!web_allow_dashboard_from || simple_pattern_matches(web_allow_dashboard_from, w->client_ip))
+ if (!web_allow_dashboard_from ||
+ connection_allowed(w->ifd, w->client_ip, w->client_host, sizeof(w->client_host),
+ web_allow_dashboard_from, "dashboard"))
w->acl |= WEB_CLIENT_ACL_DASHBOARD;
- if(!web_allow_registry_from || simple_pattern_matches(web_allow_registry_from, w->client_ip))
+ if (!web_allow_registry_from ||
+ connection_allowed(w->ifd, w->client_ip, w->client_host, sizeof(w->client_host),
+ web_allow_registry_from, "registry"))
w->acl |= WEB_CLIENT_ACL_REGISTRY;
- if(!web_allow_badges_from || simple_pattern_matches(web_allow_badges_from, w->client_ip))
+ if (!web_allow_badges_from ||
+ connection_allowed(w->ifd, w->client_ip, w->client_host, sizeof(w->client_host),
+ web_allow_badges_from, "badges"))
w->acl |= WEB_CLIENT_ACL_BADGE;
- if(!web_allow_mgmt_from || simple_pattern_matches(web_allow_mgmt_from, w->client_ip))
+ if (!web_allow_mgmt_from ||
+ connection_allowed(w->ifd, w->client_ip, w->client_host, sizeof(w->client_host),
+ web_allow_mgmt_from, "management"))
w->acl |= WEB_CLIENT_ACL_MGMT;
- if(!web_allow_streaming_from || simple_pattern_matches(web_allow_streaming_from, w->client_ip))
+ if (!web_allow_streaming_from ||
+ connection_allowed(w->ifd, w->client_ip, w->client_host, sizeof(w->client_host),
+ web_allow_streaming_from, "streaming"))
w->acl |= WEB_CLIENT_ACL_STREAMING;
- if(!web_allow_netdataconf_from || simple_pattern_matches(web_allow_netdataconf_from, w->client_ip))
+ if (!web_allow_netdataconf_from ||
+ connection_allowed(w->ifd, w->client_ip, w->client_host, sizeof(w->client_host),
+ web_allow_netdataconf_from, "netdata.conf"))
w->acl |= WEB_CLIENT_ACL_NETDATACONF;
w->acl &= w->port_acl;