summaryrefslogtreecommitdiffstats
path: root/web
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2019-07-08 20:14:42 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2019-07-08 20:14:42 +0000
commit4f88e1a9be89a257fd6ed3045703db6e900027ee (patch)
tree518eb3c3aa1dce9ea281d02e0fd3cc01a9e7913f /web
parentAdding upstream version 1.15.0. (diff)
downloadnetdata-4f88e1a9be89a257fd6ed3045703db6e900027ee.tar.xz
netdata-4f88e1a9be89a257fd6ed3045703db6e900027ee.zip
Adding upstream version 1.16.0.upstream/1.16.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'web')
-rw-r--r--web/README.md2
-rw-r--r--web/api/health/README.md49
-rw-r--r--web/api/health/health_cmdapi.c172
-rw-r--r--web/api/health/health_cmdapi.h2
-rw-r--r--web/api/netdata-swagger.json110
-rw-r--r--web/api/netdata-swagger.yaml77
-rw-r--r--web/gui/Makefile.am7
-rw-r--r--web/gui/console.html (renamed from web/gui/console/index.html)4
-rw-r--r--web/gui/dashboard_info.js37
-rw-r--r--web/gui/demosites.html1
-rw-r--r--web/gui/favicon.icobin34494 -> 1150 bytes
-rw-r--r--web/gui/images/android-icon-144x144.pngbin5534 -> 2721 bytes
-rw-r--r--web/gui/images/android-icon-192x192.pngbin6680 -> 3923 bytes
-rw-r--r--web/gui/images/android-icon-36x36.pngbin1668 -> 539 bytes
-rw-r--r--web/gui/images/android-icon-48x48.pngbin1932 -> 762 bytes
-rw-r--r--web/gui/images/android-icon-72x72.pngbin2716 -> 1153 bytes
-rw-r--r--web/gui/images/android-icon-96x96.pngbin3500 -> 1747 bytes
-rw-r--r--web/gui/images/apple-icon-114x114.pngbin4248 -> 3651 bytes
-rw-r--r--web/gui/images/apple-icon-120x120.pngbin4471 -> 2315 bytes
-rw-r--r--web/gui/images/apple-icon-144x144.pngbin5534 -> 4001 bytes
-rw-r--r--web/gui/images/apple-icon-152x152.pngbin5883 -> 5026 bytes
-rw-r--r--web/gui/images/apple-icon-180x180.pngbin7297 -> 3645 bytes
-rw-r--r--web/gui/images/apple-icon-57x57.pngbin2212 -> 1250 bytes
-rw-r--r--web/gui/images/apple-icon-60x60.pngbin2246 -> 1052 bytes
-rw-r--r--web/gui/images/apple-icon-72x72.pngbin2716 -> 1427 bytes
-rw-r--r--web/gui/images/apple-icon-76x76.pngbin2751 -> 1711 bytes
-rw-r--r--web/gui/images/apple-icon-precomposed.pngbin7254 -> 3926 bytes
-rw-r--r--web/gui/images/apple-icon.pngbin7254 -> 3926 bytes
-rw-r--r--web/gui/images/banner-icon-144x144.pngbin5534 -> 2724 bytes
-rw-r--r--web/gui/images/favicon-128.pngbin0 -> 2436 bytes
-rw-r--r--web/gui/images/favicon-16x16.pngbin1096 -> 285 bytes
-rw-r--r--web/gui/images/favicon-196x196.pngbin0 -> 10025 bytes
-rw-r--r--web/gui/images/favicon-32x32.pngbin1575 -> 454 bytes
-rw-r--r--web/gui/images/favicon-96x96.pngbin3500 -> 1925 bytes
-rw-r--r--web/gui/images/favicon.icobin1150 -> 1150 bytes
-rw-r--r--web/gui/images/ms-icon-144x144.pngbin5534 -> 4001 bytes
-rw-r--r--web/gui/images/ms-icon-150x150.pngbin5772 -> 2867 bytes
-rw-r--r--web/gui/images/ms-icon-310x150.pngbin0 -> 3632 bytes
-rw-r--r--web/gui/images/ms-icon-310x310.pngbin16102 -> 7215 bytes
-rw-r--r--web/gui/images/ms-icon-36x36.pngbin0 -> 536 bytes
-rw-r--r--web/gui/images/ms-icon-70x70.pngbin2523 -> 2436 bytes
-rw-r--r--web/gui/images/netdata-logomark.svg11
-rw-r--r--web/gui/index.html7
-rw-r--r--web/gui/main.css1
-rw-r--r--web/gui/main.js17
-rw-r--r--web/gui/static/static/img/netdata-logomark.svg3
-rw-r--r--web/gui/tv.html14
-rw-r--r--web/server/README.md71
-rw-r--r--web/server/static/static-threaded.c68
-rw-r--r--web/server/web_client.c182
-rw-r--r--web/server/web_client.h4
-rw-r--r--web/server/web_client_cache.c38
-rw-r--r--web/server/web_server.c2
53 files changed, 748 insertions, 131 deletions
diff --git a/web/README.md b/web/README.md
index c110ef65..5c1a06f5 100644
--- a/web/README.md
+++ b/web/README.md
@@ -14,7 +14,7 @@ For our convenience, Netdata provides 2 more layers:
Charts information is stored at /usr/share/netdata/web/[dashboard_info.js](gui/dashboard_info.js). This file includes information that is rendered on the dashboard, controls chart colors, section and subsection heading, titles, etc.
-If you change that file, your changes will be overwritten when Netdata is updated. You can preserve your settings by creating a new such file (there is /usr/share/netdata/web/[dashboard_info_custom.example.js](gui/dashboard_info_custom_example.js) you can use to start with).
+If you change that file, your changes will be overwritten when Netdata is updated. You can preserve your settings by creating a new such file (there is /usr/share/netdata/web/[dashboard_info_custom_example.js](gui/dashboard_info_custom_example.js) you can use to start with).
You have to copy the example file under a new name, so that it will not be overwritten with Netdata updates.
diff --git a/web/api/health/README.md b/web/api/health/README.md
index 2003a61e..66a80d5f 100644
--- a/web/api/health/README.md
+++ b/web/api/health/README.md
@@ -45,6 +45,7 @@ The following will return an SVG badge of the alarm named `NAME`, attached to th
## Health Management API
Netdata v1.12 and beyond provides a command API to control health checks and notifications at runtime. The feature is especially useful for maintenance periods, during which you receive meaningless alarms.
+From Netdata v1.16.0 and beyond, the configuration controlled via the API commands is [persisted across netdata restarts](#persistence).
Specifically, the API allows you to:
- Disable health checks completely. Alarm conditions will not be evaluated at all and no entries will be added to the alarm log.
@@ -142,6 +143,43 @@ Example 2.2: Add one more selector, to also silence alarms for cpu1 and cpu2
http://localhost/api/v1/manage/health?families=cpu1 cpu2
```
+### List silencers
+
+The command `LIST` was added in netdata v1.16.0 and returns a JSON with the current status of the silencers.
+
+```
+ curl "http://myserver/api/v1/manage/health?cmd=LIST" -H "X-Auth-Token: Mytoken"
+```
+
+As an example, the following response shows that we have two silencers configured, one for an alarm called `samplealarm` and one for alarms with context `random` on host `myhost`
+```
+json
+{
+ "all": false,
+ "type": "SILENCE",
+ "silencers": [
+ {
+ "alarm": "samplealarm"
+ },
+ {
+ "context": "random",
+ "hosts": "myhost"
+ }
+ ]
+}
+```
+
+The response below shows that we have disabled all health checks.
+
+```
+json
+{
+ "all": true,
+ "type": "DISABLE",
+ "silencers": []
+}
+
+
### Responses
- "Auth Error" : Token authentication failed
@@ -155,6 +193,17 @@ http://localhost/api/v1/manage/health?families=cpu1 cpu2
- "WARNING: Added alarm selector to silence/disable alarms without a SILENCE or DISABLE command." : Added to the response if a selector is added without a selector-specific command.
- "WARNING: SILENCE or DISABLE command is ineffective without defining any alarm selectors." : Added to the response if a selector-specific command is issued without a selector.
+### Persistence
+
+From netdata v1.16.0 and beyond, the silencers configuration is persisted to disk and loaded when netdata starts.
+The JSON string returned by the [LIST command](#list-silencers) is automatically saved to the `silencers file`, every time a command alters the silencers configuration.
+The file's location is configurable in `netdata.conf`. The default is shown below:
+
+```
+[health]
+ # silencers file = /var/lib/netdata/health.silencers.json
+```
+
### Further reading
The test script under [tests/health_mgmtapi](../../../tests/health_mgmtapi) contains a series of tests that you can either run or read through to understand the various calls and responses better.
diff --git a/web/api/health/health_cmdapi.c b/web/api/health/health_cmdapi.c
index ec177751..468054c6 100644
--- a/web/api/health/health_cmdapi.c
+++ b/web/api/health/health_cmdapi.c
@@ -1,17 +1,16 @@
//
-// Created by christopher on 11/12/18.
+// Created by Christopher on 11/12/18.
//
#include "health_cmdapi.h"
-
-static SILENCER *create_silencer(void) {
- SILENCER *t = callocz(1, sizeof(SILENCER));
- debug(D_HEALTH, "HEALTH command API: Created empty silencer");
-
- return t;
-}
-
+/**
+ * Free Silencers
+ *
+ * Clean the silencer structure
+ *
+ * @param t is the structure that will be cleaned.
+ */
void free_silencers(SILENCER *t) {
if (!t) return;
if (t->next) free_silencers(t->next);
@@ -31,38 +30,104 @@ void free_silencers(SILENCER *t) {
return;
}
+/**
+ * Silencers to JSON Entry
+ *
+ * Fill the buffer with the other values given.
+ *
+ * @param wb a pointer to the output buffer
+ * @param var the json variable
+ * @param val the json value
+ * @param hasprev has it a previous value?
+ *
+ * @return
+ */
+int health_silencers2json_entry(BUFFER *wb, char* var, char* val, int hasprev) {
+ if (val) {
+ buffer_sprintf(wb, "%s\n\t\t\t\"%s\": \"%s\"", (hasprev)?",":"", var, val);
+ return 1;
+ } else {
+ return hasprev;
+ }
+}
+/**
+ * Silencer to JSON
+ *
+ * Write the silencer values using JSON format inside a buffer.
+ *
+ * @param wb is the buffer to write the silencers.
+ */
+void health_silencers2json(BUFFER *wb) {
+ buffer_sprintf(wb, "{\n\t\"all\": %s,"
+ "\n\t\"type\": \"%s\","
+ "\n\t\"silencers\": [",
+ (silencers->all_alarms)?"true":"false",
+ (silencers->stype == STYPE_NONE)?"None":((silencers->stype == STYPE_DISABLE_ALARMS)?"DISABLE":"SILENCE"));
+
+ SILENCER *silencer;
+ int i = 0, j = 0;
+ for(silencer = silencers->silencers; silencer ; silencer = silencer->next) {
+ if(likely(i)) buffer_strcat(wb, ",");
+ buffer_strcat(wb, "\n\t\t{");
+ j=health_silencers2json_entry(wb, HEALTH_ALARM_KEY, silencer->alarms, j);
+ j=health_silencers2json_entry(wb, HEALTH_CHART_KEY, silencer->charts, j);
+ j=health_silencers2json_entry(wb, HEALTH_CONTEXT_KEY, silencer->contexts, j);
+ j=health_silencers2json_entry(wb, HEALTH_HOST_KEY, silencer->hosts, j);
+ health_silencers2json_entry(wb, HEALTH_FAMILIES_KEY, silencer->families, j);
+ j=0;
+ buffer_strcat(wb, "\n\t\t}");
+ i++;
+ }
+ if(likely(i)) buffer_strcat(wb, "\n\t");
+ buffer_strcat(wb, "]\n}\n");
+}
+/**
+ * Silencer to FILE
+ *
+ * Write the sliencer buffer to a file.
+ * @param wb
+ */
+void health_silencers2file(BUFFER *wb) {
+ if (wb->len == 0) return;
+
+ FILE *fd = fopen(silencers_filename, "wb");
+ if(fd) {
+ size_t written = (size_t)fprintf(fd, "%s", wb->buffer) ;
+ if (written == wb->len ) {
+ info("Silencer changes written to %s", silencers_filename);
+ }
+ fclose(fd);
+ return;
+ }
+ error("Silencer changes could not be written to %s. Error %s", silencers_filename, strerror(errno));
+}
+
+/**
+ * Request V1 MGMT Health
+ *
+ * Function called by api to management the health.
+ *
+ * @param host main structure with client information!
+ * @param w is the structure with all information of the client request.
+ * @param url is the url that netdata is working
+ *
+ * @return It returns 200 on success and another code otherwise.
+ */
int web_client_api_request_v1_mgmt_health(RRDHOST *host, struct web_client *w, char *url) {
int ret = 400;
(void) host;
-
-
BUFFER *wb = w->response.data;
buffer_flush(wb);
wb->contenttype = CT_TEXT_PLAIN;
buffer_flush(w->response.data);
- static uint32_t
- hash_alarm = 0,
- hash_template = 0,
- hash_chart = 0,
- hash_context = 0,
- hash_host = 0,
- hash_families = 0;
-
- if (unlikely(!hash_alarm)) {
- hash_alarm = simple_uhash(HEALTH_ALARM_KEY);
- hash_template = simple_uhash(HEALTH_TEMPLATE_KEY);
- hash_chart = simple_uhash(HEALTH_CHART_KEY);
- hash_context = simple_uhash(HEALTH_CONTEXT_KEY);
- hash_host = simple_uhash(HEALTH_HOST_KEY);
- hash_families = simple_uhash(HEALTH_FAMILIES_KEY);
- }
-
+ //Local instance of the silencer
SILENCER *silencer = NULL;
+ int config_changed = 1;
if (!w->auth_bearer_token) {
buffer_strcat(wb, HEALTH_CMDAPI_MSG_AUTHERROR);
@@ -105,50 +170,17 @@ int web_client_api_request_v1_mgmt_health(RRDHOST *host, struct web_client *w, c
free_silencers(silencers->silencers);
silencers->silencers = NULL;
buffer_strcat(wb, HEALTH_CMDAPI_MSG_RESET);
+ } else if (!strcmp(value, HEALTH_CMDAPI_CMD_LIST)) {
+ w->response.data->contenttype = CT_APPLICATION_JSON;
+ health_silencers2json(wb);
+ config_changed=0;
}
} else {
- uint32_t hash = simple_uhash(key);
- if (unlikely(silencer == NULL)) {
- if (
- (hash == hash_alarm && !strcasecmp(key, HEALTH_ALARM_KEY)) ||
- (hash == hash_template && !strcasecmp(key, HEALTH_TEMPLATE_KEY)) ||
- (hash == hash_chart && !strcasecmp(key, HEALTH_CHART_KEY)) ||
- (hash == hash_context && !strcasecmp(key, HEALTH_CONTEXT_KEY)) ||
- (hash == hash_host && !strcasecmp(key, HEALTH_HOST_KEY)) ||
- (hash == hash_families && !strcasecmp(key, HEALTH_FAMILIES_KEY))
- ) {
- silencer = create_silencer();
- }
- }
-
- if (hash == hash_alarm && !strcasecmp(key, HEALTH_ALARM_KEY)) {
- silencer->alarms = strdupz(value);
- silencer->alarms_pattern = simple_pattern_create(silencer->alarms, NULL, SIMPLE_PATTERN_EXACT);
- } else if (hash == hash_chart && !strcasecmp(key, HEALTH_CHART_KEY)) {
- silencer->charts = strdupz(value);
- silencer->charts_pattern = simple_pattern_create(silencer->charts, NULL, SIMPLE_PATTERN_EXACT);
- } else if (hash == hash_context && !strcasecmp(key, HEALTH_CONTEXT_KEY)) {
- silencer->contexts = strdupz(value);
- silencer->contexts_pattern = simple_pattern_create(silencer->contexts, NULL, SIMPLE_PATTERN_EXACT);
- } else if (hash == hash_host && !strcasecmp(key, HEALTH_HOST_KEY)) {
- silencer->hosts = strdupz(value);
- silencer->hosts_pattern = simple_pattern_create(silencer->hosts, NULL, SIMPLE_PATTERN_EXACT);
- } else if (hash == hash_families && !strcasecmp(key, HEALTH_FAMILIES_KEY)) {
- silencer->families = strdupz(value);
- silencer->families_pattern = simple_pattern_create(silencer->families, NULL, SIMPLE_PATTERN_EXACT);
- } else {
- buffer_strcat(wb, HEALTH_CMDAPI_MSG_INVALID_KEY);
- }
+ silencer = health_silencers_addparam(silencer, key, value);
}
-
}
if (likely(silencer)) {
- // Add the created instance to the linked list in silencers
- silencer->next = silencers->silencers;
- silencers->silencers = silencer;
- debug(D_HEALTH, "HEALTH command API: Added silencer %s:%s:%s:%s:%s", silencer->alarms,
- silencer->charts, silencer->contexts, silencer->hosts, silencer->families
- );
+ health_silencers_add(silencer);
buffer_strcat(wb, HEALTH_CMDAPI_MSG_ADDED);
if (silencers->stype == STYPE_NONE) {
buffer_strcat(wb, HEALTH_CMDAPI_MSG_STYPEWARNING);
@@ -162,5 +194,11 @@ int web_client_api_request_v1_mgmt_health(RRDHOST *host, struct web_client *w, c
}
w->response.data = wb;
buffer_no_cacheable(w->response.data);
+ if (ret == 200 && config_changed) {
+ BUFFER *jsonb = buffer_create(200);
+ health_silencers2json(jsonb);
+ health_silencers2file(jsonb);
+ }
+
return ret;
}
diff --git a/web/api/health/health_cmdapi.h b/web/api/health/health_cmdapi.h
index d0f30401..d8ec6aaa 100644
--- a/web/api/health/health_cmdapi.h
+++ b/web/api/health/health_cmdapi.h
@@ -12,6 +12,7 @@
#define HEALTH_CMDAPI_CMD_SILENCE "SILENCE"
#define HEALTH_CMDAPI_CMD_DISABLE "DISABLE"
#define HEALTH_CMDAPI_CMD_RESET "RESET"
+#define HEALTH_CMDAPI_CMD_LIST "LIST"
#define HEALTH_CMDAPI_MSG_AUTHERROR "Auth Error\n"
#define HEALTH_CMDAPI_MSG_SILENCEALL "All alarm notifications are silenced\n"
@@ -20,7 +21,6 @@
#define HEALTH_CMDAPI_MSG_DISABLE "Health checks disabled for alarms matching the selectors\n"
#define HEALTH_CMDAPI_MSG_SILENCE "Alarm notifications silenced for alarms matching the selectors\n"
#define HEALTH_CMDAPI_MSG_ADDED "Alarm selector added\n"
-#define HEALTH_CMDAPI_MSG_INVALID_KEY "Invalid key. Ignoring it.\n"
#define HEALTH_CMDAPI_MSG_STYPEWARNING "WARNING: Added alarm selector to silence/disable alarms without a SILENCE or DISABLE command.\n"
#define HEALTH_CMDAPI_MSG_NOSELECTORWARNING "WARNING: SILENCE or DISABLE command is ineffective without defining any alarm selectors.\n"
diff --git a/web/api/netdata-swagger.json b/web/api/netdata-swagger.json
index 2fa55c4f..63bc5638 100644
--- a/web/api/netdata-swagger.json
+++ b/web/api/netdata-swagger.json
@@ -77,6 +77,39 @@
}
}
},
+ "/alarm_variables": {
+ "get": {
+ "summary": "List variables available to configure alarms for a chart",
+ "description": "Returns the basic information of a chart and all the variables that can be used in alarm and template health configurations for the particular chart or family",
+ "parameters": [
+ {
+ "name": "chart",
+ "in": "query",
+ "description": "The id of the chart as returned by the /charts call.",
+ "required": true,
+ "type": "string",
+ "format": "as returned by /charts"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "A javascript object with information about the chart and the available variables",
+ "schema": {
+ "$ref": "#/definitions/alarm_variables"
+ }
+ },
+ "400": {
+ "description": "Bad request - the body will include a message stating what is wrong."
+ },
+ "404": {
+ "description": "No chart with the given id is found."
+ },
+ "500": {
+ "description": "Internal server error. This usually means the server is out of memory."
+ }
+ }
+ }
+ },
"/data": {
"get": {
"summary": "Get collected data for a specific chart",
@@ -631,7 +664,7 @@
{
"name": "cmd",
"in": "query",
- "description": "DISABLE ALL: No alarm criteria are evaluated, nothing is written in the alarm log. SILENCE ALL: No notifications are sent. RESET: Return to the default state. DISABLE/SILENCE: Set the mode to be used for the alarms matching the criteria of the alarm selectors.",
+ "description": "DISABLE ALL: No alarm criteria are evaluated, nothing is written in the alarm log. SILENCE ALL: No notifications are sent. RESET: Return to the default state. DISABLE/SILENCE: Set the mode to be used for the alarms matching the criteria of the alarm selectors. LIST: Show active configuration.",
"required": false,
"type": "string",
"enum": [
@@ -639,7 +672,8 @@
"SILENCE ALL",
"DISABLE",
"SILENCE",
- "RESET"
+ "RESET",
+ "LIST"
]
},
{
@@ -951,6 +985,70 @@
}
}
},
+ "alarm_variables": {
+ "type": "object",
+ "properties": {
+ "chart": {
+ "type": "string",
+ "description": "The unique id of the chart"
+ },
+ "chart_name": {
+ "type": "string",
+ "description": "The name of the chart"
+ },
+ "cnart_context": {
+ "type": "string",
+ "description": "The context of the chart. It is shared across multiple monitored software or hardware instances and used in alarm templates"
+ },
+ "family": {
+ "type": "string",
+ "description": "The family of the chart."
+ },
+ "host": {
+ "type": "string",
+ "description": "The host containing the chart."
+ },
+ "chart_variables": {
+ "type": "object",
+ "properties": {
+ "varname1": {
+ "type": "number",
+ "format": "float"
+ },
+ "varname2": {
+ "type": "number",
+ "format": "float"
+ }
+ }
+ },
+ "family_variables": {
+ "type": "object",
+ "properties": {
+ "varname1": {
+ "type": "number",
+ "format": "float"
+ },
+ "varname2": {
+ "type": "number",
+ "format": "float"
+ }
+ }
+ },
+ "host_variables": {
+ "type": "object",
+ "properties": {
+ "varname1": {
+ "type": "number",
+ "format": "float"
+ },
+ "varname2": {
+ "type": "number",
+ "format": "float"
+ }
+ }
+ }
+ }
+ },
"dimension": {
"type": "object",
"properties": {
@@ -1208,6 +1306,14 @@
"crit_parsed": {
"type": "string"
},
+ "warn_repeat_every": {
+ "type": "integer",
+ "format": "int32"
+ },
+ "crit_repeat_every": {
+ "type": "integer",
+ "format": "int32"
+ },
"green": {
"type": "string",
"format": "nullable"
diff --git a/web/api/netdata-swagger.yaml b/web/api/netdata-swagger.yaml
index c021efef..3386e01a 100644
--- a/web/api/netdata-swagger.yaml
+++ b/web/api/netdata-swagger.yaml
@@ -63,6 +63,28 @@ paths:
$ref: '#/definitions/chart'
'404':
description: 'No chart with the given id is found.'
+ /alarm_variables:
+ get:
+ summary: 'List variables available to configure alarms for a chart'
+ description: 'Returns the basic information of a chart and all the variables that can be used in alarm and template health configurations for the particular chart or family'
+ parameters:
+ - name: chart
+ in: query
+ description: 'The id of the chart as returned by the /charts call.'
+ required: true
+ type: string
+ format: 'as returned by /charts'
+ responses:
+ '200':
+ description: 'A javascript object with information about the chart and the available variables'
+ schema:
+ $ref: '#/definitions/alarm_variables'
+ '400':
+ description: 'Bad request - the body will include a message stating what is wrong.'
+ '404':
+ description: 'No chart with the given id is found.'
+ '500':
+ description: 'Internal server error. This usually means the server is out of memory.'
/data:
get:
summary: 'Get collected data for a specific chart'
@@ -415,10 +437,10 @@ paths:
parameters:
- name: cmd
in: query
- description: 'DISABLE ALL: No alarm criteria are evaluated, nothing is written in the alarm log. SILENCE ALL: No notifications are sent. RESET: Return to the default state. DISABLE/SILENCE: Set the mode to be used for the alarms matching the criteria of the alarm selectors.'
+ description: 'DISABLE ALL: No alarm criteria are evaluated, nothing is written in the alarm log. SILENCE ALL: No notifications are sent. RESET: Return to the default state. DISABLE/SILENCE: Set the mode to be used for the alarms matching the criteria of the alarm selectors. LIST: Show active configuration.'
required: false
type: string
- enum: ['DISABLE ALL', 'SILENCE ALL', 'DISABLE', 'SILENCE', 'RESET']
+ enum: ['DISABLE ALL', 'SILENCE ALL', 'DISABLE', 'SILENCE', 'RESET', 'LIST']
- name: alarm
in: query
description: 'The expression provided will match both `alarm` and `template` names.'
@@ -638,6 +660,51 @@ definitions:
red:
type: number
description: 'Chart health red trheshold'
+ alarm_variables:
+ type: object
+ properties:
+ chart:
+ type: string
+ description: 'The unique id of the chart'
+ chart_name:
+ type: string
+ description: 'The name of the chart'
+ cnart_context:
+ type: string
+ description: 'The context of the chart. It is shared across multiple monitored software or hardware instances and used in alarm templates'
+ family:
+ type: string
+ description: 'The family of the chart.'
+ host:
+ type: string
+ description: 'The host containing the chart.'
+ chart_variables:
+ type: object
+ properties:
+ varname1:
+ type: number
+ format: float
+ varname2:
+ type: number
+ format: float
+ family_variables:
+ type: object
+ properties:
+ varname1:
+ type: number
+ format: float
+ varname2:
+ type: number
+ format: float
+ host_variables:
+ type: object
+ properties:
+ varname1:
+ type: number
+ format: float
+ varname2:
+ type: number
+ format: float
dimension:
type: object
properties:
@@ -825,6 +892,12 @@ definitions:
type: string
crit_parsed:
type: string
+ warn_repeat_every:
+ type: integer
+ format: int32
+ crit_repeat_every:
+ type: integer
+ format: int32
green:
type: string
format: nullable
diff --git a/web/gui/Makefile.am b/web/gui/Makefile.am
index 7d1ceef9..ef8aa05f 100644
--- a/web/gui/Makefile.am
+++ b/web/gui/Makefile.am
@@ -61,6 +61,7 @@ dist_web_DATA = \
index.html \
main.css \
main.js \
+ console.html \
infographic.html \
robots.txt \
refresh-badges.js \
@@ -69,12 +70,6 @@ dist_web_DATA = \
version.txt \
$(NULL)
-
-webconsoledir=$(webdir)/console
-dist_webconsole_DATA = \
- console/index.html \
- $(NULL)
-
webstaticdir=$(webdir)/static/img
dist_webstatic_DATA = \
static/img/netdata-logomark.svg \
diff --git a/web/gui/console/index.html b/web/gui/console.html
index 72320191..942c8c3c 100644
--- a/web/gui/console/index.html
+++ b/web/gui/console.html
@@ -10,7 +10,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
- <link rel="icon" href="/favicon.ico" />
+ <link rel="icon" href="favicon.ico" />
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons" />
<!-- Google Tag Manager -->
<script>(function (w, d, s, l, i) {
@@ -66,7 +66,7 @@
var netdataTheme = "white"
var netdataNoBootstrap = true
</script>
- <script type="text/javascript" src="/dashboard.js?v20190515"></script>
+ <script type="text/javascript" src="dashboard.js?v20190523"></script>
</body>
</html> \ No newline at end of file
diff --git a/web/gui/dashboard_info.js b/web/gui/dashboard_info.js
index aab11ba3..0013311e 100644
--- a/web/gui/dashboard_info.js
+++ b/web/gui/dashboard_info.js
@@ -267,6 +267,12 @@ netdataDashboard.menu = {
info: 'Performance metrics for <b>RetroShare</b>. RetroShare is open source software for encrypted filesharing, serverless email, instant messaging, online chat, and BBS, based on a friend-to-friend network built on GNU Privacy Guard (GPG).'
},
+ 'riakkv': {
+ title: 'Riak KV',
+ icon: '<i class="fas fa-database"></i>',
+ info: 'Metrics for <b>Riak KV</b>, the distributed key-value store.'
+ },
+
'ipfs': {
title: 'IPFS',
icon: '<i class="fas fa-folder-open"></i>',
@@ -279,6 +285,13 @@ netdataDashboard.menu = {
info: 'Performance metrics for <b>PHP-FPM</b>, an alternative FastCGI implementation for PHP.'
},
+ 'pihole': {
+ title: 'Pi-hole',
+ icon: '<i class="fas fa-ban"></i>',
+ info: 'Metrics for <a href="https://pi-hole.net/" target="_blank">Pi-hole</a>, a black hole for Internet advertisements.' +
+ ' The metrics returned by Pi-Hole API is all from the last 24 hours.'
+ },
+
'portcheck': {
title: 'Port Check',
icon: '<i class="fas fa-heartbeat"></i>',
@@ -324,7 +337,7 @@ netdataDashboard.menu = {
'web_log': {
title: undefined,
icon: '<i class="fas fa-file-alt"></i>',
- info: 'Information extracted from a server log file. <code>web_log</code> plugin incrementally parses the server log file to provide, in real-time, a break down of key server performance metrics. For web servers, an extended log file format may optionally be used (for <code>nginx</code> and <code>apache</code>) offering timing information and bandwidth for both requests and responses. <code>web_log</code> plugin may also be configured to provide a break down of requests per URL pattern (check <a href="https://github.com/netdata/netdata/blob/master/conf.d/python.d/web_log.conf" target="_blank"><code>/etc/netdata/python.d/web_log.conf</code></a>).'
+ info: 'Information extracted from a server log file. <code>web_log</code> plugin incrementally parses the server log file to provide, in real-time, a break down of key server performance metrics. For web servers, an extended log file format may optionally be used (for <code>nginx</code> and <code>apache</code>) offering timing information and bandwidth for both requests and responses. <code>web_log</code> plugin may also be configured to provide a break down of requests per URL pattern (check <a href="https://github.com/netdata/netdata/blob/master/collectors/python.d.plugin/web_log/web_log.conf" target="_blank"><code>/etc/netdata/python.d/web_log.conf</code></a>).'
},
'named': {
@@ -461,6 +474,18 @@ netdataDashboard.menu = {
title: '',
icon: '<i class="fas fa-th-large"></i>',
info: 'Xen domain resource utilization metrics. Netdata reads this information using <b>xenstat</b> library which gives access to the resource usage information (CPU, memory, disk I/O, network) for a virtual machine.'
+ },
+
+ 'wmi': {
+ title: 'wmi',
+ icon: '<i class="fas fa-server"></i>',
+ info: undefined
+ },
+
+ 'perf': {
+ title: 'Perf Counters',
+ icon: '<i class="fas fa-tachometer-alt"></i>',
+ info: 'Performance Monitoring Counters (PMC). Data collected using <b>perf_event_open()</b> system call which utilises Hardware Performance Monitoring Units (PMU).'
}
};
@@ -515,7 +540,7 @@ netdataDashboard.submenu = {
},
'web_log.urls': {
- info: 'Number of requests for each <code>URL pattern</code> defined in <a href="https://github.com/netdata/netdata/blob/master/conf.d/python.d/web_log.conf" target="_blank"><code>/etc/netdata/python.d/web_log.conf</code></a>. This chart counts all requests matching the URL patterns defined, independently of the web server response codes (i.e. both successful and unsuccessful).'
+ info: 'Number of requests for each <code>URL pattern</code> defined in <a href="https://github.com/netdata/netdata/blob/master/collectors/python.d.plugin/web_log/web_log.conf" target="_blank"><code>/etc/netdata/python.d/web_log.conf</code></a>. This chart counts all requests matching the URL patterns defined, independently of the web server response codes (i.e. both successful and unsuccessful).'
},
'web_log.clients': {
@@ -1163,6 +1188,10 @@ netdataDashboard.context = {
'</ul>'
},
+ 'mysql.innodb_deadlocks': {
+ 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>.'
+ },
+
// ------------------------------------------------------------------------
// POSTGRESQL
@@ -1806,7 +1835,7 @@ netdataDashboard.context = {
},
'web_log.clients_all': {
- info: 'Unique client IPs accessing the web server since the last restart of netdata. This plugin keeps in memory all the unique IPs that have accessed the web server. On very busy web servers (several millions of unique IPs) you may want to disable this chart (check <a href="https://github.com/netdata/netdata/blob/master/conf.d/python.d/web_log.conf" target="_blank"><code>/etc/netdata/python.d/web_log.conf</code></a>).'
+ info: 'Unique client IPs accessing the web server since the last restart of netdata. This plugin keeps in memory all the unique IPs that have accessed the web server. On very busy web servers (several millions of unique IPs) you may want to disable this chart (check <a href="https://github.com/netdata/netdata/blob/master/collectors/python.d.plugin/web_log/web_log.conf" target="_blank"><code>/etc/netdata/python.d/web_log.conf</code></a>).'
},
// ------------------------------------------------------------------------
@@ -1937,7 +1966,7 @@ netdataDashboard.context = {
},
'web_log.squid_clients_all': {
- info: 'Unique client IPs accessing squid since the last restart of netdata. This plugin keeps in memory all the unique IPs that have accessed the server. On very busy squid servers (several millions of unique IPs) you may want to disable this chart (check <a href="https://github.com/netdata/netdata/blob/master/conf.d/python.d/web_log.conf" target="_blank"><code>/etc/netdata/python.d/web_log.conf</code></a>).'
+ info: 'Unique client IPs accessing squid since the last restart of netdata. This plugin keeps in memory all the unique IPs that have accessed the server. On very busy squid servers (several millions of unique IPs) you may want to disable this chart (check <a href="https://github.com/netdata/netdata/blob/master/collectors/python.d.plugin/web_log/web_log.conf" target="_blank"><code>/etc/netdata/python.d/web_log.conf</code></a>).'
},
'web_log.squid_transport_methods': {
diff --git a/web/gui/demosites.html b/web/gui/demosites.html
index f908e0b4..e00fbbfd 100644
--- a/web/gui/demosites.html
+++ b/web/gui/demosites.html
@@ -2,6 +2,7 @@
<!-- SPDX-License-Identifier: GPL-3.0-or-later -->
<html lang=en-us xmlns="http://www.w3.org/1999/html">
<head>
+ <meta http-equiv="Refresh" content="0; url=https://www.netdata.cloud">
<meta charset=utf-8>
<title>NetData: Get control of your Linux Servers. Simple. Effective. Awesome.</title>
<meta name=author content="Costa Tsaousis">
diff --git a/web/gui/favicon.ico b/web/gui/favicon.ico
index 857c582d..064032ae 100644
--- a/web/gui/favicon.ico
+++ b/web/gui/favicon.ico
Binary files differ
diff --git a/web/gui/images/android-icon-144x144.png b/web/gui/images/android-icon-144x144.png
index c3013cc9..69efa5a2 100644
--- a/web/gui/images/android-icon-144x144.png
+++ b/web/gui/images/android-icon-144x144.png
Binary files differ
diff --git a/web/gui/images/android-icon-192x192.png b/web/gui/images/android-icon-192x192.png
index 77d18d9c..e5744357 100644
--- a/web/gui/images/android-icon-192x192.png
+++ b/web/gui/images/android-icon-192x192.png
Binary files differ
diff --git a/web/gui/images/android-icon-36x36.png b/web/gui/images/android-icon-36x36.png
index 74576f6b..4ba804d9 100644
--- a/web/gui/images/android-icon-36x36.png
+++ b/web/gui/images/android-icon-36x36.png
Binary files differ
diff --git a/web/gui/images/android-icon-48x48.png b/web/gui/images/android-icon-48x48.png
index 5666fa10..04970d4b 100644
--- a/web/gui/images/android-icon-48x48.png
+++ b/web/gui/images/android-icon-48x48.png
Binary files differ
diff --git a/web/gui/images/android-icon-72x72.png b/web/gui/images/android-icon-72x72.png
index 7f7043f1..5cbc701e 100644
--- a/web/gui/images/android-icon-72x72.png
+++ b/web/gui/images/android-icon-72x72.png
Binary files differ
diff --git a/web/gui/images/android-icon-96x96.png b/web/gui/images/android-icon-96x96.png
index 1bbf594d..21f27cea 100644
--- a/web/gui/images/android-icon-96x96.png
+++ b/web/gui/images/android-icon-96x96.png
Binary files differ
diff --git a/web/gui/images/apple-icon-114x114.png b/web/gui/images/apple-icon-114x114.png
index 7d093e85..7993e055 100644
--- a/web/gui/images/apple-icon-114x114.png
+++ b/web/gui/images/apple-icon-114x114.png
Binary files differ
diff --git a/web/gui/images/apple-icon-120x120.png b/web/gui/images/apple-icon-120x120.png
index d4c38e7b..3fbe8fda 100644
--- a/web/gui/images/apple-icon-120x120.png
+++ b/web/gui/images/apple-icon-120x120.png
Binary files differ
diff --git a/web/gui/images/apple-icon-144x144.png b/web/gui/images/apple-icon-144x144.png
index c3013cc9..8d465692 100644
--- a/web/gui/images/apple-icon-144x144.png
+++ b/web/gui/images/apple-icon-144x144.png
Binary files differ
diff --git a/web/gui/images/apple-icon-152x152.png b/web/gui/images/apple-icon-152x152.png
index c92f3817..11a10723 100644
--- a/web/gui/images/apple-icon-152x152.png
+++ b/web/gui/images/apple-icon-152x152.png
Binary files differ
diff --git a/web/gui/images/apple-icon-180x180.png b/web/gui/images/apple-icon-180x180.png
index 1a58fdbb..314efb12 100644
--- a/web/gui/images/apple-icon-180x180.png
+++ b/web/gui/images/apple-icon-180x180.png
Binary files differ
diff --git a/web/gui/images/apple-icon-57x57.png b/web/gui/images/apple-icon-57x57.png
index 36c273ce..85283616 100644
--- a/web/gui/images/apple-icon-57x57.png
+++ b/web/gui/images/apple-icon-57x57.png
Binary files differ
diff --git a/web/gui/images/apple-icon-60x60.png b/web/gui/images/apple-icon-60x60.png
index c3c48c8b..2662e85d 100644
--- a/web/gui/images/apple-icon-60x60.png
+++ b/web/gui/images/apple-icon-60x60.png
Binary files differ
diff --git a/web/gui/images/apple-icon-72x72.png b/web/gui/images/apple-icon-72x72.png
index 7f7043f1..4a6b056e 100644
--- a/web/gui/images/apple-icon-72x72.png
+++ b/web/gui/images/apple-icon-72x72.png
Binary files differ
diff --git a/web/gui/images/apple-icon-76x76.png b/web/gui/images/apple-icon-76x76.png
index b5e73cd4..c2bf6c9f 100644
--- a/web/gui/images/apple-icon-76x76.png
+++ b/web/gui/images/apple-icon-76x76.png
Binary files differ
diff --git a/web/gui/images/apple-icon-precomposed.png b/web/gui/images/apple-icon-precomposed.png
index f69945bf..9c3e73ef 100644
--- a/web/gui/images/apple-icon-precomposed.png
+++ b/web/gui/images/apple-icon-precomposed.png
Binary files differ
diff --git a/web/gui/images/apple-icon.png b/web/gui/images/apple-icon.png
index f69945bf..9c3e73ef 100644
--- a/web/gui/images/apple-icon.png
+++ b/web/gui/images/apple-icon.png
Binary files differ
diff --git a/web/gui/images/banner-icon-144x144.png b/web/gui/images/banner-icon-144x144.png
index c3013cc9..fef3dca1 100644
--- a/web/gui/images/banner-icon-144x144.png
+++ b/web/gui/images/banner-icon-144x144.png
Binary files differ
diff --git a/web/gui/images/favicon-128.png b/web/gui/images/favicon-128.png
new file mode 100644
index 00000000..5371f920
--- /dev/null
+++ b/web/gui/images/favicon-128.png
Binary files differ
diff --git a/web/gui/images/favicon-16x16.png b/web/gui/images/favicon-16x16.png
index 43eb188f..5729f5a2 100644
--- a/web/gui/images/favicon-16x16.png
+++ b/web/gui/images/favicon-16x16.png
Binary files differ
diff --git a/web/gui/images/favicon-196x196.png b/web/gui/images/favicon-196x196.png
new file mode 100644
index 00000000..a208c27f
--- /dev/null
+++ b/web/gui/images/favicon-196x196.png
Binary files differ
diff --git a/web/gui/images/favicon-32x32.png b/web/gui/images/favicon-32x32.png
index e657e921..cdb0a480 100644
--- a/web/gui/images/favicon-32x32.png
+++ b/web/gui/images/favicon-32x32.png
Binary files differ
diff --git a/web/gui/images/favicon-96x96.png b/web/gui/images/favicon-96x96.png
index 1bbf594d..dbe7dea2 100644
--- a/web/gui/images/favicon-96x96.png
+++ b/web/gui/images/favicon-96x96.png
Binary files differ
diff --git a/web/gui/images/favicon.ico b/web/gui/images/favicon.ico
index 7ed95725..064032ae 100644
--- a/web/gui/images/favicon.ico
+++ b/web/gui/images/favicon.ico
Binary files differ
diff --git a/web/gui/images/ms-icon-144x144.png b/web/gui/images/ms-icon-144x144.png
index c3013cc9..8d465692 100644
--- a/web/gui/images/ms-icon-144x144.png
+++ b/web/gui/images/ms-icon-144x144.png
Binary files differ
diff --git a/web/gui/images/ms-icon-150x150.png b/web/gui/images/ms-icon-150x150.png
index f0cf4128..4683d56a 100644
--- a/web/gui/images/ms-icon-150x150.png
+++ b/web/gui/images/ms-icon-150x150.png
Binary files differ
diff --git a/web/gui/images/ms-icon-310x150.png b/web/gui/images/ms-icon-310x150.png
new file mode 100644
index 00000000..5d4ac57b
--- /dev/null
+++ b/web/gui/images/ms-icon-310x150.png
Binary files differ
diff --git a/web/gui/images/ms-icon-310x310.png b/web/gui/images/ms-icon-310x310.png
index 4f5f7e62..bdb591b2 100644
--- a/web/gui/images/ms-icon-310x310.png
+++ b/web/gui/images/ms-icon-310x310.png
Binary files differ
diff --git a/web/gui/images/ms-icon-36x36.png b/web/gui/images/ms-icon-36x36.png
new file mode 100644
index 00000000..e251302e
--- /dev/null
+++ b/web/gui/images/ms-icon-36x36.png
Binary files differ
diff --git a/web/gui/images/ms-icon-70x70.png b/web/gui/images/ms-icon-70x70.png
index 70012c61..5371f920 100644
--- a/web/gui/images/ms-icon-70x70.png
+++ b/web/gui/images/ms-icon-70x70.png
Binary files differ
diff --git a/web/gui/images/netdata-logomark.svg b/web/gui/images/netdata-logomark.svg
index 87fb2bda..18152fb7 100644
--- a/web/gui/images/netdata-logomark.svg
+++ b/web/gui/images/netdata-logomark.svg
@@ -1,3 +1,8 @@
-<svg width="1723" height="1723" viewBox="0 0 1723 1723" fill="none" xmlns="http://www.w3.org/2000/svg">
-<path fill-rule="evenodd" clip-rule="evenodd" d="M0.628784 849.678C0.628784 473.909 235.042 153.621 563.766 30.7914C701.438 19.0613 843.892 50.2449 970.557 129.297C1052.47 180.42 1119.71 246.528 1170.96 321.982C1161.21 207.568 1122.97 96.4678 1058.94 0.187012C1220.56 38.587 1364.64 123.126 1476.91 239.343C1518.34 297.634 1548.55 365.545 1563.67 440.489C1578.54 514.244 1577.35 587.545 1562.5 656.661C1601.04 613.105 1632.22 563.24 1654.63 509.251C1698.41 613.852 1722.63 728.899 1722.63 849.678C1722.63 1331.55 1337.15 1722.19 861.629 1722.19C386.112 1722.19 0.628784 1331.55 0.628784 849.678ZM1178.87 1369.04C1286.71 1369.04 1374.13 1280.45 1374.13 1171.17C1374.13 1061.88 1286.71 973.293 1178.87 973.293C1071.03 973.293 983.603 1061.88 983.603 1171.17C983.603 1280.45 1071.03 1369.04 1178.87 1369.04Z" fill="#00C853"/>
-</svg>
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="500px" height="500px" viewBox="0 0 500 500" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+ <g id="Artboard" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+ <g id="logo_green_fill" transform="translate(0.000000, 49.000000)" fill="#00FF00" fill-rule="nonzero">
+ <path d="M307.477876,400.442478 L206.814159,400.442478 L0.486725664,2.21238938 L293.318584,2.21238938 C407.146221,2.33432589 499.391338,94.5794427 499.513274,208.40708 C499.391356,314.41476 413.485557,400.32056 307.477876,400.442478 L307.477876,400.442478 Z" id="Path"></path>
+ </g>
+ </g>
+</svg> \ No newline at end of file
diff --git a/web/gui/index.html b/web/gui/index.html
index c9dd89b2..4a8647dd 100644
--- a/web/gui/index.html
+++ b/web/gui/index.html
@@ -15,8 +15,7 @@
<link rel="stylesheet" type="text/css" href="main.css?v=5">
- <link rel="icon" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAACXBIWXMAAA7EAAAOxAGVKw4bAAACyklEQVRYhcWXz0sUYRjHP8+wLItImEhnEfEgEVJk+56CbCECQWezOkTQTTp6kejgKUSoEAoK/wAhaaeDBkLoIahZIWIJo0U8hHQJJBYJERGfDrOju7PjuO5u6/c0PM/7zvfzvvP+eEaoVu6wBZIA3cW82wtvY7cD1xEWSTpb1bxWjm2RtbtQxoF7iLSA7gO/UL4A74G3mBIzNz0Peg1hFmUK46zXBuDaCYQJlDFE4ke/QreAZyhPMc42rt2PyEoxt4cyDUxgnO3qAVy7C8gg0hdFHwBZA0ZIOt9w7e+I9B6mdBVhmGTlbFQCuPYFhA8g56o3P3D6CwyiDCIyFshtgqRIZnKlUStg3lW7OYC0AvPATkiuA9U3uOlEOEDWTgCZ2s3LIMbCU9ID3A8HUCZO9s2jGCRxdFIflDUF/EX3I3q1N0iq+8BZf+v6MzDeFHMAEQvoJpuOAwiu3Qr89g6ZJkm5CnoHeBwDbjTV3CPoRRhF+WQBA801B+AJiAUMWMDFptuLtBef+iygs+kAh+q0gDOnCNBmETyOmywLKJyifyEG/ATqPP+LUt0FZoAlIA6MgNyKqDo2YkAO6G+QeQrjfCyJzuHaoyCvjuiVs4q0jdBMwNyTcV6j+jm0h7BkAYuohpZLJ1TUQJYrIqo7KAtW8VaabQBA1GVWmROZwzgFfwtOoRpealevdGg0a8eAoUB0D9VJ8M8A46yDTNdlL9wmaz8MMX9RrIQOpfoS4+S9br7cdAvoCiLn6wLxFtwyQhxlKMQ8j3CJpFeml+9QN90N6iLSURfEkXD8AQwms+aHyo9hk1kHSaG6+Z/MU6XmlQAeRA7EoLraQPM83si/BlPhF5E3E1dQfY5S++5Q/9dMLwdH7uv4n1PX7gEeAXejy+0y4x2QOWASk8lHNT0e4AAk3QZ6E6+E68MrZNqK2QKwgXevLCEskHSqumX/AUXU5QBtOC5FAAAAAElFTkSuQmCC">
-
+ <link rel="icon" href="data:image/x-icon;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAP9JREFUeNpiYBgFo+A/w34gpiZ8DzWzAYgNiHGAA5UdgA73g+2gcyhgg/0DGQoweB6IBQYyFCCOGOBQwBMd/xnW09ERDtgcoEBHB+zHFQrz6egIBUasocDAcJ9OxWAhE4YQI8MDILmATg7wZ8QRDfQKhQf4Cie6pAVGPA4AhQKo0BCgZRAw4ZSBpIWJNI6CD4wEKikBaFqgVSgcYMIrzcjwgcahcIGRiPYCLUPBkNhWUwP9akVcoQBpatG4MsLviAIqWj6f3Absfdq2igg7IIEKDVQKEzN5ofAenJCp1I8gJRTug5tfkGIdR1FDniMI+QZUjF8Amn5htOdHCAAEGACE6B0cS6mrEwAAAABJRU5ErkJggg==" />
<!-- <link rel="apple-touch-icon" sizes="57x57" href="images/apple-icon-57x57.png">
<link rel="apple-touch-icon" sizes="60x60" href="images/apple-icon-60x60.png">
<link rel="apple-touch-icon" sizes="72x72" href="images/apple-icon-72x72.png">
@@ -124,7 +123,7 @@
</div>
<nav class="collapse navbar-collapse navbar-right" role="navigation">
<ul class="nav navbar-nav">
- <li title="Nodes view" data-toggle="tooltip" data-placement="bottom"><a onclick="openAuthenticatedUrl('console/index.html');" class="btn" target="_blank"><i class="fas fa-tv"></i>&nbsp;<span class="hidden-sm hidden-md">Nodes<sup class="beta"> beta</sup></span></a></li>
+ <li title="Nodes view" data-toggle="tooltip" data-placement="bottom"><a onclick="openAuthenticatedUrl('console.html');" class="btn" target="_blank"><i class="fas fa-tv"></i>&nbsp;<span class="hidden-sm hidden-md">Nodes<sup class="beta"> beta</sup></span></a></li>
<li id="alarmsButton" title="check the health monitoring alarms and their log" data-toggle="tooltip" data-placement="bottom"><a href="#" class="btn" data-toggle="modal" data-target="#alarmsModal"><i class="fas fa-bell"></i>&nbsp;<span class="hidden-sm hidden-md">Alarms&nbsp;</span><span id="alarms_count_badge" class="badge"></span></a></li>
<li title="change dashboard settings" data-toggle="tooltip" data-placement="bottom"><a href="#" class="btn" data-toggle="modal" data-target="#optionsModal"><i class="fas fa-cog"></i>&nbsp;<span class="hidden-sm hidden-md">Settings</span></a></li>
<li title="check for netdata updates<br/>you should keep your netdata updated" data-toggle="tooltip" data-placement="bottom" class="hidden-sm" id="updateButton"><a href="#" class="btn" data-toggle="modal" data-target="#updateModal"><i class="fas fa-cloud-download-alt"></i> <span class="hidden-sm hidden-md">Update </span><span id="update_badge" class="badge"></span></a></li>
@@ -1371,6 +1370,6 @@
</div>
<iframe id="ssoifrm" width="0" height="0"></iframe>
<div id="hiddenDownloadLinks" style="display: none;" hidden></div>
- <script type="text/javascript" src="dashboard.js?v20190130-1"></script>
+ <script type="text/javascript" src="dashboard.js?v20190621-1"></script>
</body>
</html>
diff --git a/web/gui/main.css b/web/gui/main.css
index 57115414..2ddb776e 100644
--- a/web/gui/main.css
+++ b/web/gui/main.css
@@ -322,6 +322,7 @@ body.modal-open {
.sidebar-body {
position: absolute;
display: none;
+ height: 100vh;
}
.dashboard-section-container {
diff --git a/web/gui/main.js b/web/gui/main.js
index 277ae840..65c4d4a8 100644
--- a/web/gui/main.js
+++ b/web/gui/main.js
@@ -775,7 +775,7 @@ function renderMyNetdataMenu(machinesArray) {
html += (
`<div class="agent-item">
<i class="fas fa-tv"></i>
- <a onClick="openAuthenticatedUrl('console/index.html');" target="_blank">Nodes<sup class="beta"> beta</sup></a>
+ <a onClick="openAuthenticatedUrl('console.html');" target="_blank">Nodes<sup class="beta"> beta</sup></a>
<div></div>
</div>
<div class="agent-item">
@@ -793,7 +793,7 @@ function renderMyNetdataMenu(machinesArray) {
html += (
`<div class="agent-item">
<i class="fas fa-tv"></i>
- <a onclick="openAuthenticatedUrl('console/index.html');" target="_blank">Nodes<sup class="beta"> beta</sup></a>
+ <a onclick="openAuthenticatedUrl('console.html');" target="_blank">Nodes<sup class="beta"> beta</sup></a>
<div></div>
</div>
<div class="agent-item">
@@ -1993,7 +1993,7 @@ function clipboardCopyBadgeEmbed(url) {
function alarmsUpdateModal() {
var active = '<h3>Raised Alarms</h3><table class="table">';
var all = '<h3>All Running Alarms</h3><div class="panel-group" id="alarms_all_accordion" role="tablist" aria-multiselectable="true">';
- var footer = '<hr/><a href="https://github.com/netdata/netdata/tree/master/web/api/badges#netdata-badges" target="_blank">netdata badges</a> refresh automatically. Their color indicates the state of the alarm: <span style="color: #e05d44"><b>&nbsp;red&nbsp;</b></span> is critical, <span style="color:#fe7d37"><b>&nbsp;orange&nbsp;</b></span> is warning, <span style="color: #4c1"><b>&nbsp;bright green&nbsp;</b></span> is ok, <span style="color: #9f9f9f"><b>&nbsp;light grey&nbsp;</b></span> is undefined (i.e. no data or no status), <span style="color: #000"><b>&nbsp;black&nbsp;</b></span> is not initialized. You can copy and paste their URLs to embed them in any web page.<br/>netdata can send notifications for these alarms. Check <a href="https://github.com/netdata/netdata/blob/master/health/notifications/health_alarm_notify.conf">this configuration file</a> for more information.';
+ var footer = '<hr/><a href="https://github.com/netdata/netdata/tree/master/web/api/badges#netdata-badges" target="_blank">netdata badges</a> refresh automatically. Their color indicates the state of the alarm: <span style="color: #e05d44"><b>&nbsp;red&nbsp;</b></span> is critical, <span style="color:#fe7d37"><b>&nbsp;orange&nbsp;</b></span> is warning, <span style="color: #4c1"><b>&nbsp;bright green&nbsp;</b></span> is ok, <span style="color: #9f9f9f"><b>&nbsp;light grey&nbsp;</b></span> is undefined (i.e. no data or no status), <span style="color: #000"><b>&nbsp;black&nbsp;</b></span> is not initialized. You can copy and paste their URLs to embed them in any web page.<br/>netdata can send notifications for these alarms. Check <a href="https://github.com/netdata/netdata/blob/master/health/notifications/health_alarm_notify.conf" target="_blank">this configuration file</a> for more information.';
loadClipboard(function () {
});
@@ -2100,6 +2100,14 @@ function alarmsUpdateModal() {
+ ((chart.red !== null) ? ('<tr><td width="10%" style="text-align:right">red&nbsp;threshold</td><td><code>' + chart.red + ' ' + units + '</code></td></tr>') : '');
}
+ if (alarm.warn_repeat_every > 0) {
+ html += '<tr><td width="10%" style="text-align:right">repeat&nbsp;warning</td><td>' + NETDATA.seconds4human(alarm.warn_repeat_every) + '</td></tr>';
+ }
+
+ if (alarm.crit_repeat_every > 0) {
+ html += '<tr><td width="10%" style="text-align:right">repeat&nbsp;critical</td><td>' + NETDATA.seconds4human(alarm.crit_repeat_every) + '</td></tr>';
+ }
+
var delay = '';
if ((alarm.delay_up_duration > 0 || alarm.delay_down_duration > 0) && alarm.delay_multiplier !== 0 && alarm.delay_max_duration > 0) {
if (alarm.delay_up_duration === alarm.delay_down_duration) {
@@ -4840,12 +4848,11 @@ function renderAccountUI() {
container.removeAttribute("title");
container.removeAttribute("data-original-title");
container.removeAttribute("data-placement");
- // <a href="/console/index.html#/charts/${NETDATA.registry.machine_guid}" target="_blank" class="btn">
container.innerHTML = (
`<a href="#" class="dropdown-toggle" data-toggle="dropdown"><span id="amc-account-name"></span> <strong class="caret"></strong></a>
<ul id="cloud-menu" class="dropdown-menu scrollable-menu inpagemenu" role="menu">
<li>
- <a onclick="openAuthenticatedUrl('console/index.html');" target="_blank" class="btn">
+ <a onclick="openAuthenticatedUrl('console.html');" target="_blank" class="btn">
<i class="fas fa-tv"></i>&nbsp;&nbsp;<span class="hidden-sm hidden-md">Nodes<sup class="beta"> beta</sup></span>
</a>
</li>
diff --git a/web/gui/static/static/img/netdata-logomark.svg b/web/gui/static/static/img/netdata-logomark.svg
deleted file mode 100644
index 87fb2bda..00000000
--- a/web/gui/static/static/img/netdata-logomark.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-<svg width="1723" height="1723" viewBox="0 0 1723 1723" fill="none" xmlns="http://www.w3.org/2000/svg">
-<path fill-rule="evenodd" clip-rule="evenodd" d="M0.628784 849.678C0.628784 473.909 235.042 153.621 563.766 30.7914C701.438 19.0613 843.892 50.2449 970.557 129.297C1052.47 180.42 1119.71 246.528 1170.96 321.982C1161.21 207.568 1122.97 96.4678 1058.94 0.187012C1220.56 38.587 1364.64 123.126 1476.91 239.343C1518.34 297.634 1548.55 365.545 1563.67 440.489C1578.54 514.244 1577.35 587.545 1562.5 656.661C1601.04 613.105 1632.22 563.24 1654.63 509.251C1698.41 613.852 1722.63 728.899 1722.63 849.678C1722.63 1331.55 1337.15 1722.19 861.629 1722.19C386.112 1722.19 0.628784 1331.55 0.628784 849.678ZM1178.87 1369.04C1286.71 1369.04 1374.13 1280.45 1374.13 1171.17C1374.13 1061.88 1286.71 973.293 1178.87 973.293C1071.03 973.293 983.603 1061.88 983.603 1171.17C983.603 1280.45 1071.03 1369.04 1178.87 1369.04Z" fill="#00C853"/>
-</svg>
diff --git a/web/gui/tv.html b/web/gui/tv.html
index bd549be9..58485b26 100644
--- a/web/gui/tv.html
+++ b/web/gui/tv.html
@@ -94,7 +94,7 @@ setTimeout(function(){
<div style="width: 100%; height: calc(100% - 15px); text-align: center; display: inline-block;">
<br/>
<div data-netdata="system.cpu"
- data-host="http://registry.my-netdata.io"
+ data-host="https://registry.my-netdata.io"
data-title="CPU usage of registry.my-netdata.io"
data-chart-library="dygraph"
data-width="49%"
@@ -120,7 +120,7 @@ setTimeout(function(){
</div>
<div style="width: 100%; height: calc(100% - 15px); text-align: center; display: inline-block;">
<div data-netdata="system.io"
- data-host="http://registry.my-netdata.io"
+ data-host="https://registry.my-netdata.io"
data-common-max="io"
data-common-min="io"
data-title="I/O on registry.my-netdata.io"
@@ -148,7 +148,7 @@ setTimeout(function(){
</div>
<div style="width: 100%; height: calc(100% - 15px); text-align: center; display: inline-block;">
<div data-netdata="system.net"
- data-host="http://registry.my-netdata.io"
+ data-host="https://registry.my-netdata.io"
data-common-max="traffic"
data-common-min="traffic"
data-title="Network traffic on registry.my-netdata.io"
@@ -178,7 +178,7 @@ setTimeout(function(){
registry.my-netdata.io
<br/>
<div data-netdata="netdata.requests"
- data-host="http://registry.my-netdata.io"
+ data-host="https://registry.my-netdata.io"
data-common-max="netdata-requests"
data-decimal-digits="0"
data-title="Chart Refreshes/s"
@@ -189,7 +189,7 @@ setTimeout(function(){
data-points="300"
></div>
<div data-netdata="netdata.clients"
- data-host="http://registry.my-netdata.io"
+ data-host="https://registry.my-netdata.io"
data-common-max="netdata-clients"
data-decimal-digits="0"
data-title="Sockets"
@@ -204,7 +204,7 @@ setTimeout(function(){
data-dimensions="in"
data-common-max="netdata-net-in"
data-decimal-digits="0"
- data-host="http://registry.my-netdata.io"
+ data-host="https://registry.my-netdata.io"
data-title="Requests Traffic"
data-chart-library="easypiechart"
data-width="15%"
@@ -216,7 +216,7 @@ setTimeout(function(){
data-dimensions="out"
data-common-max="netdata-net-out"
data-decimal-digits="0"
- data-host="http://registry.my-netdata.io"
+ data-host="https://registry.my-netdata.io"
data-title="Chart Data Traffic"
data-chart-library="easypiechart"
data-width="15%"
diff --git a/web/server/README.md b/web/server/README.md
index 7d74c181..df29f331 100644
--- a/web/server/README.md
+++ b/web/server/README.md
@@ -33,15 +33,15 @@ The ports to bind are controlled via `[web].bind to`, like this:
```
[web]
default port = 19999
- bind to = 127.0.0.1=dashboard 10.1.1.1:19998=management|netdata.conf hostname:19997=badges [::]:19996=streaming 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:/tmp/netdata.sock
```
Using the above, netdata will bind to:
-- IPv4 127.0.0.1 at port 19999 (port was used from `default port`). Only the UI (dashboard) and the read API will be accessible on this port.
+- IPv4 127.0.0.1 at port 19999 (port was used from `default port`). Only the UI (dashboard) and the read API will be accessible on this port. Both HTTP and HTTPS requests will be accepted.
- IPv4 10.1.1.1 at port 19998. The management API and netdata.conf will be accessible on this port.
- All the IPs `hostname` resolves to (both IPv4 and IPv6 depending on the resolved IPs) at port 19997. Only badges will be accessible on this port.
-- All IPv6 IPs at port 19996. Only metric streaming requests from other netdata agents will be accepted on this port.
+- 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.
@@ -57,6 +57,65 @@ The API requests are serviced as follows:
- `badges` gives access only to the badges API calls.
- `management` gives access only to the management API calls.
+### Enabling TLS support
+
+
+Netdata since version 1.16 supports encrypted HTTP connections to the web server and encryption of the data stream between a slave and a master.
+Inbound unix socket connections are unaffected, regardless of the SSL settings.
+To enable SSL, provide the path to your certificate and private key in the `[web]` section of `netdata.conf`:
+
+```
+[web]
+ ssl key = /etc/netdata/ssl/key.pem
+ ssl certificate = /etc/netdata/ssl/cert.pem
+```
+
+Both files must be readable by the netdata user. If any of the two files does not exist or is unreadable, Netdata falls back to HTTP.
+
+For a master/slave connection, only the master needs these settings.
+
+For test purposes, you can generate self-signed certificates with the following command:
+
+```
+$ openssl req -newkey rsa:2048 -nodes -sha512 -x509 -days 365 -keyout key.pem -out cert.pem
+```
+
+TIP: If you use 4096 bits for the key and the certificate, netdata will need more CPU to process the whole communication.
+rsa4096 can be until 4 times slower than rsa2048, so we recommend using 2048 bits. You can verify the difference by running
+
+```
+$ openssl speed rsa2048 rsa4096
+```
+
+#### SSL enforcement
+
+When the certificates are defined and unless any other options are provided, a Netdata server will:
+- Redirect all incoming HTTP web server requests to HTTPS. Applies to the dashboard, the API, netdata.conf and badges.
+- Allow incoming slave connections to use both unencrypted and encrypted communications for streaming.
+
+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
+:------:|:-----:|:-----:|:-----:|:--------
+none | Redirected to HTTPS | Accepted | Accepted | Accepted
+`force` | Redirected to HTTPS | Accepted | Denied | Accepted
+`optional` | Accepted | Accepted | Accepted | Accepted
+
+Example:
+
+```
+[web]
+ bind to = *=dashboard|registry|badges|management|streaming|netdata.conf^SSL=force
+```
+
+For information how to configure the slaves to use TLS, check [securing the communication](../../streaming#securing-the-communication) in the streaming documentation.
+You will find there additional details on the expected behavior for client and server nodes, when their respective SSL options are enabled.
+
+#### SSL error
+
+It is possible that when you start to use the Netdata with SSL some erros will be register in the logs, this happens due possible incompatibilities between the browser options related to SSL like Ciphers and TLS/SSL version and the Netdata internal configuration. The most common error would be `error:00000006:lib(0):func(0):EVP lib`. In a near future the Netdata will allow our users to change the internal configuration to avoid errors like this, but until there we are setting the most common and safety options to the communication.
+
### Access lists
Netdata supports access lists in `netdata.conf`:
@@ -96,10 +155,10 @@ setting | default | info
:------:|:-------:|:----
ses max window | `15` | See [single exponential smoothing](../api/queries/des/)
des max window | `15` | See [double exponential smoothing](../api/queries/des/)
-listen backlog | `4096` | The port backlog. Check `man 2 listen`.
-web files owner | `netdata` | The user that owns the web static files. Netdata will refuse to serve a file that is not owned by this user, even if it has read access to that file. If the user given is not found, netdata will only serve files owned by user given in `run as user`.
+listen backlog | `4096` | The port backlog. Check `man 2 listen`.
+web files owner | `netdata` | The user that owns the web static files. Netdata will refuse to serve a file that is not owned by this user, even if it has read access to that file. If the user given is not found, netdata will only serve files owned by user given in `run as user`.
web files group | `netdata` | If this is set, Netdata will check if the file is owned by this group and refuse to serve the file if it's not.
-disconnect idle clients after seconds | `60` | The time in seconds to disconnect web clients after being totally idle.
+disconnect idle clients after seconds | `60` | The time in seconds to disconnect web clients after being totally idle.
timeout for first request | `60` | How long to wait for a client to send a request before closing the socket. Prevents slow request attacks.
accept a streaming request every seconds | `0` | Can be used to set a limit on how often a master Netdata server will accept streaming requests from the slaves in a [streaming and replication setup](../../streaming)
respect do not track policy | `no` | If set to `yes`, will respect the client's browser preferences on storing cookies.
diff --git a/web/server/static/static-threaded.c b/web/server/static/static-threaded.c
index 1945b8a3..5dda2700 100644
--- a/web/server/static/static-threaded.c
+++ b/web/server/static/static-threaded.c
@@ -152,10 +152,67 @@ static void *web_server_add_callback(POLLINFO *pi, short int *events, void *data
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;
- if(unlikely(pi->socktype == AF_UNIX))
+ if ( !strncmp(pi->client_port,"UNIX",4)){
web_client_set_unix(w);
- else
+ } else {
web_client_set_tcp(w);
+ }
+
+#ifdef ENABLE_HTTPS
+ if ((!web_client_check_unix(w)) && ( netdata_srv_ctx )) {
+ if( sock_delnonblock(w->ifd) < 0 ){
+ error("Web server cannot remove the non-blocking flag from socket %d",w->ifd);
+ }
+
+ //Read the first 7 bytes from the message, but the message
+ //is not removed from the queue, because we are using MSG_PEEK
+ char test[8];
+ if ( recv(w->ifd,test, 7,MSG_PEEK) == 7 ) {
+ test[7] = 0x00;
+ }
+ else {
+ //Case I do not have success to read 7 bytes,
+ //this means that the mensage was not completely read, so
+ //I cannot identify it yet.
+ sock_setnonblock(w->ifd);
+ return w;
+ }
+
+ //The next two ifs are not together because I am reusing SSL structure
+ if (!w->ssl.conn)
+ {
+ w->ssl.conn = SSL_new(netdata_srv_ctx);
+ if ( w->ssl.conn ) {
+ SSL_set_accept_state(w->ssl.conn);
+ } else {
+ error("Failed to create SSL context on socket fd %d.", w->ifd);
+ if (test[0] < 0x18){
+ WEB_CLIENT_IS_DEAD(w);
+ sock_setnonblock(w->ifd);
+ return w;
+ }
+ }
+ }
+
+ if (w->ssl.conn) {
+ if (SSL_set_fd(w->ssl.conn, w->ifd) != 1) {
+ error("Failed to set the socket to the SSL on socket fd %d.", w->ifd);
+ //The client is not set dead, because I received a normal HTTP request
+ //instead a Client Hello(HTTPS).
+ if ( test[0] < 0x18 ){
+ WEB_CLIENT_IS_DEAD(w);
+ }
+ }
+ else{
+ w->ssl.flags = security_process_accept(w->ssl.conn, (int)test[0]);
+ }
+ }
+
+ sock_setnonblock(w->ifd);
+ } else{
+ w->ssl.flags = NETDATA_SSL_NO_HANDSHAKE;
+ }
+#endif
debug(D_WEB_CLIENT, "%llu: ADDED CLIENT FD %d", w->id, pi->fd);
return w;
@@ -189,6 +246,8 @@ static int web_server_rcv_callback(POLLINFO *pi, short int *events) {
struct web_client *w = (struct web_client *)pi->data;
int fd = pi->fd;
+ //BRING IT TO HERE
+
if(unlikely(web_client_receive(w) < 0))
return -1;
@@ -398,6 +457,9 @@ void *socket_listen_main_static_threaded(void *ptr) {
if(!api_sockets.opened)
fatal("LISTENER: no listen sockets available.");
+#ifdef ENABLE_HTTPS
+ security_start_ssl(NETDATA_SSL_CONTEXT_SERVER);
+#endif
// 6 threads is the optimal value
// since 6 are the parallel connections browsers will do
// so, if the machine has more CPUs, avoid using resources unnecessarily
@@ -412,7 +474,7 @@ void *socket_listen_main_static_threaded(void *ptr) {
if(static_threaded_workers_count < 1) static_threaded_workers_count = 1;
- size_t max_sockets = (size_t)config_get_number(CONFIG_SECTION_WEB, "web server max sockets", (long long int)(rlimit_nofile.rlim_cur / 2));
+ size_t max_sockets = (size_t)config_get_number(CONFIG_SECTION_WEB, "web server max sockets", (long long int)(rlimit_nofile.rlim_cur / 4));
static_workers_private_data = callocz((size_t)static_threaded_workers_count, sizeof(struct web_server_static_threaded_worker));
diff --git a/web/server/web_client.c b/web/server/web_client.c
index 3dc6ec82..bd275f5e 100644
--- a/web/server/web_client.c
+++ b/web/server/web_client.c
@@ -143,7 +143,9 @@ void web_client_request_done(struct web_client *w) {
debug(D_WEB_CLIENT, "%llu: Closing filecopy input file descriptor %d.", w->id, w->ifd);
if(web_server_mode != WEB_SERVER_MODE_STATIC_THREADED) {
- if (w->ifd != -1) close(w->ifd);
+ if (w->ifd != -1){
+ close(w->ifd);
+ }
}
w->ifd = w->ofd;
@@ -688,6 +690,9 @@ const char *web_response_code_to_string(int code) {
case 200:
return "OK";
+ case 301:
+ return "Moved Permanently";
+
case 307:
return "Temporary Redirect";
@@ -724,15 +729,21 @@ const char *web_response_code_to_string(int code) {
}
static inline char *http_header_parse(struct web_client *w, char *s, int parse_useragent) {
- static uint32_t hash_origin = 0, hash_connection = 0, hash_accept_encoding = 0, hash_donottrack = 0, hash_useragent = 0, hash_authorization = 0;
+ static uint32_t hash_origin = 0, hash_connection = 0, hash_donottrack = 0, hash_useragent = 0, hash_authorization = 0, hash_host = 0;
+#ifdef NETDATA_WITH_ZLIB
+ static uint32_t hash_accept_encoding = 0;
+#endif
if(unlikely(!hash_origin)) {
hash_origin = simple_uhash("Origin");
hash_connection = simple_uhash("Connection");
+#ifdef NETDATA_WITH_ZLIB
hash_accept_encoding = simple_uhash("Accept-Encoding");
+#endif
hash_donottrack = simple_uhash("DNT");
hash_useragent = simple_uhash("User-Agent");
hash_authorization = simple_uhash("X-Auth-Token");
+ hash_host = simple_uhash("Host");
}
char *e = s;
@@ -780,6 +791,9 @@ static inline char *http_header_parse(struct web_client *w, char *s, int parse_u
} else if(hash == hash_authorization&& !strcasecmp(s, "X-Auth-Token")) {
w->auth_bearer_token = strdupz(v);
}
+ else if(hash == hash_host && !strcasecmp(s, "Host")){
+ strncpyz(w->host, v, (ve - v));
+ }
#ifdef NETDATA_WITH_ZLIB
else if(hash == hash_accept_encoding && !strcasecmp(s, "Accept-Encoding")) {
if(web_enable_gzip) {
@@ -807,7 +821,12 @@ static inline char *http_header_parse(struct web_client *w, char *s, int parse_u
typedef enum {
HTTP_VALIDATION_OK,
HTTP_VALIDATION_NOT_SUPPORTED,
+#ifdef ENABLE_HTTPS
+ HTTP_VALIDATION_INCOMPLETE,
+ HTTP_VALIDATION_REDIRECT
+#else
HTTP_VALIDATION_INCOMPLETE
+#endif
} HTTP_VALIDATION;
static inline HTTP_VALIDATION http_request_validate(struct web_client *w) {
@@ -847,6 +866,35 @@ static inline HTTP_VALIDATION http_request_validate(struct web_client *w) {
w->mode = WEB_CLIENT_MODE_OPTIONS;
}
else if(!strncmp(s, "STREAM ", 7)) {
+#ifdef ENABLE_HTTPS
+ if ( (w->ssl.flags) && (netdata_use_ssl_on_stream & NETDATA_SSL_FORCE)){
+ w->header_parse_tries = 0;
+ w->header_parse_last_size = 0;
+ web_client_disable_wait_receive(w);
+ char hostname[256];
+ char *copyme = strstr(s,"hostname=");
+ if ( copyme ){
+ copyme += 9;
+ char *end = strchr(copyme,'&');
+ if(end){
+ size_t length = end - copyme;
+ memcpy(hostname,copyme,length);
+ hostname[length] = 0X00;
+ }
+ else{
+ memcpy(hostname,"not available",13);
+ hostname[13] = 0x00;
+ }
+ }
+ else{
+ memcpy(hostname,"not available",13);
+ hostname[13] = 0x00;
+ }
+ error("The server is configured to always use encrypt connection, please enable the SSL on slave with hostname '%s'.",hostname);
+ return HTTP_VALIDATION_NOT_SUPPORTED;
+ }
+#endif
+
encoded_url = s = &s[7];
w->mode = WEB_CLIENT_MODE_STREAM;
}
@@ -899,6 +947,16 @@ static inline HTTP_VALIDATION http_request_validate(struct web_client *w) {
// copy the URL - we are going to overwrite parts of it
// TODO -- ideally we we should avoid copying buffers around
strncpyz(w->last_url, w->decoded_url, NETDATA_WEB_REQUEST_URL_SIZE);
+#ifdef ENABLE_HTTPS
+ if ( (!web_client_check_unix(w)) && (netdata_srv_ctx) ) {
+ if ((w->ssl.conn) && ((w->ssl.flags & NETDATA_SSL_NO_HANDSHAKE) && (netdata_use_ssl_on_http & NETDATA_SSL_FORCE) && (w->mode != WEB_CLIENT_MODE_STREAM)) ) {
+ w->header_parse_tries = 0;
+ w->header_parse_last_size = 0;
+ web_client_disable_wait_receive(w);
+ return HTTP_VALIDATION_REDIRECT;
+ }
+ }
+#endif
w->header_parse_tries = 0;
w->header_parse_last_size = 0;
@@ -918,6 +976,26 @@ static inline HTTP_VALIDATION http_request_validate(struct web_client *w) {
return HTTP_VALIDATION_INCOMPLETE;
}
+static inline ssize_t web_client_send_data(struct web_client *w,const void *buf,size_t len, int flags)
+{
+ ssize_t bytes;
+#ifdef ENABLE_HTTPS
+ if ( (!web_client_check_unix(w)) && (netdata_srv_ctx) ) {
+ if ( ( w->ssl.conn ) && ( !w->ssl.flags ) ){
+ bytes = SSL_write(w->ssl.conn,buf, len) ;
+ } else {
+ bytes = send(w->ofd,buf, len , flags);
+ }
+ } else {
+ bytes = send(w->ofd,buf, len , flags);
+ }
+#else
+ bytes = send(w->ofd, buf, len, flags);
+#endif
+
+ return bytes;
+}
+
static inline void web_client_send_http_header(struct web_client *w) {
if(unlikely(w->response.code != 200))
buffer_no_cacheable(w->response.data);
@@ -948,6 +1026,23 @@ static inline void web_client_send_http_header(struct web_client *w) {
strftime(edate, sizeof(edate), "%a, %d %b %Y %H:%M:%S %Z", tm);
}
+ char headerbegin[8328];
+ if (w->response.code == 301) {
+ memcpy(headerbegin,"\r\nLocation: https://",20);
+ size_t headerlength = strlen(w->host);
+ memcpy(&headerbegin[20],w->host,headerlength);
+ headerlength += 20;
+ size_t tmp = strlen(w->last_url);
+ memcpy(&headerbegin[headerlength],w->last_url,tmp);
+ headerlength += tmp;
+ memcpy(&headerbegin[headerlength],"\r\n",2);
+ headerlength += 2;
+ headerbegin[headerlength] = 0x00;
+ }else {
+ memcpy(headerbegin,"\r\n",2);
+ headerbegin[2]=0x00;
+ }
+
buffer_sprintf(w->response.header_output,
"HTTP/1.1 %d %s\r\n"
"Connection: %s\r\n"
@@ -955,13 +1050,14 @@ static inline void web_client_send_http_header(struct web_client *w) {
"Access-Control-Allow-Origin: %s\r\n"
"Access-Control-Allow-Credentials: true\r\n"
"Content-Type: %s\r\n"
- "Date: %s\r\n"
+ "Date: %s%s"
, w->response.code, code_msg
, web_client_has_keepalive(w)?"keep-alive":"close"
, VERSION
, w->origin
, content_type_string
, date
+ , headerbegin
);
if(unlikely(web_x_frame_options))
@@ -1046,6 +1142,37 @@ static inline void web_client_send_http_header(struct web_client *w) {
size_t count = 0;
ssize_t bytes;
+#ifdef ENABLE_HTTPS
+ if ( (!web_client_check_unix(w)) && (netdata_srv_ctx) ) {
+ if ( ( w->ssl.conn ) && ( !w->ssl.flags ) ){
+ while((bytes = SSL_write(w->ssl.conn, buffer_tostring(w->response.header_output), buffer_strlen(w->response.header_output))) < 0) {
+ count++;
+ if(count > 100 || (errno != EAGAIN && errno != EWOULDBLOCK)) {
+ error("Cannot send HTTP headers to web client.");
+ break;
+ }
+ }
+ } else {
+ while((bytes = send(w->ofd, buffer_tostring(w->response.header_output), buffer_strlen(w->response.header_output), 0)) == -1) {
+ count++;
+
+ if(count > 100 || (errno != EAGAIN && errno != EWOULDBLOCK)) {
+ error("Cannot send HTTP headers to web client.");
+ break;
+ }
+ }
+ }
+ } else {
+ while((bytes = send(w->ofd, buffer_tostring(w->response.header_output), buffer_strlen(w->response.header_output), 0)) == -1) {
+ count++;
+
+ if(count > 100 || (errno != EAGAIN && errno != EWOULDBLOCK)) {
+ error("Cannot send HTTP headers to web client.");
+ break;
+ }
+ }
+ }
+#else
while((bytes = send(w->ofd, buffer_tostring(w->response.header_output), buffer_strlen(w->response.header_output), 0)) == -1) {
count++;
@@ -1054,6 +1181,7 @@ static inline void web_client_send_http_header(struct web_client *w) {
break;
}
}
+#endif
if(bytes != (ssize_t) buffer_strlen(w->response.header_output)) {
if(bytes > 0)
@@ -1303,7 +1431,16 @@ void web_client_process_request(struct web_client *w) {
return;
}
break;
-
+#ifdef ENABLE_HTTPS
+ case HTTP_VALIDATION_REDIRECT:
+ {
+ buffer_flush(w->response.data);
+ w->response.data->contenttype = CT_TEXT_HTML;
+ buffer_strcat(w->response.data, "<!DOCTYPE html><!-- SPDX-License-Identifier: GPL-3.0-or-later --><html><body onload=\"window.location.href ='https://'+ window.location.hostname + ':' + window.location.port + window.location.pathname\">Redirecting to safety connection, case your browser does not support redirection, please click <a onclick=\"window.location.href ='https://'+ window.location.hostname + ':' + window.location.port + window.location.pathname\">here</a>.</body></html>");
+ w->response.code = 301;
+ break;
+ }
+#endif
case HTTP_VALIDATION_NOT_SUPPORTED:
debug(D_WEB_CLIENT_ACCESS, "%llu: Cannot understand '%s'.", w->id, w->response.data->buffer);
@@ -1373,9 +1510,11 @@ ssize_t web_client_send_chunk_header(struct web_client *w, size_t len)
{
debug(D_DEFLATE, "%llu: OPEN CHUNK of %zu bytes (hex: %zx).", w->id, len, len);
char buf[24];
- sprintf(buf, "%zX\r\n", len);
-
- ssize_t bytes = send(w->ofd, buf, strlen(buf), 0);
+ ssize_t bytes;
+ bytes = (ssize_t)sprintf(buf, "%zX\r\n", len);
+ buf[bytes] = 0x00;
+
+ bytes = web_client_send_data(w,buf,strlen(buf),0);
if(bytes > 0) {
debug(D_DEFLATE, "%llu: Sent chunk header %zd bytes.", w->id, bytes);
w->stats_sent_bytes += bytes;
@@ -1397,7 +1536,8 @@ ssize_t web_client_send_chunk_close(struct web_client *w)
{
//debug(D_DEFLATE, "%llu: CLOSE CHUNK.", w->id);
- ssize_t bytes = send(w->ofd, "\r\n", 2, 0);
+ ssize_t bytes;
+ bytes = web_client_send_data(w,"\r\n",2,0);
if(bytes > 0) {
debug(D_DEFLATE, "%llu: Sent chunk suffix %zd bytes.", w->id, bytes);
w->stats_sent_bytes += bytes;
@@ -1419,7 +1559,8 @@ ssize_t web_client_send_chunk_finalize(struct web_client *w)
{
//debug(D_DEFLATE, "%llu: FINALIZE CHUNK.", w->id);
- ssize_t bytes = send(w->ofd, "\r\n0\r\n\r\n", 7, 0);
+ ssize_t bytes;
+ bytes = web_client_send_data(w,"\r\n0\r\n\r\n",7,0);
if(bytes > 0) {
debug(D_DEFLATE, "%llu: Sent chunk suffix %zd bytes.", w->id, bytes);
w->stats_sent_bytes += bytes;
@@ -1533,7 +1674,7 @@ ssize_t web_client_send_deflate(struct web_client *w)
debug(D_WEB_CLIENT, "%llu: Sending %zu bytes of data (+%zd of chunk header).", w->id, w->response.zhave - w->response.zsent, t);
- len = send(w->ofd, &w->response.zbuffer[w->response.zsent], (size_t) (w->response.zhave - w->response.zsent), MSG_DONTWAIT);
+ len = web_client_send_data(w,&w->response.zbuffer[w->response.zsent], (size_t) (w->response.zhave - w->response.zsent), MSG_DONTWAIT);
if(len > 0) {
w->stats_sent_bytes += len;
w->response.zsent += len;
@@ -1589,7 +1730,7 @@ ssize_t web_client_send(struct web_client *w) {
return 0;
}
- bytes = send(w->ofd, &w->response.data->buffer[w->response.sent], w->response.data->len - w->response.sent, MSG_DONTWAIT);
+ bytes = web_client_send_data(w,&w->response.data->buffer[w->response.sent], w->response.data->len - w->response.sent, MSG_DONTWAIT);
if(likely(bytes > 0)) {
w->stats_sent_bytes += bytes;
w->response.sent += bytes;
@@ -1664,11 +1805,26 @@ ssize_t web_client_receive(struct web_client *w)
if(unlikely(w->mode == WEB_CLIENT_MODE_FILECOPY))
return web_client_read_file(w);
+ ssize_t bytes;
+ ssize_t left = w->response.data->size - w->response.data->len;
+
// do we have any space for more data?
buffer_need_bytes(w->response.data, NETDATA_WEB_REQUEST_RECEIVE_SIZE);
- ssize_t left = w->response.data->size - w->response.data->len;
- ssize_t bytes = recv(w->ifd, &w->response.data->buffer[w->response.data->len], (size_t) (left - 1), MSG_DONTWAIT);
+#ifdef ENABLE_HTTPS
+ if ( (!web_client_check_unix(w)) && (netdata_srv_ctx) ) {
+ if ( ( w->ssl.conn ) && (!w->ssl.flags)) {
+ bytes = SSL_read(w->ssl.conn, &w->response.data->buffer[w->response.data->len], (size_t) (left - 1));
+ }else {
+ bytes = recv(w->ifd, &w->response.data->buffer[w->response.data->len], (size_t) (left - 1), MSG_DONTWAIT);
+ }
+ }
+ else{
+ bytes = recv(w->ifd, &w->response.data->buffer[w->response.data->len], (size_t) (left - 1), MSG_DONTWAIT);
+ }
+#else
+ bytes = recv(w->ifd, &w->response.data->buffer[w->response.data->len], (size_t) (left - 1), MSG_DONTWAIT);
+#endif
if(likely(bytes > 0)) {
w->stats_received_bytes += bytes;
diff --git a/web/server/web_client.h b/web/server/web_client.h
index 4263e252..0a57e8d8 100644
--- a/web/server/web_client.h
+++ b/web/server/web_client.h
@@ -129,6 +129,7 @@ struct web_client {
char decoded_url[NETDATA_WEB_REQUEST_URL_SIZE + 1]; // we decode the URL in this buffer
char last_url[NETDATA_WEB_REQUEST_URL_SIZE+1]; // we keep a copy of the decoded URL here
+ char host[256];
struct timeval tv_in, tv_ready;
@@ -153,6 +154,9 @@ struct web_client {
// STATIC-THREADED WEB SERVER MEMBERS
size_t pollinfo_slot; // POLLINFO slot of the web client
size_t pollinfo_filecopy_slot; // POLLINFO slot of the file read
+#ifdef ENABLE_HTTPS
+ struct netdata_ssl ssl;
+#endif
};
extern uid_t web_files_uid(void);
diff --git a/web/server/web_client_cache.c b/web/server/web_client_cache.c
index ab470560..763e7e96 100644
--- a/web/server/web_client_cache.c
+++ b/web/server/web_client_cache.c
@@ -6,6 +6,18 @@
// ----------------------------------------------------------------------------
// allocate and free web_clients
+#ifdef ENABLE_HTTPS
+
+static void web_client_reuse_ssl(struct web_client *w) {
+ if (netdata_srv_ctx) {
+ if (w->ssl.conn) {
+ SSL_clear(w->ssl.conn);
+ }
+ }
+}
+#endif
+
+
static void web_client_zero(struct web_client *w) {
// zero everything about it - but keep the buffers
@@ -35,6 +47,14 @@ static void web_client_free(struct web_client *w) {
buffer_free(w->response.header);
buffer_free(w->response.data);
freez(w->user_agent);
+#ifdef ENABLE_HTTPS
+ if ((!web_client_check_unix(w)) && ( netdata_srv_ctx )) {
+ if (w->ssl.conn) {
+ SSL_free(w->ssl.conn);
+ w->ssl.conn = NULL;
+ }
+ }
+#endif
freez(w);
}
@@ -159,12 +179,25 @@ struct web_client *web_client_get_from_cache_or_allocate() {
if(w->prev) w->prev->next = w->next;
if(w->next) w->next->prev = w->prev;
web_clients_cache.avail_count--;
+#ifdef ENABLE_HTTPS
+ web_client_reuse_ssl(w);
+ SSL *ssl = w->ssl.conn;
+#endif
web_client_zero(w);
web_clients_cache.reused++;
+#ifdef ENABLE_HTTPS
+ w->ssl.conn = ssl;
+ w->ssl.flags = NETDATA_SSL_START;
+ debug(D_WEB_CLIENT_ACCESS,"Reusing SSL structure with (w->ssl = NULL, w->accepted = %d)",w->ssl.flags);
+#endif
}
else {
// allocate it
w = web_client_alloc();
+#ifdef ENABLE_HTTPS
+ w->ssl.flags = NETDATA_SSL_START;
+ debug(D_WEB_CLIENT_ACCESS,"Starting SSL structure with (w->ssl = NULL, w->accepted = %d)",w->ssl.flags);
+#endif
web_clients_cache.allocated++;
}
@@ -205,6 +238,11 @@ void web_client_release(struct web_client *w) {
if (w->ifd != -1) close(w->ifd);
if (w->ofd != -1 && w->ofd != w->ifd) close(w->ofd);
w->ifd = w->ofd = -1;
+#ifdef ENABLE_HTTPS
+ web_client_reuse_ssl(w);
+ w->ssl.flags = NETDATA_SSL_START;
+#endif
+
}
// unlink it from the used
diff --git a/web/server/web_server.c b/web/server/web_server.c
index 11f7edf8..9e51c81f 100644
--- a/web/server/web_server.c
+++ b/web/server/web_server.c
@@ -138,5 +138,3 @@ void web_client_initialize_connection(struct web_client *w) {
web_client_cache_verify(0);
}
-
-