- **Users and User Groups resource usage**, by summarizing the process tree per user and group (CPU, memory, disk reads, disk writes, swap, threads, pipes, sockets, etc)
-- **Apache web server** mod-status (v2.2, v2.4)
+- **Apache web server** mod-status (v2.2, v2.4) and cache log statistics (multiple servers)
-- **Nginx web server** stub-status
+- **Nginx web server** stub-status (multiple servers)
- **mySQL databases** (multiple servers, each showing: bandwidth, queries/s, handlers, locks, issues, tmp operations, connections, binlog metrics, threads, innodb metrics, etc)
+- **Redis databases** (multiple servers, each showing: operations, hit rate, memory, keys, clients, slaves)
+
+- **memcached databases** (multiple servers, each showing: bandwidth, connections, items, etc)
+
- **ISC Bind name server** (multiple servers, each showing: clients, requests, queries, updates, failures and several per view metrics)
- **Postfix email server** message queue (entries, size)
-- **Squid proxy server** (clients bandwidth and requests, servers bandwidth and requests)
+- **exim email server** message queue (emails queued)
+
+- **IPFS** (Bandwidth, Peers)
+
+- **Squid proxy server** (multiple servers, each showing: clients bandwidth and requests, servers bandwidth and requests)
- **Hardware sensors** (temperature, voltage, fans, power, humidity, etc)
- **PHP-FPM** (multiple instances, each reporting connections, requests, performance)
+- **hddtemp** (disk temperatures)
+
- **SNMP devices** can be monitored too (although you will need to configure these)
And you can extend it, by writing plugins that collect data from any source, using any computer language.
## Documentation
Check the **[netdata wiki](https://github.com/firehol/netdata/wiki)**.
-
python.d/apache.conf \
python.d/apache_cache.conf \
python.d/cpufreq.conf \
+ python.d/dovecot.conf \
python.d/example.conf \
python.d/exim.conf \
python.d/hddtemp.conf \
+ python.d/ipfs.conf \
python.d/memcached.conf \
python.d/mysql.conf \
python.d/nginx.conf \
sidekiq: *sidekiq*
java: java
chat: irssi
+ipfs: ipfs
example: no
# exim: yes
# hddtemp: yes
+# ipfs: yes
# mysql: yes
# nginx: yes
# phpfpm: yes
--- /dev/null
+# netdata python.d.plugin configuration for dovecot
+#
+# This file is in YaML format. Generally the format is:
+#
+# name: value
+#
+# There are 2 sections:
+# - global variables
+# - one or more JOBS
+#
+# JOBS allow you to collect values from multiple sources.
+# Each source will have its own set of charts.
+#
+# JOB parameters have to be indented (using spaces only, example below).
+
+# ----------------------------------------------------------------------
+# Global Variables
+# These variables set the defaults for all JOBs, however each JOB
+# may define its own, overriding the defaults.
+
+# update_every sets the default data collection frequency.
+# If unset, the python.d.plugin default is used.
+# update_every: 1
+
+# priority controls the order of charts at the netdata dashboard.
+# Lower numbers move the charts towards the top of the page.
+# If unset, the default for python.d.plugin is used.
+# priority: 60000
+
+# retries sets the number of retries to be made in case of failures.
+# If unset, the default for python.d.plugin is used.
+# Attempts to restore the service are made once every update_every
+# and only if the module has collected values in the past.
+# retries: 5
+
+# ----------------------------------------------------------------------
+# JOBS (data collection sources)
+#
+# The default JOBS share the same *name*. JOBS with the same name
+# are mutually exclusive. Only one of them will be allowed running at
+# any time. This allows autodetection to try several alternatives and
+# pick the one that works.
+#
+# Any number of jobs is supported.
+#
+# All python.d.plugin JOBS (for all its modules) support a set of
+# predefined parameters. These are:
+#
+# job_name:
+# name: myname # the JOB's name as it will appear at the
+# # dashboard (by default is the job_name)
+# # JOBs sharing a name are mutually exclusive
+# update_every: 1 # the JOB's data collection frequency
+# priority: 60000 # the JOB's order on the dashboard
+# retries: 5 # the JOB's number of restoration attempts
+#
+# Additionally to the above, dovecot also supports the following:
+#
+# socket: 'path/to/dovecot/stats'
+#
+# or
+# host: 'IP or HOSTNAME' # the host to connect to
+# port: PORT # the port to connect to
+#
+#
+
+# ----------------------------------------------------------------------
+# AUTO-DETECTION JOBS
+# only one of them will run (they have the same name)
+
+localhost:
+ name : 'local'
+ host : 'localhost'
+ port : 24242
+
+localipv4:
+ name : 'local'
+ host : '127.0.0.1'
+ port : 24242
+
+localipv6:
+ name : 'local'
+ host : '::1'
+ port : 24242
+
+localsocket:
+ name : 'local'
+ socket : '/var/run/dovecot/stats'
+
# port: PORT # the port to connect to
#
+# By default this module will try to autodetect number of disks.
+# However this can be overridden by setting variable `disk_count` to
+# desired number of disks. Example for two disks:
+#
+# disk_count: 2
+#
+
# ----------------------------------------------------------------------
# AUTO-DETECTION JOBS
# only one of them will run (they have the same name)
localipv6:
name: 'local'
- host: '127.0.0.1'
+ host: '::1'
port: 7634
--- /dev/null
+# netdata python.d.plugin configuration for ipfs
+#
+# This file is in YaML format. Generally the format is:
+#
+# name: value
+#
+# There are 2 sections:
+# - global variables
+# - one or more JOBS
+#
+# JOBS allow you to collect values from multiple sources.
+# Each source will have its own set of charts.
+#
+# JOB parameters have to be indented (using spaces only, example below).
+
+# ----------------------------------------------------------------------
+# Global Variables
+# These variables set the defaults for all JOBs, however each JOB
+# may define its own, overriding the defaults.
+
+# update_every sets the default data collection frequency.
+# If unset, the python.d.plugin default is used.
+# update_every: 1
+
+# priority controls the order of charts at the netdata dashboard.
+# Lower numbers move the charts towards the top of the page.
+# If unset, the default for python.d.plugin is used.
+# priority: 60000
+
+# retries sets the number of retries to be made in case of failures.
+# If unset, the default for python.d.plugin is used.
+# Attempts to restore the service are made once every update_every
+# and only if the module has collected values in the past.
+# retries: 5
+
+# ----------------------------------------------------------------------
+# JOBS (data collection sources)
+#
+# The default JOBS share the same *name*. JOBS with the same name
+# are mutually exclusive. Only one of them will be allowed running at
+# any time. This allows autodetection to try several alternatives and
+# pick the one that works.
+#
+# Any number of jobs is supported.
+#
+# All python.d.plugin JOBS (for all its modules) support a set of
+# predefined parameters. These are:
+#
+# job_name:
+# name: myname # the JOB's name as it will appear at the
+# # dashboard (by default is the job_name)
+# # JOBs sharing a name are mutually exclusive
+# update_every: 1 # the JOB's data collection frequency
+# priority: 60000 # the JOB's order on the dashboard
+# retries: 5 # the JOB's number of restoration attempts
+#
+# Additionally to the above, ipfs also supports the following:
+#
+# url: 'URL' # URL to the IPFS API
+#
+# ----------------------------------------------------------------------
+# AUTO-DETECTION JOBS
+# only one of them will run (they have the same name)
+
+localhost:
+ name : 'local'
+ url : 'http://localhost:5001'
['18ee1c6197a4381b1c1631ef6129824f']='apps_groups.conf'
['2f4a85fedecce1bf425fa1039f6b021e']='apps_groups.conf'
['3af522d65b50a5e447607ffb28c81ff5']='apps_groups.conf'
+ ['3b1bfa40a4ff6a200bb2fc00bc51a664']='apps_groups.conf'
['4a448831776de8acf2e0bdc4cc994cb4']='apps_groups.conf'
['5bf51bb24fb41db9b1e448bd060d3f8c']='apps_groups.conf'
['636d032928ea0f4741eab264fb49c099']='apps_groups.conf'
['b27f10a38a95edbbec20f44a4728b7c4']='python.d.conf'
['b32164929eda7449a9677044e11151bf']='python.d.conf'
['b8969be5b3ceb4a99477937119bd4323']='python.d.conf'
+ ['bba2f3886587f137ea08a6e63dd3d376']='python.d.conf'
['d55be5bb5e108da1e7645da007c53cd4']='python.d.conf'
['f82924563e41d99cdae5431f0af69155']='python.d.conf'
['7830066c46a7e5f9682b8d3f4566b4e5']='python.d/cpufreq.conf'
['73125ae64d5c6e9361944cd9bd14844e']='python.d/exim.conf'
['a94af1c808aafdf00537d85ff2197ec8']='python.d/exim.conf'
['2a0794fd43eadf30a51805bc9ba2c64d']='python.d/hddtemp.conf'
+ ['47180421d580baeaedf8c0ef3d647fb5']='python.d/hddtemp.conf'
['731a1fcfe9b2da1b9d685056a59541b8']='python.d/hddtemp.conf'
['d74dc63fbe631dab9a2ff1b0f5d71719']='python.d/hddtemp.conf'
+ ['0e59bc11d0a869ea0247c04c08c8d72e']='python.d/ipfs.conf'
+ ['70105b1744a8e13f49083d7f1981aea2']='python.d/ipfs.conf'
+ ['97f337eb96213f3ede05e522e3743a6c']='python.d/memcached.conf'
['1ea8e8ef1fa8a3a0fcdfba236f4cb195']='python.d/mysql.conf'
['5379cdc26d7725e2b0d688d785816cef']='python.d/mysql.conf'
['7deb236ec68a512b9bdd18e6a51d76f7']='python.d/mysql.conf'
pass
except AttributeError as e:
self._stop(job)
- msg.error(job.chart_name, "cannot find check() function.")
+ msg.error(job.chart_name, "cannot find check() function or it thrown unhandled exception.")
msg.debug(str(e))
except (UnboundLocalError, Exception) as e:
msg.error(job.chart_name, str(e))
# sys.stdout.flush()
i += 1
except AttributeError:
- msg.error(job.chart_name, "cannot find create() function.")
+ msg.error(job.chart_name, "cannot find create() function or it thrown unhandled exception.")
self._stop(job)
except (UnboundLocalError, Exception) as e:
msg.error(job.chart_name, str(e))
apache.chart.py \
apache_cache.chart.py \
cpufreq.chart.py \
+ dovecot.chart.py \
example.chart.py \
exim.chart.py \
hddtemp.chart.py \
+ ipfs.chart.py \
memcached.chart.py \
mysql.chart.py \
nginx.chart.py \
python_modules/pyyaml3/scanner.py \
python_modules/pyyaml3/serializer.py \
python_modules/pyyaml3/tokens.py \
- $(NULL)
\ No newline at end of file
+ $(NULL)
--- /dev/null
+# -*- coding: utf-8 -*-
+# Description: dovecot netdata python.d module
+# Author: Pawel Krupa (paulfantom)
+
+from base import SocketService
+
+# default module values (can be overridden per job in `config`)
+# update_every = 2
+priority = 60000
+retries = 60
+
+# charts order (can be overridden if you want less charts, or different order)
+ORDER = ['sessions', 'commands',
+ 'faults',
+ 'context_switches',
+ 'disk', 'bytes', 'syscalls',
+ 'lookup', 'cache',
+ 'auth', 'auth_cache']
+
+CHARTS = {
+ 'sessions': {
+ 'options': [None, "logins and sessions", 'number', 'IMAP', 'dovecot.imap', 'line'],
+ 'lines': [
+ ['num_logins', 'logins', 'absolute'],
+ ['num_connected_sessions', 'active sessions', 'absolute']
+ ]},
+ 'commands': {
+ 'options': [None, "commands", "commands", 'IMAP', 'dovecot.imap', 'line'],
+ 'lines': [
+ ['num_cmds', 'commands', 'absolute']
+ ]},
+ 'faults': {
+ 'options': [None, "faults", "faults", 'Faults', 'dovecot.faults', 'line'],
+ 'lines': [
+ ['min_faults', 'minor', 'absolute'],
+ ['maj_faults', 'major', 'absolute']
+ ]},
+ 'context_switches': {
+ 'options': [None, "context switches", '', 'Context Switches', 'dovecot.context_switches', 'line'],
+ 'lines': [
+ ['vol_cs', 'volountary', 'absolute'],
+ ['invol_cs', 'involountary', 'absolute']
+ ]},
+ 'disk': {
+ 'options': [None, "disk", 'bytes/s', 'Reads and Writes', 'dovecot.read_write', 'line'],
+ 'lines': [
+ ['disk_input', 'read', 'incremental'],
+ ['disk_output', 'write', 'incremental']
+ ]},
+ 'bytes': {
+ 'options': [None, "bytes", 'bytes/s', 'Reads and Writes', 'dovecot.read_write', 'line'],
+ 'lines': [
+ ['read_bytes', 'read', 'incremental'],
+ ['write_bytes', 'write', 'incremental']
+ ]},
+ 'syscalls': {
+ 'options': [None, "number of syscalls", 'syscalls/s', 'Reads and Writes', 'dovecot.read_write', 'line'],
+ 'lines': [
+ ['read_count', 'read', 'incremental'],
+ ['write_count', 'write', 'incremental']
+ ]},
+ 'lookup': {
+ 'options': [None, "lookups", 'number/s', 'Mail', 'dovecot.mail', 'line'],
+ 'lines': [
+ ['mail_lookup_path', 'path', 'incremental'],
+ ['mail_lookup_attr', 'attr', 'incremental']
+ ]},
+ 'cache': {
+ 'options': [None, "hits", 'hits/s', 'Mail', 'dovecot.mail', 'line'],
+ 'lines': [
+ ['mail_cache_hits', 'hits', 'incremental']
+ ]},
+ 'auth': {
+ 'options': [None, "attempts", 'attempts', 'Authentication', 'dovecot.auth', 'stacked'],
+ 'lines': [
+ ['auth_successes', 'success', 'absolute'],
+ ['auth_failures', 'failure', 'absolute']
+ ]},
+ 'auth_cache': {
+ 'options': [None, "cache", 'number', 'Authentication', 'dovecot.auth', 'stacked'],
+ 'lines': [
+ ['auth_cache_hits', 'hit', 'absolute'],
+ ['auth_cache_misses', 'miss', 'absolute']
+ ]}
+}
+
+
+class Service(SocketService):
+ def __init__(self, configuration=None, name=None):
+ SocketService.__init__(self, configuration=configuration, name=name)
+ self.request = "EXPORT\tglobal\r\n"
+ self.host = None # localhost
+ self.port = None # 24242
+ # self._keep_alive = True
+ self.unix_socket = "/var/run/dovecot/stats"
+ self.order = ORDER
+ self.definitions = CHARTS
+
+ def _get_data(self):
+ """
+ Format data received from socket
+ :return: dict
+ """
+ try:
+ raw = self._get_raw_data()
+ except (ValueError, AttributeError):
+ return None
+
+ data = raw.split('\n')[:2]
+ desc = data[0].split('\t')
+ vals = data[1].split('\t')
+ # ret = dict(zip(desc, vals))
+ ret = {}
+ for i in range(len(desc)):
+ try:
+ #d = str(desc[i])
+ #if d in ('user_cpu', 'sys_cpu', 'clock_time'):
+ # val = float(vals[i])
+ #else:
+ # val = int(vals[i])
+ #ret[d] = val
+ ret[str(desc[i])] = int(vals[i])
+ except ValueError:
+ pass
+ if len(ret) == 0:
+ return None
+ return ret
Get data from TCP/IP socket
:return: dict
"""
- self.disk_count = self._get_disk_count()
try:
raw = self._get_raw_data().split("|")[:-1]
except AttributeError:
except (KeyError, TypeError) as e:
self.info("No excluded disks")
self.debug(str(e))
+
+ try:
+ self.disk_count = int(self.configuration['disk_count'])
+ except (KeyError, TypeError) as e:
+ self.info("Autodetecting number of disks")
+ self.disk_count = self._get_disk_count()
+ self.debug(str(e))
+
data = self._get_data()
if data is None:
return False
--- /dev/null
+# -*- coding: utf-8 -*-
+# Description: IPFS netdata python.d module
+# Authors: Pawel Krupa (paulfantom), davidak
+
+from base import UrlService
+import json
+
+# default module values (can be overridden per job in `config`)
+# update_every = 2
+priority = 60000
+retries = 60
+
+# default job configuration (overridden by python.d.plugin)
+# config = {'local': {
+# 'update_every': update_every,
+# 'retries': retries,
+# 'priority': priority,
+# 'url': 'http://localhost:5001'
+# }}
+
+# charts order (can be overridden if you want less charts, or different order)
+ORDER = ['bandwidth', 'peers']
+
+CHARTS = {
+ 'bandwidth': {
+ 'options': [None, 'IPFS Bandwidth', 'kbits/s', 'Bandwidth', 'ipfs.bandwidth', 'line'],
+ 'lines': [
+ ["in", None, "absolute", 8, 1000],
+ ["out", None, "absolute", -8, 1000]
+ ]},
+ 'peers': {
+ 'options': [None, 'IPFS Peers', 'peers', 'Peers', 'ipfs.peers', 'line'],
+ 'lines': [
+ ["peers", None, 'absolute']
+ ]}
+}
+
+
+class Service(UrlService):
+ def __init__(self, configuration=None, name=None):
+ UrlService.__init__(self, configuration=configuration, name=name)
+ try:
+ self.baseurl = str(self.configuration['url'])
+ except (KeyError, TypeError):
+ self.baseurl = "http://localhost:5001"
+ self.order = ORDER
+ self.definitions = CHARTS
+
+ def _get_bandwidth(self):
+ """
+ Format data received from http request
+ :return: int, int
+ """
+ self.url = self.baseurl + "/api/v0/stats/bw"
+ try:
+ raw = self._get_raw_data()
+ except AttributeError:
+ return None
+
+ try:
+ parsed = json.loads(raw)
+ bw_in = int(parsed['RateIn'])
+ bw_out = int(parsed['RateOut'])
+ except:
+ return None
+
+ return bw_in, bw_out
+
+ def _get_peers(self):
+ """
+ Format data received from http request
+ :return: int
+ """
+ self.url = self.baseurl + "/api/v0/swarm/peers"
+ try:
+ raw = self._get_raw_data()
+ except AttributeError:
+ return None
+
+ try:
+ parsed = json.loads(raw)
+ peers = len(parsed['Strings'])
+ except:
+ return None
+
+ return peers
+
+ def _get_data(self):
+ """
+ Get data from API
+ :return: dict
+ """
+ try:
+ peers = self._get_peers()
+ bandwidth_in, bandwidth_out = self._get_bandwidth()
+ except:
+ return None
+ data = {}
+ if peers is not None:
+ data['peers'] = peers
+ if bandwidth_in is not None and bandwidth_out is not None:
+ data['in'] = bandwidth_in
+ data['out'] = bandwidth_out
+
+ if len(data) == 0:
+ return None
+ return data
CHARTS = {
'net': {
- 'options': [None, 'Network', 'bytes', 'Network', 'memcached.net', 'line'],
+ 'options': [None, 'Network', 'kilobytes/s', 'Network', 'memcached.net', 'line'],
'lines': [
- ['bytes_read', 'read', 'absolute'],
- ['bytes_written', 'written', 'absolute']
+ ['bytes_read', 'read', 'incremental', 1, 1024],
+ ['bytes_written', 'written', 'incremental', 1, 1024]
]},
'connections': {
- 'options': [None, 'Connections', 'connections', 'Cluster', 'memcached.cluster', 'line'],
+ 'options': [None, 'Connections', 'connections/s', 'Cluster', 'memcached.cluster', 'line'],
'lines': [
- ['curr_connections', 'current', 'absolute'],
- ['rejected_connections', 'rejected', 'absolute'],
- ['total_connections', 'total', 'absolute']
+ ['curr_connections', 'current', 'incremental'],
+ ['rejected_connections', 'rejected', 'incremental'],
+ ['total_connections', 'total', 'incremental']
]},
'items': {
'options': [None, 'Items', 'items', 'Cluster', 'memcached.cluster', 'line'],
['total_items', 'total', 'absolute']
]},
'evicted_reclaimed': {
- 'options': [None, 'Items', 'items', 'Evicted & Reclaimed', 'memcached.evicted_reclaimed', 'line'],
+ 'options': [None, 'Items', 'items', 'Evicted and Reclaimed', 'memcached.evicted_reclaimed', 'line'],
'lines': [
['evictions', 'evicted', 'absolute'],
['reclaimed', 'reclaimed', 'absolute']
self.request = "stats\r\n"
self.host = "localhost"
self.port = 11211
+ self._keep_alive = True
self.unix_socket = None
self.order = ORDER
self.definitions = CHARTS
else:
return data
+ def _check_raw_data(self, data):
+ if data.endswith('END\r\n'):
+ return True
+ else:
+ return False
+
def check(self):
"""
Parse configuration, check if memcached is available
#}
# query executed on MySQL server
-QUERY = "SHOW GLOBAL STATUS"
+QUERY = "SHOW GLOBAL STATUS;"
ORDER = ['net',
'queries',
host=self.configuration['host'],
port=self.configuration['port'],
connect_timeout=self.update_every)
+ except MySQLdb.OperationalError as e:
+ self.error("Cannot establish connection to MySQL.")
+ self.debug(str(e))
+ raise RuntimeError
except Exception as e:
self.error("problem connecting to server:", e)
raise RuntimeError
cursor = self.connection.cursor()
cursor.execute(QUERY)
raw_data = cursor.fetchall()
+ except MySQLdb.OperationalError as e:
+ self.debug("Reconnecting due to", str(e))
+ self._connect()
+ cursor = self.connection.cursor()
+ cursor.execute(QUERY)
+ raw_data = cursor.fetchall()
except Exception as e:
self.error("cannot execute query.", e)
self.connection.close()
self.connection = None
return None
+
data = dict(raw_data)
try:
data["Thread_cache_misses"] = int(data["Threads_created"] * 10000 / float(data["Connections"]))
try:
raw = self._get_raw_data().split(" ")
return {'active': int(raw[2]),
- 'requests': int(raw[7]),
+ 'requests': int(raw[9]),
'reading': int(raw[11]),
'writing': int(raw[13]),
'waiting': int(raw[15]),
- 'accepts': int(raw[8]),
- 'handled': int(raw[9])}
+ 'accepts': int(raw[7]),
+ 'handled': int(raw[8])}
except (ValueError, AttributeError):
return None
def __add_openers(self):
# TODO add error handling
- opener = urllib2.build_opener()
+ self.opener = urllib2.build_opener()
# Proxy handling
# TODO currently self.proxies isn't parsed from configuration file
if self.user is not None and self.password is not None:
passman = urllib2.HTTPPasswordMgrWithDefaultRealm()
passman.add_password(None, self.url, self.user, self.password)
- opener.add_handler(urllib2.HTTPBasicAuthHandler(passman))
+ self.opener.add_handler(urllib2.HTTPBasicAuthHandler(passman))
+ self.debug("Enabling HTTP basic auth")
- urllib2.install_opener(opener)
+ #urllib2.install_opener(opener)
def _get_raw_data(self):
"""
"""
raw = None
try:
- f = urllib2.urlopen(self.url, timeout=self.update_every * 2)
+ f = self.opener.open(self.url, timeout=self.update_every * 2)
+ # f = urllib2.urlopen(self.url, timeout=self.update_every * 2)
except Exception as e:
self.error(str(e))
return None
self.__add_openers()
- if self._get_data() is None or len(self._get_data()) == 0:
+ test = self._get_data()
+ if test is None or len(test) == 0:
return False
else:
return True
self._disconnect()
else:
# connect to unix socket
- self._sock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
- self._sock.connect(self.unix_socket)
+ try:
+ self._sock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
+ self._sock.connect(self.unix_socket)
+ except socket.error:
+ self._sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+ self._sock.connect(self.unix_socket)
+
except Exception as e:
self.error(str(e),
"Cannot create socket with following configuration: host:", str(self.host),
'accesses': {
'options': [None, "tomcat requests", "requests/s", "statistics", "tomcat.accesses", "area"],
'lines': [
- ["accesses"]
+ ["accesses", None, 'incremental']
]},
'volume': {
'options': [None, "tomcat volume", "KB/s", "volume", "tomcat.volume", "area"],
'lines': [
- ["volume", None, 'incremental']
+ ["volume", None, 'incremental', 1, 1024]
]},
'threads': {
'options': [None, "tomcat threads", "current threads", "statistics", "tomcat.threads", "line"],
'jvm': {
'options': [None, "JVM Free Memory", "MB", "statistics", "tomcat.jvm", "area"],
'lines': [
- ["jvm", None, "absolute"]
+ ["jvm", None, "absolute", 1, 1048576]
]}
}
if self.port == 0:
self.port = 80
- if self._get_data() is None or len(self._get_data()) == 0:
+ test = self._get_data()
+ if test is None or len(test) == 0:
return False
else:
return True
try:
data = ET.fromstring(raw)
except ET.ParseError as e:
- #if e.code == errors.codes[errors.XML_ERROR_JUNK_AFTER_DOC_ELEMENT]:
+ # if e.code == errors.codes[errors.XML_ERROR_JUNK_AFTER_DOC_ELEMENT]:
if e.code == 9:
- # cut rest of invalid string
- pos = 0
- for i in range(e.position[0] - 1):
- pos += raw.find('\n', pos)
- raw = raw[:47604 + pos + e.position[0] - 1]
+ end = raw.find('</status>')
+ end += 9
+ raw = raw[:end]
+ self.debug(raw)
data = ET.fromstring(raw)
else:
raise Exception(e)
[Service]
Type=forking
WorkingDirectory=/tmp
-User=root
-Group=root
-PIDFile=@localstatedir_POST@/run/netdata.pid
-ExecStart=@sbindir_POST@/netdata -P @localstatedir_POST@/run/netdata.pid
+User=netdata
+Group=netdata
+RuntimeDirectory=netdata
+PIDFile=@localstatedir_POST@/run/netdata/netdata.pid
+ExecStart=@sbindir_POST@/netdata -P @localstatedir_POST@/run/netdata/netdata.pid
KillMode=mixed
KillSignal=SIGTERM
TimeoutStopSec=30
+#Hardening
+AmbientCapabilities=CAP_DAC_READ_SEARCH CAP_SYS_PTRACE
+CapabilityBoundingSet=CAP_DAC_READ_SEARCH CAP_SYS_PTRACE
+PrivateTmp=true
+ProtectSystem=full
+ProtectHome=read-only
+#NoNewPrivileges=true is implicitly set by the MemoryDenyWriteExecute=true
+MemoryDenyWriteExecute=true
+
[Install]
WantedBy=multi-user.target
// var netdataNoRegistry = true; // Don't update the registry for this access
// var netdataRegistryCallback = null; // Callback function that will be invoked with one param,
// the URLs from the registry
+// var netdataShowHelp = true; // enable/disable help
//
// You can also set the default netdata server, using the following.
// When this variable is not set, we assume the page is hosted on your
else
NETDATA.themes.current = NETDATA.themes.white;
+ if(typeof netdataShowHelp === 'undefined')
+ netdataShowHelp = true;
+
NETDATA.colors = NETDATA.themes.current.colors;
// these are the colors Google Charts are using
destroy_on_hide: false, // destroy charts when they are not visible
- show_help: true, // when enabled the charts will show some help
+ show_help: netdataShowHelp, // when enabled the charts will show some help
show_help_delay_show_ms: 500,
show_help_delay_hide_ms: 0,
try {
// console.log('localStorage: loading "' + key.toString() + '"');
ret = localStorage.getItem(key.toString());
+ // console.log('netdata loaded: ' + key.toString() + ' = ' + ret.toString());
if(ret === null || ret === 'undefined') {
// console.log('localStorage: cannot load it, saving "' + key.toString() + '" with value "' + JSON.stringify(def) + '"');
localStorage.setItem(key.toString(), JSON.stringify(def));
force_before_ms: null, // the timespan to sync all other charts
force_after_ms: null,
+ callback: null,
+
// set a new master
setMaster: function(state, after, before) {
if(NETDATA.options.current.sync_pan_and_zoom === false)
this.force_after_ms = after;
this.force_before_ms = before;
NETDATA.options.auto_refresher_stop_until = now + NETDATA.options.current.global_pan_sync_time;
+
+ if(typeof this.callback === 'function')
+ this.callback(true, after, before);
},
// clear the master
this.force_after_ms = null;
this.force_before_ms = null;
NETDATA.options.auto_refresher_stop_until = 0;
+
+ if(typeof this.callback === 'function')
+ this.callback(false, 0, 0);
},
// is the given state the master of the global
NETDATA.parseDom(NETDATA.chartRefresher);
// Registry initialization
- setTimeout(NETDATA.registry.init, 3000);
+ setTimeout(NETDATA.registry.init, 1000);
};
// ----------------------------------------------------------------------------------------------------------------
padding-top: 50px;
}
+ .loadOverlay {
+ position: absolute;
+ top: 0px;
+ left: 0px;
+ width: 100%;
+ height:100%;
+ z-index: 2000;
+ font-size: 10vh;
+ font-family: sans-serif;
+ padding: 40vh 0 40vh 0;
+ font-weight: bold;
+ text-align: center;
+ }
+
.modal-wide .modal-dialog {
width: 80%;
}
}
</style>
- <!-- you can set your netdata server globally, by ucommenting this -->
- <!-- you can also give a different server per chart, with the attribute: data-host="http://netdata.server:19999" -->
- <!-- <script> netdataServer = "http://box:19999"; </script> -->
-
<!-- check which theme to use -->
- <script>
+ <script type="text/javascript">
+ // --------------------------------------------------------------------
+ // urlOptions
+
+ var urlOptions = {
+ hash: '#',
+ theme: null,
+ help: null,
+ pan_and_zoom: false,
+ after: 0,
+ before: 0,
+ nowelcome: 0,
+ hasProperty: function(property) {
+ // console.log('checking property ' + property + ' of type ' + typeof(this[property]));
+ return typeof this[property] !== 'undefined';
+ }
+ };
+
+ function netdataPanAndZoomCallback(status, after, before) {
+ urlOptions.pan_and_zoom = status;
+ urlOptions.after = after;
+ urlOptions.before = before;
+ netdataHashUpdate();
+ }
+
+ function netdataHashUpdate() {
+ history.replaceState(null, document.title, netdataHash());
+ }
+
+ function netdataHash() {
+ var hash = urlOptions.hash;
+
+ if(urlOptions.pan_and_zoom === true) {
+ hash += ';after=' + urlOptions.after.toString() +
+ ';before=' + urlOptions.before.toString();
+ }
+
+ if(urlOptions.theme !== null)
+ hash += ';theme=' + urlOptions.theme.toString();
+
+ if(urlOptions.help !== null)
+ hash += ';help=' + urlOptions.help.toString();
+
+ return hash;
+ }
+
+ function netdataHashParse() {
+ var variables = document.location.hash.split(';');
+ var len = variables.length;
+ while(len--) {
+ if(len !== 0) {
+ var p = variables[len].split('=');
+ if(urlOptions.hasProperty(p[0]) && typeof p[1] !== 'undefined')
+ urlOptions[p[0]] = p[1];
+ }
+ else {
+ if(variables[len].length > 0)
+ urlOptions.hash = variables[len];
+ }
+ }
+
+ if(urlOptions.before > 0 && urlOptions.after > 0)
+ urlOptions.pan_and_zoom = true;
+
+ // console.log(urlOptions);
+ }
+
+ netdataHashParse();
+
+ // --------------------------------------------------------------------
+ // check options that should be processed before loading netdata.js
+
function loadLocalStorage(name) {
var ret = null;
if(typeof ret === 'undefined' || ret === null)
return null;
+ // console.log('loaded: ' + name.toString() + ' = ' + ret.toString());
+
return ret;
}
function saveLocalStorage(name, value) {
+ // console.log('saving: ' + name.toString() + ' = ' + value.toString());
try {
if(typeof Storage !== "undefined" && typeof localStorage === 'object') {
localStorage.setItem(name, value.toString());
return ret;
}
- var netdataTheme = getTheme('slate');
-
function setTheme(theme) {
if(theme === netdataTheme) return false;
-
return saveLocalStorage('netdataTheme', theme);
}
+ var netdataTheme = getTheme('slate');
+ var netdataShowHelp = true;
+
+ if(urlOptions.theme !== null) {
+ setTheme(urlOptions.theme);
+ netdataTheme = urlOptions.theme;
+ }
+ else
+ urlOptions.theme = netdataTheme;
+
+ if(urlOptions.help !== null) {
+ saveLocalStorage('options.show_help', urlOptions.help);
+ netdataShowHelp = urlOptions.help;
+ }
+ else {
+ urlOptions.help = loadLocalStorage('options.show_help');
+ }
+
+ // --------------------------------------------------------------------
+ // registry call back to render my-netdata menu
+
var netdataRegistryCallback = function(machines_array) {
var el = '';
var a1 = '';
</script>
<!-- load the dashboard manager - it will do the rest -->
- <script type="text/javascript" src="dashboard.js?v40"></script>
+ <script type="text/javascript" src="dashboard.js?v41"></script>
</head>
-
<body data-spy="scroll" data-target="#sidebar">
+ <div id="loadOverlay" class="loadOverlay" style="background-color: #888; color: #888;">
+ netdata<br/><div style="font-size: 3vh;">Real-time performance monitoring, done right!</div>
+ </div>
+ <script type="text/javascript">
+ // change the loadOverlay colors ASAP to match the theme
+ document.getElementById('loadOverlay').style = (urlOptions.theme === 'slate')?"background-color:#272b30; color: #373b40;":"background-color:#fff; color: #ddd;";
+ </script>
<nav class="navbar navbar-default navbar-fixed-top" role="banner">
<div class="container">
<nav id="mynetdata_nav" class="collapse navbar-collapse navbar-left hidden-sm hidden-xs" role="navigation" style="padding-right: 20px;">
</div>
</div>
-<script>
+<script type="text/javascript">
var this_is_demo = null;
function isdemo() {
if(this_is_demo !== null) return this_is_demo;
if(isdemo()) {
document.getElementById('masthead').style.display = 'block';
}
+
+function netdataURL(url) {
+ if(typeof url === 'undefined')
+ url = document.location.toString();
+
+ if(url.indexOf('#') !== -1)
+ url = url.substring(0, url.indexOf('#'));
+
+ var hash = netdataHash();
+
+ // console.log('netdataURL: ' + url + hash);
+
+ return url + hash;
+}
+
+function netdataReload(url) {
+ var t = netdataURL(url);
+ // console.log('netdataReload: ' + t);
+ document.location = t;
+
+ // since we play with hash
+ // this is needed to reload the page
+ location.reload();
+}
+
var gotoServerValidateRemaining = 0;
var gotoServerMiddleClick = false;
var gotoServerStop = false;
// to allow the user walk through all its servers.
penaldy = 500;
+ var finalURL = netdataURL(url);
+
setTimeout(function() {
- document.getElementById('gotoServerList').innerHTML += '<tr><td style="padding-left: 20px;"><a href="' + url + '" target="_blank">' + url + '</a></td><td style="padding-left: 30px;"><code id="' + guid + '-' + id + '-status">checking...</code></td></tr>';
+ document.getElementById('gotoServerList').innerHTML += '<tr><td style="padding-left: 20px;"><a href="' + finalURL + '" target="_blank">' + url + '</a></td><td style="padding-left: 30px;"><code id="' + guid + '-' + id + '-status">checking...</code></td></tr>';
NETDATA.registry.hello(url, function(data) {
if (data) {
// console.log('OK ' + id + ' URL: ' + url);
document.getElementById(guid + '-' + id + '-status').innerHTML = "OK";
- var hash = document.location.hash || '';
if(!gotoServerStop) {
gotoServerStop = true;
if(gotoServerMiddleClick) {
- window.open(url + hash, '_blank');
+ window.open(finalURL, '_blank');
gotoServerMiddleClick = false;
- document.getElementById('gotoServerResponse').innerHTML = '<b>Opening new window to ' + NETDATA.registry.machines[guid].name + '<br/><a href="' + url + hash + '">' + url + hash + '</a></b><br/>(check your pop-up blocker if it fails)';
+ document.getElementById('gotoServerResponse').innerHTML = '<b>Opening new window to ' + NETDATA.registry.machines[guid].name + '<br/><a href="' + finalURL + '">' + url + '</a></b><br/>(check your pop-up blocker if it fails)';
}
else
- document.location = url + hash;
+ document.location = finalURL;
}
}
else {
info: undefined
},
+ 'ipfs': {
+ title: 'IPFS',
+ info: undefined
+ },
+
'phpfpm': {
title: 'PHP-FPM',
info: undefined,
case 'phpfpm':
case 'postfix':
case 'redis':
+ case 'ipfs':
case 'squid':
case 'snmp':
case 'tomcat':
netdata_url = NETDATA.serverDefault;
NETDATA.pause(function() {
+ $("#loadOverlay").css("display","none");
+ $("#loadOverlay").css("display","none");
// download all the charts the server knows
NETDATA.chartRegistry.downloadAll(netdata_url, function(data) {
// ----------------------------------------------------------------------------
-function getUrlParameter(sParam) {
- var sPageURL = decodeURIComponent(window.location.search.substring(1)),
- sURLVariables = sPageURL.split('&'),
- sParameterName,
- i;
-
- for (i = 0; i < sURLVariables.length; i++) {
- sParameterName = sURLVariables[i].split('=');
-
- if (sParameterName[0] === sParam) {
- return sParameterName[1] === undefined ? true : sParameterName[1];
- }
- }
-}
-
function finalizePage() {
// resize all charts - without starting the background thread
// this has to be done while NETDATA is paused
// the Dom elements are initially zero-sized
NETDATA.parseDom();
- var before = 0, after = 0, nowelcome = 0;
- after = getUrlParameter('force_after_ms');
- before = getUrlParameter('force_before_ms');
- nowelcome = (getUrlParameter('nowelcome') === true)?true:false;
-
- if(before > 0 && after > 0) {
- nowelcome = true;
- NETDATA.globalPanAndZoom.setMaster(NETDATA.options.targets[0], after, before);
+ if(urlOptions.pan_and_zoom === true) {
+ urlOptions.nowelcome = true;
+ NETDATA.globalPanAndZoom.setMaster(NETDATA.options.targets[0], urlOptions.after, urlOptions.before);
}
// ------------------------------------------------------------------------
$(".chart-message").shorten();
// ------------------------------------------------------------------------
+ // callback for us to track PanAndZoom operations
+ NETDATA.globalPanAndZoom.callback = netdataPanAndZoomCallback;
+
// let it run (update the charts)
NETDATA.unpause();
// check if we have to jump to a specific section
- scrollToId(location.hash.replace('#',''));
+ scrollToId(urlOptions.hash.replace('#',''));
/* activate bootstrap sidebar (affix) */
$('#sidebar').affix({
// change the URL based on the current position of the screen
$('#sidebar').on('activate.bs.scrollspy', function (e) {
var el = $(e.target);
- if(el.find('ul').size() == 0)
- history.replaceState(null, document.title, el.find('a').attr('href'));
+ if(el.find('ul').size() == 0) {
+ var hash = el.find('a').attr('href');
+ if(typeof hash === 'string' && hash.substring(0, 1) == '#') {
+ urlOptions.hash = hash;
+ // console.log(urlOptions.hash);
+ netdataHashUpdate();
+ }
+ }
});
document.getElementById('footer').style.display = 'block';
$('#stop_updates_when_focus_is_lost').change(function() { NETDATA.setOption('stop_updates_when_focus_is_lost', $(this).prop('checked')); });
$('#smooth_plot').change(function() { NETDATA.setOption('smooth_plot', $(this).prop('checked')); });
$('#pan_and_zoom_data_padding').change(function() { NETDATA.setOption('pan_and_zoom_data_padding', $(this).prop('checked')); });
- $('#show_help').change(function() { NETDATA.setOption('show_help', $(this).prop('checked')); location.reload(); });
+ $('#show_help').change(function() {
+ urlOptions.help = $(this).prop('checked');
+ NETDATA.setOption('show_help', urlOptions.help);
+ netdataReload();
+ });
// this has to be the last
// it reloads the page
$('#netdata_theme_control').change(function() {
- if(setTheme($(this).prop('checked')?'slate':'white'))
- location.reload();
+ urlOptions.theme = $(this).prop('checked')?'slate':'white';
+ if(setTheme(urlOptions.theme))
+ netdataReload();
});
$('#updateModal').on('shown.bs.modal', function() {
});
if(isdemo()) {
- if(!nowelcome) {
+ if(!urlOptions.nowelcome) {
setTimeout(function() {
$('#welcomeModal').modal();
}, 1000);
NETDATA.resetOptions();
if(setTheme('slate'))
- location.reload();
+ netdataReload();
if(help !== NETDATA.options.current.show_help)
- location.reload();
+ netdataReload();
}
downloadAllCharts();