diff options
author | Lennart Weller <lhw@ring0.de> | 2017-01-24 15:21:09 +0000 |
---|---|---|
committer | Lennart Weller <lhw@ring0.de> | 2017-01-24 15:21:09 +0000 |
commit | 3ed3b02ed96ddab1c084811f3579b3a2aec83e04 (patch) | |
tree | 7a61ab288ae47800c4f11be5677d6ad8288dcd98 | |
parent | New upstream version 1.4.0+dfsg (diff) | |
download | netdata-3ed3b02ed96ddab1c084811f3579b3a2aec83e04.tar.xz netdata-3ed3b02ed96ddab1c084811f3579b3a2aec83e04.zip |
New upstream version 1.5.0+dfsgupstream/1.5.0+dfsg
Diffstat (limited to '')
372 files changed, 47094 insertions, 26261 deletions
diff --git a/.codeclimate.yml b/.codeclimate.yml new file mode 100644 index 000000000..91e0babae --- /dev/null +++ b/.codeclimate.yml @@ -0,0 +1,66 @@ +--- +engines: + csslint: + enabled: true + duplication: + enabled: true + config: + languages: + - ruby + - javascript + - python + - php + checks: + Similar code: + enabled: false + eslint: + enabled: true + checks: + max-statements: + enabled: false + complexity: + enabled: false + no-eval: + enabled: false + no-extend-native: + enabled: false + no-void: + enabled: false + no-alert: + enabled: false + fixme: + enabled: false + phpmd: + enabled: true + radon: + enabled: true + checks: + Complexity: + enabled: false +ratings: + paths: + - "**.css" + - "**.inc" + - "**.js" + - "**.jsx" + - "**.module" + - "**.php" + - "**.py" + - "**.rb" +exclude_paths: +- .gitignore +- conf.d/ +- hooks/ +- tests/ +- m4/ +- web/css/ +- web/lib/ +- web/fonts/ +- web/old/ +- python.d/python_modules/pyyaml2/ +- python.d/python_modules/pyyaml3/ +- node.d/node_modules/ber/ +- node.d/node_modules/asn1.js +- node.d/node_modules/extend.js +- node.d/node_modules/pixl-xml.js +- node.d/node_modules/net-snmp.js diff --git a/.csslintrc b/.csslintrc new file mode 100644 index 000000000..aacba956e --- /dev/null +++ b/.csslintrc @@ -0,0 +1,2 @@ +--exclude-exts=.min.css +--ignore=adjoining-classes,box-model,ids,order-alphabetical,unqualified-attributes diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 000000000..96212a359 --- /dev/null +++ b/.eslintignore @@ -0,0 +1 @@ +**/*{.,-}min.js diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 000000000..9faa37508 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,213 @@ +ecmaFeatures: + modules: true + jsx: true + +env: + amd: true + browser: true + es6: true + jquery: true + node: true + +# http://eslint.org/docs/rules/ +rules: + # Possible Errors + comma-dangle: [2, never] + no-cond-assign: 2 + no-console: 0 + no-constant-condition: 2 + no-control-regex: 2 + no-debugger: 2 + no-dupe-args: 2 + no-dupe-keys: 2 + no-duplicate-case: 2 + no-empty: 2 + no-empty-character-class: 2 + no-ex-assign: 2 + no-extra-boolean-cast: 2 + no-extra-parens: 0 + no-extra-semi: 2 + no-func-assign: 2 + no-inner-declarations: [2, functions] + no-invalid-regexp: 2 + no-irregular-whitespace: 2 + no-negated-in-lhs: 2 + no-obj-calls: 2 + no-regex-spaces: 2 + no-sparse-arrays: 2 + no-unexpected-multiline: 2 + no-unreachable: 2 + use-isnan: 2 + valid-jsdoc: 0 + valid-typeof: 2 + + # Best Practices + accessor-pairs: 2 + block-scoped-var: 0 + complexity: [2, 6] + consistent-return: 0 + curly: 0 + default-case: 0 + dot-location: 0 + dot-notation: 0 + eqeqeq: 2 + guard-for-in: 2 + no-alert: 2 + no-caller: 2 + no-case-declarations: 2 + no-div-regex: 2 + no-else-return: 0 + no-empty-label: 2 + no-empty-pattern: 2 + no-eq-null: 2 + no-eval: 2 + no-extend-native: 2 + no-extra-bind: 2 + no-fallthrough: 2 + no-floating-decimal: 0 + no-implicit-coercion: 0 + no-implied-eval: 2 + no-invalid-this: 0 + no-iterator: 2 + no-labels: 0 + no-lone-blocks: 2 + no-loop-func: 2 + no-magic-number: 0 + no-multi-spaces: 0 + no-multi-str: 0 + no-native-reassign: 2 + no-new-func: 2 + no-new-wrappers: 2 + no-new: 2 + no-octal-escape: 2 + no-octal: 2 + no-proto: 2 + no-redeclare: 2 + no-return-assign: 2 + no-script-url: 2 + no-self-compare: 2 + no-sequences: 0 + no-throw-literal: 0 + no-unused-expressions: 2 + no-useless-call: 2 + no-useless-concat: 2 + no-void: 2 + no-warning-comments: 0 + no-with: 2 + radix: 2 + vars-on-top: 0 + wrap-iife: 2 + yoda: 0 + + # Strict + strict: 0 + + # Variables + init-declarations: 0 + no-catch-shadow: 2 + no-delete-var: 2 + no-label-var: 2 + no-shadow-restricted-names: 2 + no-shadow: 0 + no-undef-init: 2 + no-undef: 0 + no-undefined: 0 + no-unused-vars: 0 + no-use-before-define: 0 + + # Node.js and CommonJS + callback-return: 2 + global-require: 2 + handle-callback-err: 2 + no-mixed-requires: 0 + no-new-require: 0 + no-path-concat: 2 + no-process-exit: 2 + no-restricted-modules: 0 + no-sync: 0 + + # Stylistic Issues + array-bracket-spacing: 0 + block-spacing: 0 + brace-style: 0 + camelcase: 0 + comma-spacing: 0 + comma-style: 0 + computed-property-spacing: 0 + consistent-this: 0 + eol-last: 0 + func-names: 0 + func-style: 0 + id-length: 0 + id-match: 0 + indent: 0 + jsx-quotes: 0 + key-spacing: 0 + linebreak-style: 0 + lines-around-comment: 0 + max-depth: 0 + max-len: 0 + max-nested-callbacks: 0 + max-params: 0 + max-statements: [2, 30] + new-cap: 0 + new-parens: 0 + newline-after-var: 0 + no-array-constructor: 0 + no-bitwise: 0 + no-continue: 0 + no-inline-comments: 0 + no-lonely-if: 0 + no-mixed-spaces-and-tabs: 0 + no-multiple-empty-lines: 0 + no-negated-condition: 0 + no-nested-ternary: 0 + no-new-object: 0 + no-plusplus: 0 + no-restricted-syntax: 0 + no-spaced-func: 0 + no-ternary: 0 + no-trailing-spaces: 0 + no-underscore-dangle: 0 + no-unneeded-ternary: 0 + object-curly-spacing: 0 + one-var: 0 + operator-assignment: 0 + operator-linebreak: 0 + padded-blocks: 0 + quote-props: 0 + quotes: 0 + require-jsdoc: 0 + semi-spacing: 0 + semi: 0 + sort-vars: 0 + space-after-keywords: 0 + space-before-blocks: 0 + space-before-function-paren: 0 + space-before-keywords: 0 + space-in-parens: 0 + space-infix-ops: 0 + space-return-throw-case: 0 + space-unary-ops: 0 + spaced-comment: 0 + wrap-regex: 0 + + # ECMAScript 6 + arrow-body-style: 0 + arrow-parens: 0 + arrow-spacing: 0 + constructor-super: 0 + generator-star-spacing: 0 + no-arrow-condition: 0 + no-class-assign: 0 + no-const-assign: 0 + no-dupe-class-members: 0 + no-this-before-super: 0 + no-var: 0 + object-shorthand: 0 + prefer-arrow-callback: 0 + prefer-const: 0 + prefer-reflect: 0 + prefer-spread: 0 + prefer-template: 0 + require-yield: 0 diff --git a/.gitignore b/.gitignore index c281ea324..e5f27403d 100644 --- a/.gitignore +++ b/.gitignore @@ -84,8 +84,17 @@ cmake_install.cmake .jetbrains* +.DS_Store + contrib/debian/changelog profile/benchmark-dictionary profile/benchmark-registry *.pyc + +diagrams/*.png +diagrams/*.svg +diagrams/*.atxt +diagrams/plantuml.jar + +netdata.cppcheck diff --git a/.travis/decrypt-if-have-key b/.travis/decrypt-if-have-key new file mode 100755 index 000000000..7fcab8970 --- /dev/null +++ b/.travis/decrypt-if-have-key @@ -0,0 +1,33 @@ +#!/bin/bash + +set -e + +# Decrypt our private files; changes to this file should be inspected +# closely to ensure they do not create information leaks + +eval key="\${encrypted_${1}_key}" +eval iv="\${encrypted_${1}_iv}" + +if [ ! "$key" ] +then + echo "No aes key present - skipping decryption" + exit 0 +fi + +for i in .travis/*.enc +do + u=$(echo $i | sed -e 's/.enc$//') + openssl aes-256-cbc -K "$key" -iv "$iv" -in $i -out $u -d +done + +if [ -f .travis/travis_rsa ] +then + echo "ssh key present - loading to agent" + # add key, then remove to prevent leaks + chmod 600 .travis/travis_rsa + ssh-add .travis/travis_rsa + rm -f .travis/travis_rsa + touch /tmp/ssh-key-loaded +else + echo "No ssh key present - skipping agent start" +fi diff --git a/.travis/deploy-if-have-key b/.travis/deploy-if-have-key new file mode 100755 index 000000000..50e69b939 --- /dev/null +++ b/.travis/deploy-if-have-key @@ -0,0 +1,44 @@ +#!/bin/bash + +set -e + +# Deploy tar-files and checksums to the firehol website + +if [ ! -f /tmp/ssh-key-loaded ] +then + echo "No ssh key decrypted - skipping deployment to website" + exit 0 +fi + +case "$TRAVIS_BRANCH" in + master|stable-*) + : + ;; + *) + echo "Not on master or stable-* branch - skipping deployment to website" + exit 0 + ;; +esac + +if [ "$TRAVIS_PULL_REQUEST" = "true" ] +then + echo "Building pull request - skipping deployment to website" + exit 0 +fi + +if [ "$TRAVIS_TAG" != "" ] +then + echo "Building tag - skipping deployment to website" + exit 0 +fi + +if [ "$CC" != "gcc" ] +then + echo "Building non-gcc version - skipping deployment to website" + exit 0 +fi + +ssh-keyscan -H firehol.org >> ~/.ssh/known_hosts +ssh travis@firehol.org mkdir -p uploads/netdata/$TRAVIS_BRANCH/ +scp -p *.tar.* travis@firehol.org:uploads/netdata/$TRAVIS_BRANCH/ +ssh travis@firehol.org touch uploads/netdata/$TRAVIS_BRANCH/complete.txt diff --git a/.travis/travis_rsa.enc b/.travis/travis_rsa.enc Binary files differnew file mode 100644 index 000000000..148a425bc --- /dev/null +++ b/.travis/travis_rsa.enc @@ -1,3 +1,133 @@ +netdata (1.5.0) - 2016-01-22 + + * yet another release that makes netdata the fastest + netdata ever! + + * netdata runs on FreeBSD, FreeNAS and MacOS ! + + Vladimir Kobal (@vlvkobal) has done a magnificent work + porting netdata to FreeBSD and MacOS. + + Everyhing works: cpu, memory, disks performance, disks space, + network interfaces, interrupts, IPv4 metrics, IPv6 metrics + processes, context switches, softnet, IPC queues, + IPC semaphores, IPC shared memory, uptime, etc. Wow! + + * netdata supports data archiving to backend databases: + + - Graphite + - OpenTSDB + - Prometheus + + and of course all the compatible ones + (KairosDB, InfluxDB, Blueflood, etc) + + * new plugins: + + Ilya Mashchenko (@l2isbad) has created most of the python + data collection plugins in this release ! + + - Systemd Services (using cgroups!) + - FPing (yes, network latency in netdata!) + - postgres databases @facetoe, @moumoul + - Vanish disk cache (v3 and v4) @l2isbad + - ElasticSearch @l2isbad + - HAproxy @l2isbad + - FreeRadius @l2isbad, @lgz + - mdstat (RAID) @l2isbad + - ISC bind (via rndc) @l2isbad + - ISC dhcpd @l2isbad, @lgz + - Fail2Ban @l2isbad + - OpenVPN status log @l2isbad, @lgz + - NUMA memory @tycho + - CPU Idle @tycho + - gunicorn log @deltaskelta + - ECC memory hardware errors + - IPC semaphores + - uptime plugin (with a nice badge too) + + * improved plugins: + + - netfilter conntrack + - mysql (replication) @l2isbad + - ipfs @pjz + - cpufreq @tycho + - hddtemp @l2isbad + - sensors @l2isbad + - nginx @leolovenet + - nginx_log @paulfantom + - phpfpm @leolovenet + - redis @leolovenet + - dovecot @justohall + - cgroups + - disk space + - apps.plugin + - /proc/interrupts @rlefevre + - /proc/softirqs @rlefevre + - /proc/vmstat (system memory charts) + - /proc/net/snmp6 (IPv6 charts) + - /proc/self/meminfo (system memory charts) + - /proc/net/dev (network interfaces) + - tc (linux QoS) + + * new/improved alarms: + + - MySQL / MariaDB alarms (incl. replication) + - IPFS alarms + - HAproxy alarms + - UDP buffer alarms + - TCP AttemptFails + - ECC memory alarms + - netfilter connections alarms + - SNMP + + * new alarm notifications: + + - messagebird.com @tech-no-logical + - pagerduty.com @jimcooley + - pushbullet.com @tperalta82 + - twilio.com @shadycuz + - HipChat + - kafka + + * shell integration + + - shell scripts can now query netdata easily! + + * dashboard improvements: + - dashboard is now faster on firefox, safari, opera, edge + (edge is still the slowest) + - dashboard now has a little bigger fonts + - SHIFT + mouse wheel to zoom charts, works on all browsers + - perfect-scrollbar on the dashboard + - dashboard 4K resolution fixes + - dashboard compatibility fixes for embedding charts in + third party web sites + - charts on custom dashboards can have common min/max + even if they come from different netdata servers + - alarm log is now saved and loaded back so that + the alarm history is available at the dashboard + + * other improvements: + - python.d.plugin has received way to many improvements + from many contributors! + - charts.d.plugin can now be forked to support + multiple independent instances + - registry has been re-factored to lower its memory + requirements (required for the public registry) + - simple patterns in cgroups, disks and alarms + - netdata-installer.sh can now correctly install + netdata in containers + - supplied logrotate script compatibility fixes + - spec cleanup @breed808 + - clocks and timers reworked @rlefevre + + netdata has received a lot more improvements from many more + contributors! + + Thank you all guys! + + netdata (1.4.0) - 2016-10-04 At a glance: diff --git a/LICENSE.md b/LICENSE.md index f07429000..79891e5a3 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,6 +1,6 @@ # Netdata -Copyright 2014-2016, Costa Tsaousis. +Copyright 2016-2017, Costa Tsaousis. Released under [GPL v3 or later](http://www.gnu.org/licenses/gpl-3.0.en.html). --- @@ -55,22 +55,28 @@ connectivity is not available. [MIT License](http://getbootstrap.com/getting-started/#license-faqs) -- [NanoScroller](https://jamesflorentino.github.io/nanoScrollerJS/) - - Copyright 2012, James Florentino - [MIT License](https://github.com/jamesflorentino/nanoScrollerJS/blob/master/LICENSE) - - - [Bootstrap Toggle](http://www.bootstraptoggle.com/) Copyright (c) 2011-2014 Min Hur, The New York Times Company [MIT License](https://github.com/minhur/bootstrap-toggle/blob/master/LICENSE) -- [AVL C Library](http://freecode.com/projects/avl) +- [bootstrap-table](http://bootstrap-table.wenzhixin.net.cn/) + + Copyright (c) 2012-2016 Zhixin Wen <wenzhixin2010@gmail.com> + [MIT License](https://github.com/wenzhixin/bootstrap-table/blob/master/LICENSE) + + +- [tableExport.jquery.plugin](https://github.com/hhurz/tableExport.jquery.plugin) + + Copyright (c) 2015,2016 hhurz + [MIT License](http://rawgit.com/hhurz/tableExport.jquery.plugin/master/tableExport.js) + + +- [perfect-scrollbar](https://jamesflorentino.github.io/nanoScrollerJS/) - Copyright 2000, Daniel Nagy, Budapest University of Technology and Economics - Released under GNU General Public License (GPL) version 2 + Copyright 2016, Hyunje Alex Jun and other contributors + [MIT License](https://github.com/noraesae/perfect-scrollbar/blob/master/LICENSE) - [FontAwesome](https://fortawesome.github.io/Font-Awesome/) @@ -142,14 +148,3 @@ connectivity is not available. Copyright 2006, Kirill Simonov [MIT License](http://pyyaml.org) -- [bootstrap-table](http://bootstrap-table.wenzhixin.net.cn/) - - Copyright (c) 2012-2016 Zhixin Wen <wenzhixin2010@gmail.com> - [MIT License](https://github.com/wenzhixin/bootstrap-table/blob/master/LICENSE) - -- [tableExport.jquery.plugin](https://github.com/hhurz/tableExport.jquery.plugin) - - Copyright (c) 2015,2016 hhurz - [MIT License](http://rawgit.com/hhurz/tableExport.jquery.plugin/master/tableExport.js) - - diff --git a/Makefile.am b/Makefile.am index ea1ac256b..ab77bc734 100644 --- a/Makefile.am +++ b/Makefile.am @@ -18,6 +18,13 @@ MAINTAINERCLEANFILES= \ EXTRA_DIST = \ .gitignore \ + .codeclimate.yml \ + .csslintrc \ + .eslintignore \ + .eslintrc \ + .travis \ + m4/ax_check_enable_debug.m4 \ + m4/ax_c_statement_expressions.m4 \ autogen.sh \ README.md \ LICENSE.md \ @@ -39,6 +46,8 @@ SUBDIRS = \ $(NULL) dist_noinst_DATA= \ + diagrams/config.puml \ + diagrams/registry.puml \ configs.signatures \ Dockerfile \ netdata.spec \ @@ -47,7 +56,7 @@ dist_noinst_DATA= \ # until integrated within build # should be proper init.d/openrc/systemd usable dist_noinst_SCRIPTS= \ - ansible/netdata.yml \ + diagrams/build.sh \ coverity-scan.sh \ docker-build.sh \ netdata-installer.sh \ diff --git a/Makefile.in b/Makefile.in index 67397d055..412345f61 100644 --- a/Makefile.in +++ b/Makefile.in @@ -1,7 +1,7 @@ -# Makefile.in generated by automake 1.14.1 from Makefile.am. +# Makefile.in generated by automake 1.15 from Makefile.am. # @configure_input@ -# Copyright (C) 1994-2013 Free Software Foundation, Inc. +# Copyright (C) 1994-2014 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -16,7 +16,17 @@ VPATH = @srcdir@ -am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ @@ -80,21 +90,20 @@ POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = . -DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ - $(top_srcdir)/configure $(am__configure_deps) \ - $(srcdir)/config.h.in $(srcdir)/netdata.spec.in \ - $(dist_noinst_SCRIPTS) $(dist_noinst_DATA) COPYING ChangeLog \ - compile config.guess config.sub install-sh missing ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_c___atomic.m4 \ $(top_srcdir)/m4/ax_c__generic.m4 \ $(top_srcdir)/m4/ax_c_mallinfo.m4 \ $(top_srcdir)/m4/ax_c_mallopt.m4 \ $(top_srcdir)/m4/ax_check_compile_flag.m4 \ + $(top_srcdir)/m4/ax_gcc_func_attribute.m4 \ $(top_srcdir)/m4/ax_pthread.m4 $(top_srcdir)/m4/jemalloc.m4 \ $(top_srcdir)/m4/tcmalloc.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(top_srcdir)/configure \ + $(am__configure_deps) $(dist_noinst_SCRIPTS) \ + $(dist_noinst_DATA) $(am__DIST_COMMON) am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \ configure.lineno config.status.lineno mkinstalldirs = $(install_sh) -d @@ -160,6 +169,9 @@ ETAGS = etags CTAGS = ctags CSCOPE = cscope DIST_SUBDIRS = $(SUBDIRS) +am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/config.h.in \ + $(srcdir)/netdata.spec.in COPYING ChangeLog compile \ + config.guess config.sub install-sh missing DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) distdir = $(PACKAGE)-$(VERSION) top_distdir = $(distdir) @@ -358,6 +370,13 @@ MAINTAINERCLEANFILES = \ EXTRA_DIST = \ .gitignore \ + .codeclimate.yml \ + .csslintrc \ + .eslintignore \ + .eslintrc \ + .travis \ + m4/ax_check_enable_debug.m4 \ + m4/ax_c_statement_expressions.m4 \ autogen.sh \ README.md \ LICENSE.md \ @@ -379,6 +398,8 @@ SUBDIRS = \ $(NULL) dist_noinst_DATA = \ + diagrams/config.puml \ + diagrams/registry.puml \ configs.signatures \ Dockerfile \ netdata.spec \ @@ -388,7 +409,7 @@ dist_noinst_DATA = \ # until integrated within build # should be proper init.d/openrc/systemd usable dist_noinst_SCRIPTS = \ - ansible/netdata.yml \ + diagrams/build.sh \ coverity-scan.sh \ docker-build.sh \ netdata-installer.sh \ @@ -413,7 +434,6 @@ $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__confi echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign Makefile -.PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ @@ -635,15 +655,15 @@ dist-xz: distdir $(am__post_remove_distdir) dist-tarZ: distdir - @echo WARNING: "Support for shar distribution archives is" \ - "deprecated." >&2 + @echo WARNING: "Support for distribution archives compressed with" \ + "legacy program 'compress' is deprecated." >&2 @echo WARNING: "It will be removed altogether in Automake 2.0" >&2 tardir=$(distdir) && $(am__tar) | compress -c >$(distdir).tar.Z $(am__post_remove_distdir) dist-shar: distdir - @echo WARNING: "Support for distribution archives compressed with" \ - "legacy program 'compress' is deprecated." >&2 + @echo WARNING: "Support for shar distribution archives is" \ + "deprecated." >&2 @echo WARNING: "It will be removed altogether in Automake 2.0" >&2 shar $(distdir) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).shar.gz $(am__post_remove_distdir) @@ -679,17 +699,17 @@ distcheck: dist esac chmod -R a-w $(distdir) chmod u+w $(distdir) - mkdir $(distdir)/_build $(distdir)/_inst + mkdir $(distdir)/_build $(distdir)/_build/sub $(distdir)/_inst chmod a-w $(distdir) test -d $(distdir)/_build || exit 0; \ dc_install_base=`$(am__cd) $(distdir)/_inst && pwd | sed -e 's,^[^:\\/]:[\\/],/,'` \ && dc_destdir="$${TMPDIR-/tmp}/am-dc-$$$$/" \ && am__cwd=`pwd` \ - && $(am__cd) $(distdir)/_build \ - && ../configure \ + && $(am__cd) $(distdir)/_build/sub \ + && ../../configure \ $(AM_DISTCHECK_CONFIGURE_FLAGS) \ $(DISTCHECK_CONFIGURE_FLAGS) \ - --srcdir=.. --prefix="$$dc_install_base" \ + --srcdir=../.. --prefix="$$dc_install_base" \ && $(MAKE) $(AM_MAKEFLAGS) \ && $(MAKE) $(AM_MAKEFLAGS) dvi \ && $(MAKE) $(AM_MAKEFLAGS) check \ @@ -865,6 +885,8 @@ uninstall-am: mostlyclean-generic pdf pdf-am ps ps-am tags tags-am uninstall \ uninstall-am +.PRECIOUS: Makefile + # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. @@ -1,148 +1,240 @@ -[![Build Status](https://travis-ci.org/firehol/netdata.svg?branch=master)](https://travis-ci.org/firehol/netdata) -<a href="https://scan.coverity.com/projects/firehol-netdata"><img alt="Coverity Scan Build Status" src="https://scan.coverity.com/projects/9140/badge.svg"/></a> -[![Docker Pulls](https://img.shields.io/docker/pulls/titpetric/netdata.svg)](https://hub.docker.com/r/titpetric/netdata/) +# netdata [![Build Status](https://travis-ci.org/firehol/netdata.svg?branch=master)](https://travis-ci.org/firehol/netdata) [![Coverity Scan Build Status](https://scan.coverity.com/projects/9140/badge.svg)](https://scan.coverity.com/projects/firehol-netdata) [![Code Climate](https://codeclimate.com/github/firehol/netdata/badges/gpa.svg)](https://codeclimate.com/github/firehol/netdata) [![Docker Pulls](https://img.shields.io/docker/pulls/titpetric/netdata.svg)](https://hub.docker.com/r/titpetric/netdata/) +> *New to netdata? Here is a live demo: [http://my-netdata.io](http://my-netdata.io)* -[![User Base](https://registry.my-netdata.io/api/v1/badge.svg?chart=netdata.registry_entries&dimensions=persons&label=user%20base&units=null&value_color=blue&precision=0&v42)](https://registry.my-netdata.io/#menu_netdata_submenu_registry) -[![Monitored Servers](https://registry.my-netdata.io/api/v1/badge.svg?chart=netdata.registry_entries&dimensions=machines&label=servers%20monitored&units=null&value_color=orange&precision=0&v42)](https://registry.my-netdata.io/#menu_netdata_submenu_registry) -[![Sessions Served](https://registry.my-netdata.io/api/v1/badge.svg?chart=netdata.registry_sessions&label=sessions%20served&units=null&value_color=yellowgreen&precision=0&v42)](https://registry.my-netdata.io/#menu_netdata_submenu_registry) +**netdata** is a system for **distributed real-time performance and health monitoring**. +It provides **unparalleled insights, in real-time**, of everything happening on the +system it runs (including applications such as web and database servers), using +**modern interactive web dashboards**. -[![New Users Today](http://registry.my-netdata.io/api/v1/badge.svg?chart=netdata.registry_entries&dimensions=persons&after=-86400&options=unaligned&group=incremental-sum&label=new%20users%20today&units=null&value_color=blue&precision=0&v42)](https://registry.my-netdata.io/#menu_netdata_submenu_registry) -[![New Machines Today](https://registry.my-netdata.io/api/v1/badge.svg?chart=netdata.registry_entries&dimensions=machines&group=incremental-sum&after=-86400&options=unaligned&label=servers%20added%20today&units=null&value_color=orange&precision=0&v42)](https://registry.my-netdata.io/#menu_netdata_submenu_registry) -[![Sessions Today](https://registry.my-netdata.io/api/v1/badge.svg?chart=netdata.registry_sessions&after=-86400&group=incremental-sum&options=unaligned&label=sessions%20served%20today&units=null&value_color=yellowgreen&precision=0&v42)](https://registry.my-netdata.io/#menu_netdata_submenu_registry) +_netdata is **fast** and **efficient**, designed to permanently run on all systems +(**physical** & **virtual** servers, **containers**, **IoT** devices), without +disrupting their core function._ +--- -# netdata - -> Oct 4th, 2016 -> -> [netdata v1.4.0 released!](https://github.com/firehol/netdata/releases) -> -> - the fastest netdata ever (with a better look too)! -> - improved IoT and containers support! -> - alarms improved in almost every way! -> - new plugins: softnet netdev, extended TCP metrics, UDPLite, NFS v2, v3 client (server was there already), NFS v4 server & client, APCUPSd, RetroShare -> - improved plugins: mysql, cgroups, hddtemp, sensors, phpfm, tc (QoS) +## User base ---- +*Since May 16th 2016 (the date the [global public netdata registry](https://github.com/firehol/netdata/wiki/mynetdata-menu-item) was released):*<br/> +[![User Base](https://registry.my-netdata.io/api/v1/badge.svg?chart=netdata.registry_entries&dimensions=persons&label=user%20base&units=null&value_color=blue&precision=0&v42)](https://registry.my-netdata.io/#menu_netdata_submenu_registry) [![Monitored Servers](https://registry.my-netdata.io/api/v1/badge.svg?chart=netdata.registry_entries&dimensions=machines&label=servers%20monitored&units=null&value_color=orange&precision=0&v42)](https://registry.my-netdata.io/#menu_netdata_submenu_registry) [![Sessions Served](https://registry.my-netdata.io/api/v1/badge.svg?chart=netdata.registry_sessions&label=sessions%20served&units=null&value_color=yellowgreen&precision=0&v42)](https://registry.my-netdata.io/#menu_netdata_submenu_registry) -> Aug 28th, 2016 -> -> [netdata v1.3.0 released!](https://github.com/firehol/netdata/releases) -> -> - netdata has **[health monitoring / alarms](https://github.com/firehol/netdata/wiki/health-monitoring)**! -> - netdata **[generates badges](https://github.com/firehol/netdata/wiki/Generating-Badges)** that can be embeded anywhere! -> - netdata plugins are now written in python! -> - new plugins: redis, memcached, nginx_log, ipfs, apache_cache +*in the last 24 hours:*<br/> +[![New Users Today](http://registry.my-netdata.io/api/v1/badge.svg?chart=netdata.registry_entries&dimensions=persons&after=-86400&options=unaligned&group=incremental-sum&label=new%20users%20today&units=null&value_color=blue&precision=0&v42)](https://registry.my-netdata.io/#menu_netdata_submenu_registry) [![New Machines Today](https://registry.my-netdata.io/api/v1/badge.svg?chart=netdata.registry_entries&dimensions=machines&group=incremental-sum&after=-86400&options=unaligned&label=servers%20added%20today&units=null&value_color=orange&precision=0&v42)](https://registry.my-netdata.io/#menu_netdata_submenu_registry) [![Sessions Today](https://registry.my-netdata.io/api/v1/badge.svg?chart=netdata.registry_sessions&after=-86400&group=incremental-sum&options=unaligned&label=sessions%20served%20today&units=null&value_color=yellowgreen&precision=0&v42)](https://registry.my-netdata.io/#menu_netdata_submenu_registry) --- -**Real-time performance and health monitoring, done right!** +## News -This is the default dashboard of **netdata**: +<p align="center"> +Netdata is featured at <b><a href="https://octoverse.github.com/" target="_blank">GitHub's State Of The Octoverse 2016</a></b><br/> +<a href="https://octoverse.github.com/" target="_blank"><img src="https://cloud.githubusercontent.com/assets/2662304/21743260/23ebe62c-d507-11e6-80c0-76b95f53e464.png"/></a> +</p> - - real-time, per second updates, snappy refreshes! - - 300+ charts out of the box, 2000+ metrics monitored! - - zero configuration, zero maintenance, zero dependencies! - - dozens of health monitoring alarms, out of the box! +`Jan 22nd, 2017` - **[netdata v1.5.0 released!](https://github.com/firehol/netdata/releases)** -Live demo: [http://my-netdata.io](http://my-netdata.io) - -![netdata](https://cloud.githubusercontent.com/assets/2662304/14092712/93b039ea-f551-11e5-822c-beadbf2b2a2e.gif) + - netdata now runs on **FreeBSD** and **MacOS** + - netdata now supports **Graphite**, **OpenTSDB**, **Prometheus** and compatible backends + - netdata now monitors **SystemD Services** + - new plugins: fping, postgres, varnish, elasticsearch, haproxy, freeradius, mdstat, ISC dhcpd, fail2ban, openvpn, NUMA memory, CPU Idle States, gunicorn, ECC memory errors, IPC semaphores, uptime + - improved plugins: netfilter conntrack, mysql/mariadb, ipfs, cpufreq, hddtemp, sensors, nginx, nginx_log, phpfpm, redis, dovecot, containers and cgroups, disk space, apps.plugin, tc (QoS) and almost all internal plugins (memory, IPv4 and IPv6, network interfaces, QoS, etc) + - dozens of new and improved alarms (including performance monitoring alarms for mysql) + - new alarm notifications: messagebird.com, pagerduty.com, pushbullet.com, twilio.com, hipchat, kafka + - dozens more improvements and performance optimizations --- ## Features -**netdata** is a highly optimized Linux daemon providing **real-time performance monitoring for Linux systems, Applications, SNMP devices, over the web**! - -It tries to visualize the **truth of now**, in its **greatest detail**, so that you can get insights of what is happening now and what just happened, on your systems and applications. +<p align="center"> +<img src="https://cloud.githubusercontent.com/assets/2662304/19168687/f6a567be-8c19-11e6-8561-ce8d589e8346.gif"/> +</p> + + - **Stunning interactive bootstrap dashboards**<br/> + mouse and touch friendly, in 2 themes: dark, light + + - **Amazingly fast**<br/> + responds to all queries in less than 0.5 ms per metric, + even on low-end hardware + + - **Highly efficient**<br/> + collects thousands of metrics per server per second, + with just 1% CPU utilization of a single core, a few MB of RAM and no disk I/O at all + + - **Sophisticated alarming**<br/> + supports dynamic thresholds, hysteresis, alarm templates, + multiple role-based notification methods (such as email, slack.com, + pushover.net, pushbullet.com telegram.org, twilio.com, messagebird.com) + + - **Extensible**<br/> + you can monitor anything you can get a metric for, + using its Plugin API (anything can be a netdata plugin, + BASH, python, perl, node.js, java, Go, ruby, etc) + + - **Embeddable**<br/> + it can run anywhere a Linux kernel runs (even IoT) + and its charts can be embedded on your web pages too + + - **Customizable**<br/> + custom dashboards can be built using simple HTML (no javascript necessary) + + - **Zero configuration**<br/> + auto-detects everything, it can collect up to 5000 metrics + per server out of the box + + - **Zero dependencies**<br/> + it is even its own web server, for its static web files and its web API + + - **Zero maintenance**<br/> + you just run it, it does the rest + + - **scales to infinity**<br/> + requiring minimal central resources + + - **back-ends supported**<br/> + can archive its metrics on `graphite` or `opentsdb`, in the same or lower detail + (lower: to prevent it from congesting these servers due to the amount of data collected) -This is what you get: - -- **Stunning bootstrap dashboards**, out of the box (theme-able: dark, light) -- **Blazingly fast** and **super efficient**, mostly written in C (for default installations, expect just 2% of a single core CPU usage and a few MB of RAM) -- **Zero configuration** - you just install it and it auto-detects everything -- **Zero dependencies**, it is its own web server for its static web files and its web API -- **Zero maintenance**, you just run it, it does the rest -- **Custom dashboards** that can be built using simple HTML (no javascript necessary) -- **Extensible**, you can monitor anything you can get a metric for, using its Plugin API (anything can be a netdata plugin - from BASH to python and node.js, so you can easily monitor any application, any API) -- **Embeddable**, it can run anywhere a Linux kernel runs (even IoT) and its charts can be embedded on your web pages too +![netdata](https://cloud.githubusercontent.com/assets/2662304/14092712/93b039ea-f551-11e5-822c-beadbf2b2a2e.gif) --- ## What does it monitor? -This is what it currently monitors (most with zero configuration): +netdata monitors several thousands of metrics per device. +All these metrics are collected and visualized in real-time. + +> _Almost all metrics are auto-detected, without any configuration._ -- **CPU usage, interrupts, softirqs and frequency** (total and per core) +This is a list of what it currently monitors: -- **RAM, swap and kernel memory usage** (including KSM and kernel memory deduper) +- **CPU**<br/> + usage, interrupts, softirqs, frequency, total and per core -- **Disks** (per disk: I/O, operations, backlog, utilization, space, etc) +- **Memory**<br/> + RAM, swap and kernel memory usage, KSM (Kernel Samepage Merging), NUMA + +- **Disks**<br/> + per disk: I/O, operations, backlog, utilization, space ![sda](https://cloud.githubusercontent.com/assets/2662304/14093195/c882bbf4-f554-11e5-8863-1788d643d2c0.gif) -- **Network interfaces** (per interface: bandwidth, packets, errors, drops, etc) +- **Network interfaces**<br/> + per interface: bandwidth, packets, errors, drops ![dsl0](https://cloud.githubusercontent.com/assets/2662304/14093128/4d566494-f554-11e5-8ee4-5392e0ac51f0.gif) -- **IPv4 networking** (bandwidth, packets, errors, fragments, tcp: connections, packets, errors, handshake, udp: packets, errors, broadcast: bandwidth, packets, multicast: bandwidth, packets) +- **IPv4 networking**<br/> + bandwidth, packets, errors, fragments, + tcp: connections, packets, errors, handshake, + udp: packets, errors, + broadcast: bandwidth, packets, + multicast: bandwidth, packets + +- **IPv6 networking**<br/> + bandwidth, packets, errors, fragments, ECT, + udp: packets, errors, + udplite: packets, errors, + broadcast: bandwidth, + multicast: bandwidth, packets, + icmp: messages, errors, echos, router, neighbor, MLDv2, group membership, + break down by type + +- **Interprocess Communication - IPC**<br/> + such as semaphores and semaphores arrays -- **IPv6 networking** (bandwidth, packets, errors, fragments, ECT, udp: packets, errors, udplite: packets, errors, broadcast: bandwidth, multicast: bandwidth, packets, icmp: messages, errors, echos, router, neighbor, MLDv2, group membership, break down by type) +- **netfilter / iptables Linux firewall**<br/> + connections, connection tracker events, errors -- **netfilter / iptables Linux firewall** (connections, connection tracker events, errors, etc) +- **Linux DDoS protection**<br/> + SYNPROXY metrics -- **Linux DDoS protection** (SYNPROXY metrics) +- **fping** latencies</br> + for any number of hosts, showing latency, packets and packet loss -- **Processes** (running, blocked, forks, active, etc) + ![image](https://cloud.githubusercontent.com/assets/2662304/20464811/9517d2b4-af57-11e6-8361-f6cc57541cd7.png) -- **Entropy** (random numbers pool, using in cryptography) -- **NFS file servers and clients**, v2, v3, v4 (I/O, cache, read ahead, RPC calls) +- **Processes**<br/> + running, blocked, forks, active -- **Network QoS** (yes, the only tool that visualizes network `tc` classes in realtime) +- **Entropy**<br/> + random numbers pool, using in cryptography + +- **NFS file servers and clients**<br/> + NFS v2, v3, v4: I/O, cache, read ahead, RPC calls + +- **Network QoS**<br/> + the only tool that visualizes network `tc` classes in realtime ![qos-tc-classes](https://cloud.githubusercontent.com/assets/2662304/14093004/68966020-f553-11e5-98fe-ffee2086fafd.gif) -- **Linux Control Groups** (containers), systemd, lxc, docker, etc +- **Linux Control Groups**<br/> + containers: systemd, lxc, docker -- **Applications**, by grouping the process tree (CPU, memory, disk reads, disk writes, swap, threads, pipes, sockets, etc) +- **Applications**<br/> + by grouping the process tree and reporting CPU, memory, disk reads, + disk writes, swap, threads, pipes, sockets - per group ![apps](https://cloud.githubusercontent.com/assets/2662304/14093565/67c4002c-f557-11e5-86bd-0154f5135def.gif) -- **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) +- **Users and User Groups resource usage**<br/> + by summarizing the process tree per user and group, + reporting: CPU, memory, disk reads, disk writes, swap, threads, pipes, sockets + +- **Apache and lighttpd web servers**<br/> + `mod-status` (v2.2, v2.4) and cache log statistics, for multiple servers -- **Apache web servers** mod-status (v2.2, v2.4) and cache log statistics (multiple servers - compatible with lighttpd too) +- **Nginx web servers**<br/> + `stub-status`, for multiple servers -- **Nginx web servers** stub-status (multiple servers) +- **Tomcat**<br/> + accesses, threads, free memory, volume -- **mySQL databases** (multiple servers, each showing: bandwidth, queries/s, handlers, locks, issues, tmp operations, connections, binlog metrics, threads, innodb metrics, etc) +- **mySQL databases**<br/> + multiple servers, each showing: bandwidth, queries/s, handlers, locks, issues, + tmp operations, connections, binlog metrics, threads, innodb metrics, and more -- **Redis databases** (multiple servers, each showing: operations, hit rate, memory, keys, clients, slaves) +- **Postgres databases**<br/> + multiple servers, each showing: per database statistics (connections, tuples + read - written - returned, transactions, locks), backend processes, indexes, + tables, write ahead, background writer and more -- **memcached databases** (multiple servers, each showing: bandwidth, connections, items, etc) +- **Redis databases**<br/> + multiple servers, each showing: operations, hit rate, memory, keys, clients, slaves -- **ISC Bind name servers** (multiple servers, each showing: clients, requests, queries, updates, failures and several per view metrics) +- **memcached databases**<br/> + multiple servers, each showing: bandwidth, connections, items -- **Postfix email servers** message queue (entries, size) +- **ISC Bind name servers**<br/> + multiple servers, each showing: clients, requests, queries, updates, failures and several per view metrics -- **exim email servers** message queue (emails queued) +- **Postfix email servers**<br/> + message queue (entries, size) -- **IPFS** (Bandwidth, Peers) +- **exim email servers**<br/> + message queue (emails queued) -- **Squid proxy servers** (multiple servers, each showing: clients bandwidth and requests, servers bandwidth and requests) +- **Dovecot** POP3/IMAP servers<br/> -- **Hardware sensors** (temperature, voltage, fans, power, humidity, etc) +- **IPFS**<br/> + bandwidth, peers -- **NUT and APC UPSes** (load, charge, battery voltage, temperature, utility metrics, output metrics) +- **Squid proxy servers**<br/> + multiple servers, each showing: clients bandwidth and requests, servers bandwidth and requests -- **Tomcat** (accesses, threads, free memory, volume) +- **Hardware sensors**<br/> + temperature, voltage, fans, power, humidity -- **PHP-FPM** (multiple instances, each reporting connections, requests, performance) +- **NUT and APC UPSes**<br/> + load, charge, battery voltage, temperature, utility metrics, output metrics -- **hddtemp** (disk temperatures) +- **PHP-FPM**<br/> + multiple instances, each reporting connections, requests, performance -- **SNMP devices** can be monitored too (although you will need to configure these) +- **hddtemp**<br/> + disk temperatures + +- **SNMP devices**<br/> + 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. @@ -150,22 +242,30 @@ And you can extend it, by writing plugins that collect data from any source, usi ## Installation -Use our **[automatic installer](https://github.com/firehol/netdata/wiki/Installation)** to build and install it on your system +Use our **[automatic installer](https://github.com/firehol/netdata/wiki/Installation)** to build and install it on your system. -It should run on **any Linux** system. It has been tested on: +It should run on **any Linux** system (including IoT). It has been tested on: -- Gentoo +- Alpine - Arch Linux -- Ubuntu / Debian - CentOS +- Debian - Fedora +- Gentoo +- openSUSE +- PLD Linux - RedHat Enterprise Linux - SUSE -- Alpine Linux -- PLD Linux +- Ubuntu --- ## Documentation Check the **[netdata wiki](https://github.com/firehol/netdata/wiki)**. + +## License + +netdata is GPLv3+. + +It re-distributes other open-source tools and libraries. Please check its [License Statement](https://github.com/firehol/netdata/blob/master/LICENSE.md). diff --git a/aclocal.m4 b/aclocal.m4 index 76042ca3f..9867db245 100644 --- a/aclocal.m4 +++ b/aclocal.m4 @@ -1,6 +1,6 @@ -# generated automatically by aclocal 1.14.1 -*- Autoconf -*- +# generated automatically by aclocal 1.15 -*- Autoconf -*- -# Copyright (C) 1996-2013 Free Software Foundation, Inc. +# Copyright (C) 1996-2014 Free Software Foundation, Inc. # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -180,7 +180,62 @@ else fi[]dnl ])# PKG_CHECK_MODULES -# Copyright (C) 2002-2013 Free Software Foundation, Inc. + +# PKG_INSTALLDIR(DIRECTORY) +# ------------------------- +# Substitutes the variable pkgconfigdir as the location where a module +# should install pkg-config .pc files. By default the directory is +# $libdir/pkgconfig, but the default can be changed by passing +# DIRECTORY. The user can override through the --with-pkgconfigdir +# parameter. +AC_DEFUN([PKG_INSTALLDIR], +[m4_pushdef([pkg_default], [m4_default([$1], ['${libdir}/pkgconfig'])]) +m4_pushdef([pkg_description], + [pkg-config installation directory @<:@]pkg_default[@:>@]) +AC_ARG_WITH([pkgconfigdir], + [AS_HELP_STRING([--with-pkgconfigdir], pkg_description)],, + [with_pkgconfigdir=]pkg_default) +AC_SUBST([pkgconfigdir], [$with_pkgconfigdir]) +m4_popdef([pkg_default]) +m4_popdef([pkg_description]) +]) dnl PKG_INSTALLDIR + + +# PKG_NOARCH_INSTALLDIR(DIRECTORY) +# ------------------------- +# Substitutes the variable noarch_pkgconfigdir as the location where a +# module should install arch-independent pkg-config .pc files. By +# default the directory is $datadir/pkgconfig, but the default can be +# changed by passing DIRECTORY. The user can override through the +# --with-noarch-pkgconfigdir parameter. +AC_DEFUN([PKG_NOARCH_INSTALLDIR], +[m4_pushdef([pkg_default], [m4_default([$1], ['${datadir}/pkgconfig'])]) +m4_pushdef([pkg_description], + [pkg-config arch-independent installation directory @<:@]pkg_default[@:>@]) +AC_ARG_WITH([noarch-pkgconfigdir], + [AS_HELP_STRING([--with-noarch-pkgconfigdir], pkg_description)],, + [with_noarch_pkgconfigdir=]pkg_default) +AC_SUBST([noarch_pkgconfigdir], [$with_noarch_pkgconfigdir]) +m4_popdef([pkg_default]) +m4_popdef([pkg_description]) +]) dnl PKG_NOARCH_INSTALLDIR + + +# PKG_CHECK_VAR(VARIABLE, MODULE, CONFIG-VARIABLE, +# [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) +# ------------------------------------------- +# Retrieves the value of the pkg-config variable for the given module. +AC_DEFUN([PKG_CHECK_VAR], +[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl +AC_ARG_VAR([$1], [value of $3 for $2, overriding pkg-config])dnl + +_PKG_CONFIG([$1], [variable="][$3]["], [$2]) +AS_VAR_COPY([$1], [pkg_cv_][$1]) + +AS_VAR_IF([$1], [""], [$5], [$4])dnl +])# PKG_CHECK_VAR + +# Copyright (C) 2002-2014 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -192,10 +247,10 @@ fi[]dnl # generated from the m4 files accompanying Automake X.Y. # (This private macro should not be called outside this file.) AC_DEFUN([AM_AUTOMAKE_VERSION], -[am__api_version='1.14' +[am__api_version='1.15' dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to dnl require some minimum version. Point them to the right macro. -m4_if([$1], [1.14.1], [], +m4_if([$1], [1.15], [], [AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl ]) @@ -211,14 +266,14 @@ m4_define([_AM_AUTOCONF_VERSION], []) # Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced. # This function is AC_REQUIREd by AM_INIT_AUTOMAKE. AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION], -[AM_AUTOMAKE_VERSION([1.14.1])dnl +[AM_AUTOMAKE_VERSION([1.15])dnl m4_ifndef([AC_AUTOCONF_VERSION], [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl _AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))]) # AM_AUX_DIR_EXPAND -*- Autoconf -*- -# Copyright (C) 2001-2013 Free Software Foundation, Inc. +# Copyright (C) 2001-2014 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -263,15 +318,14 @@ _AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))]) # configured tree to be moved without reconfiguration. AC_DEFUN([AM_AUX_DIR_EXPAND], -[dnl Rely on autoconf to set up CDPATH properly. -AC_PREREQ([2.50])dnl -# expand $ac_aux_dir to an absolute path -am_aux_dir=`cd $ac_aux_dir && pwd` +[AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT])dnl +# Expand $ac_aux_dir to an absolute path. +am_aux_dir=`cd "$ac_aux_dir" && pwd` ]) # AM_CONDITIONAL -*- Autoconf -*- -# Copyright (C) 1997-2013 Free Software Foundation, Inc. +# Copyright (C) 1997-2014 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -302,7 +356,7 @@ AC_CONFIG_COMMANDS_PRE( Usually this means the macro was only invoked conditionally.]]) fi])]) -# Copyright (C) 1999-2013 Free Software Foundation, Inc. +# Copyright (C) 1999-2014 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -493,7 +547,7 @@ _AM_SUBST_NOTMAKE([am__nodep])dnl # Generate code to set up dependency tracking. -*- Autoconf -*- -# Copyright (C) 1999-2013 Free Software Foundation, Inc. +# Copyright (C) 1999-2014 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -569,7 +623,7 @@ AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS], # Do all the work for Automake. -*- Autoconf -*- -# Copyright (C) 1996-2013 Free Software Foundation, Inc. +# Copyright (C) 1996-2014 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -659,8 +713,8 @@ AC_REQUIRE([AC_PROG_MKDIR_P])dnl # <http://lists.gnu.org/archive/html/automake/2012-07/msg00001.html> # <http://lists.gnu.org/archive/html/automake/2012-07/msg00014.html> AC_SUBST([mkdir_p], ['$(MKDIR_P)']) -# We need awk for the "check" target. The system "awk" is bad on -# some platforms. +# We need awk for the "check" target (and possibly the TAP driver). The +# system "awk" is bad on some platforms. AC_REQUIRE([AC_PROG_AWK])dnl AC_REQUIRE([AC_PROG_MAKE_SET])dnl AC_REQUIRE([AM_SET_LEADING_DOT])dnl @@ -733,7 +787,11 @@ to "yes", and re-run configure. END AC_MSG_ERROR([Your 'rm' program is bad, sorry.]) fi -fi]) +fi +dnl The trailing newline in this macro's definition is deliberate, for +dnl backward compatibility and to allow trailing 'dnl'-style comments +dnl after the AM_INIT_AUTOMAKE invocation. See automake bug#16841. +]) dnl Hook into '_AC_COMPILER_EXEEXT' early to learn its expansion. Do not dnl add the conditional right here, as _AC_COMPILER_EXEEXT may be further @@ -762,7 +820,7 @@ for _am_header in $config_headers :; do done echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count]) -# Copyright (C) 2001-2013 Free Software Foundation, Inc. +# Copyright (C) 2001-2014 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -773,7 +831,7 @@ echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_co # Define $install_sh. AC_DEFUN([AM_PROG_INSTALL_SH], [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl -if test x"${install_sh}" != xset; then +if test x"${install_sh+set}" != xset; then case $am_aux_dir in *\ * | *\ *) install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;; @@ -783,7 +841,7 @@ if test x"${install_sh}" != xset; then fi AC_SUBST([install_sh])]) -# Copyright (C) 2003-2013 Free Software Foundation, Inc. +# Copyright (C) 2003-2014 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -805,7 +863,7 @@ AC_SUBST([am__leading_dot])]) # Add --enable-maintainer-mode option to configure. -*- Autoconf -*- # From Jim Meyering -# Copyright (C) 1996-2013 Free Software Foundation, Inc. +# Copyright (C) 1996-2014 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -840,7 +898,7 @@ AC_MSG_CHECKING([whether to enable maintainer-specific portions of Makefiles]) # Check to see how 'make' treats includes. -*- Autoconf -*- -# Copyright (C) 2001-2013 Free Software Foundation, Inc. +# Copyright (C) 2001-2014 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -890,7 +948,7 @@ rm -f confinc confmf # Fake the existence of programs that GNU maintainers use. -*- Autoconf -*- -# Copyright (C) 1997-2013 Free Software Foundation, Inc. +# Copyright (C) 1997-2014 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -929,7 +987,7 @@ fi # Helper functions for option handling. -*- Autoconf -*- -# Copyright (C) 2001-2013 Free Software Foundation, Inc. +# Copyright (C) 2001-2014 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -958,7 +1016,7 @@ AC_DEFUN([_AM_SET_OPTIONS], AC_DEFUN([_AM_IF_OPTION], [m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])]) -# Copyright (C) 1999-2013 Free Software Foundation, Inc. +# Copyright (C) 1999-2014 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -1005,7 +1063,7 @@ AC_LANG_POP([C])]) # For backward compatibility. AC_DEFUN_ONCE([AM_PROG_CC_C_O], [AC_REQUIRE([AC_PROG_CC])]) -# Copyright (C) 2001-2013 Free Software Foundation, Inc. +# Copyright (C) 2001-2014 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -1024,7 +1082,7 @@ AC_DEFUN([AM_RUN_LOG], # Check to make sure that the build environment is sane. -*- Autoconf -*- -# Copyright (C) 1996-2013 Free Software Foundation, Inc. +# Copyright (C) 1996-2014 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -1105,7 +1163,7 @@ AC_CONFIG_COMMANDS_PRE( rm -f conftest.file ]) -# Copyright (C) 2009-2013 Free Software Foundation, Inc. +# Copyright (C) 2009-2014 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -1165,7 +1223,7 @@ AC_SUBST([AM_BACKSLASH])dnl _AM_SUBST_NOTMAKE([AM_BACKSLASH])dnl ]) -# Copyright (C) 2001-2013 Free Software Foundation, Inc. +# Copyright (C) 2001-2014 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -1193,7 +1251,7 @@ fi INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" AC_SUBST([INSTALL_STRIP_PROGRAM])]) -# Copyright (C) 2006-2013 Free Software Foundation, Inc. +# Copyright (C) 2006-2014 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -1212,7 +1270,7 @@ AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)]) # Check how to create a tarball. -*- Autoconf -*- -# Copyright (C) 2004-2013 Free Software Foundation, Inc. +# Copyright (C) 2004-2014 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -1348,6 +1406,7 @@ m4_include([m4/ax_c__generic.m4]) m4_include([m4/ax_c_mallinfo.m4]) m4_include([m4/ax_c_mallopt.m4]) m4_include([m4/ax_check_compile_flag.m4]) +m4_include([m4/ax_gcc_func_attribute.m4]) m4_include([m4/ax_pthread.m4]) m4_include([m4/jemalloc.m4]) m4_include([m4/tcmalloc.m4]) diff --git a/ansible/netdata.yml b/ansible/netdata.yml deleted file mode 100644 index 4c7e73681..000000000 --- a/ansible/netdata.yml +++ /dev/null @@ -1,62 +0,0 @@ ---- -- name: "Install pre-requisites" - apt: - name: "{{ item }}" - state: present - with_items: - - autoconf - - autoconf-archive - - autogen - - automake - - gcc - - git - - libmnl-dev - - make - - pkg-config - - uuid-dev - - zlib1g-dev - when: ansible_os_family == "Debian" - -- name: "Install pre-requisites" - yum: - name: "{{ item }}" - state: present - with_items: - - autoconf - - autoconf-archive - - autogen - - automake - - curl - - gcc - - git - - jq - - libmnl-devel - - libuuid-devel - - make - - pkgconfig - - zlib-devel - when: ansible_os_family == "RedHat" - -- name: "Clone repo" - git: - clone: yes - repo: https://github.com/firehol/netdata.git - dest: /tmp/netdata - -- name: "Installation" - shell: cd /tmp/netdata/ && ./netdata-installer.sh --dont-wait --libs-are-really-here - -- name: "Clean /tmp" - file: - path: /tmp/netdata - state: absent - -- name: "KillAll" - shell: killall netdata - -- name: "Daemon config" - systemd: - daemon_reload: yes - name: netdata - enabled: yes - state: started diff --git a/charts.d/Makefile.in b/charts.d/Makefile.in index fabbb6dde..ef63f78f3 100644 --- a/charts.d/Makefile.in +++ b/charts.d/Makefile.in @@ -1,7 +1,7 @@ -# Makefile.in generated by automake 1.14.1 from Makefile.am. +# Makefile.in generated by automake 1.15 from Makefile.am. # @configure_input@ -# Copyright (C) 1994-2013 Free Software Foundation, Inc. +# Copyright (C) 1994-2014 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -16,7 +16,17 @@ VPATH = @srcdir@ -am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ @@ -80,18 +90,19 @@ POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = charts.d -DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ - $(dist_charts_SCRIPTS) $(dist_charts_DATA) ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_c___atomic.m4 \ $(top_srcdir)/m4/ax_c__generic.m4 \ $(top_srcdir)/m4/ax_c_mallinfo.m4 \ $(top_srcdir)/m4/ax_c_mallopt.m4 \ $(top_srcdir)/m4/ax_check_compile_flag.m4 \ + $(top_srcdir)/m4/ax_gcc_func_attribute.m4 \ $(top_srcdir)/m4/ax_pthread.m4 $(top_srcdir)/m4/jemalloc.m4 \ $(top_srcdir)/m4/tcmalloc.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(dist_charts_SCRIPTS) \ + $(dist_charts_DATA) $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = @@ -146,6 +157,7 @@ am__can_run_installinfo = \ esac DATA = $(dist_charts_DATA) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +am__DIST_COMMON = $(srcdir)/Makefile.in DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ @@ -329,7 +341,6 @@ $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__confi echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu charts.d/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu charts.d/Makefile -.PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ @@ -559,6 +570,8 @@ uninstall-am: uninstall-dist_chartsDATA uninstall-dist_chartsSCRIPTS pdf-am ps ps-am tags-am uninstall uninstall-am \ uninstall-dist_chartsDATA uninstall-dist_chartsSCRIPTS +.PRECIOUS: Makefile + # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. diff --git a/charts.d/ap.chart.sh b/charts.d/ap.chart.sh index 7b4f690bb..0e85c486d 100755 --- a/charts.d/ap.chart.sh +++ b/charts.d/ap.chart.sh @@ -1,5 +1,11 @@ # no need for shebang - this file is loaded from charts.d.plugin +# netdata +# real-time performance and health monitoring, done right! +# (C) 2016 Costa Tsaousis <costa@tsaousis.gr> +# GPL v3+ +# + # _update_every is a special variable - it holds the number of seconds # between the calls of the _update() function ap_update_every= @@ -7,13 +13,11 @@ ap_priority=6900 declare -A ap_devs=() -export PATH="${PATH}:/sbin:/usr/sbin:/usr/local/sbin" - # _check is called once, to find out if this chart should be enabled or not ap_check() { require_cmd iw || return 1 - local ev=$(iw dev | awk ' + local ev=$(run iw dev | awk ' BEGIN { i = ""; ssid = ""; @@ -43,6 +47,7 @@ ap_check() { # - 1 to disable the chart [ ${#ap_devs[@]} -gt 0 ] && return 0 + error "no devices found in AP mode, with 'iw dev'" return 1 } diff --git a/charts.d/apache.chart.sh b/charts.d/apache.chart.sh index 2d68d43b2..b503e74e3 100755 --- a/charts.d/apache.chart.sh +++ b/charts.d/apache.chart.sh @@ -1,5 +1,11 @@ # no need for shebang - this file is loaded from charts.d.plugin +# netdata +# real-time performance and health monitoring, done right! +# (C) 2016 Costa Tsaousis <costa@tsaousis.gr> +# GPL v3+ +# + # the URL to download apache status info apache_url="http://127.0.0.1:80/server-status?auto" apache_curl_opts= @@ -67,14 +73,14 @@ apache_detect() { # we will not check of the Conns* # keys, since these are apache 2.4 specific - [ -z "${apache_key_accesses}" ] && echo >&2 "apache: missing 'Total Accesses' from apache server: ${*}" && return 1 - [ -z "${apache_key_kbytes}" ] && echo >&2 "apache: missing 'Total kBytes' from apache server: ${*}" && return 1 - [ -z "${apache_key_reqpersec}" ] && echo >&2 "apache: missing 'ReqPerSec' from apache server: ${*}" && return 1 - [ -z "${apache_key_bytespersec}" ] && echo >&2 "apache: missing 'BytesPerSec' from apache server: ${*}" && return 1 - [ -z "${apache_key_bytesperreq}" ] && echo >&2 "apache: missing 'BytesPerReq' from apache server: ${*}" && return 1 - [ -z "${apache_key_busyworkers}" ] && echo >&2 "apache: missing 'BusyWorkers' from apache server: ${*}" && return 1 - [ -z "${apache_key_idleworkers}" ] && echo >&2 "apache: missing 'IdleWorkers' from apache server: ${*}" && return 1 - [ -z "${apache_key_scoreboard}" ] && echo >&2 "apache: missing 'Scoreboard' from apache server: ${*}" && return 1 + [ -z "${apache_key_accesses}" ] && error "missing 'Total Accesses' from apache server: ${*}" && return 1 + [ -z "${apache_key_kbytes}" ] && error "missing 'Total kBytes' from apache server: ${*}" && return 1 + [ -z "${apache_key_reqpersec}" ] && error "missing 'ReqPerSec' from apache server: ${*}" && return 1 + [ -z "${apache_key_bytespersec}" ] && error "missing 'BytesPerSec' from apache server: ${*}" && return 1 + [ -z "${apache_key_bytesperreq}" ] && error "missing 'BytesPerReq' from apache server: ${*}" && return 1 + [ -z "${apache_key_busyworkers}" ] && error "missing 'BusyWorkers' from apache server: ${*}" && return 1 + [ -z "${apache_key_idleworkers}" ] && error "missing 'IdleWorkers' from apache server: ${*}" && return 1 + [ -z "${apache_key_scoreboard}" ] && error "missing 'Scoreboard' from apache server: ${*}" && return 1 if [ ! -z "${apache_key_connstotal}" \ -a ! -z "${apache_key_connsasyncwriting}" \ @@ -92,7 +98,7 @@ apache_detect() { apache_get() { local oIFS="${IFS}" ret - IFS=$':\n' apache_response=($(curl -Ss ${apache_curl_opts} "${apache_url}")) + IFS=$':\n' apache_response=($(run curl -Ss ${apache_curl_opts} "${apache_url}")) ret=$? IFS="${oIFS}" @@ -130,7 +136,7 @@ apache_get() { -o -z "${apache_idleworkers}" \ ] then - echo >&2 "apache: empty values got from apache server: ${apache_response[*]}" + error "empty values got from apache server: ${apache_response[*]}" return 1 fi @@ -151,7 +157,7 @@ apache_check() { apache_get if [ $? -ne 0 ] then - echo >&2 "apache: cannot find stub_status on URL '${apache_url}'. Please set apache_url='http://apache.server:80/server-status?auto' in $confd/apache.conf" + error "cannot find stub_status on URL '${apache_url}'. Please set apache_url='http://apache.server:80/server-status?auto' in $confd/apache.conf" return 1 fi diff --git a/charts.d/apcupsd.chart.sh b/charts.d/apcupsd.chart.sh index df18aaa2e..46a86101c 100755 --- a/charts.d/apcupsd.chart.sh +++ b/charts.d/apcupsd.chart.sh @@ -1,5 +1,11 @@ # no need for shebang - this file is loaded from charts.d.plugin +# netdata +# real-time performance and health monitoring, done right! +# (C) 2016 Costa Tsaousis <costa@tsaousis.gr> +# GPL v3+ +# + apcupsd_ip=127.0.0.1 apcupsd_port=3551 @@ -12,7 +18,7 @@ apcupsd_timeout=3 apcupsd_priority=90000 apcupsd_get() { - timeout $apcupsd_timeout apcaccess status "$1:$2" + run -t $apcupsd_timeout apcaccess status "$1:$2" } apcupsd_check() { @@ -23,14 +29,14 @@ apcupsd_check() { require_cmd apcaccess || return 1 - apcupsd_get $apcupsd_ip $apcupsd_port >/dev/null + run apcupsd_get $apcupsd_ip $apcupsd_port >/dev/null if [ $? -ne 0 ] then - echo >&2 "apcupsd: ERROR: Cannot get information for apcupsd server." + error "cannot get information for apcupsd server." return 1 elif [ $(apcupsd_get $apcupsd_ip $apcupsd_port | awk '/^STATUS.*/{ print $3 }') != "ONLINE" ] then - echo >&2 "apcupsd: ERROR: UPS not online." + error "APC UPS not online." return 1 fi @@ -62,7 +68,7 @@ DIMENSION output_voltage_nominal nominal absolute 1 100 CHART apcupsd.load '' "UPS Load" "percentage" ups apcupsd.load area $((apcupsd_priority)) $apcupsd_update_every DIMENSION load load absolute 1 100 -CHART apcupsd.temp '' "UPS Temperature" "Celcius" ups apcupsd.temperature line $((apcupsd_priority + 7)) $apcupsd_update_every +CHART apcupsd.temp '' "UPS Temperature" "Celsius" ups apcupsd.temperature line $((apcupsd_priority + 7)) $apcupsd_update_every DIMENSION temp temp absolute 1 100 CHART apcupsd.time '' "UPS Time Remaining" "Minutes" ups apcupsd.time area $((apcupsd_priority + 2)) $apcupsd_update_every @@ -146,7 +152,7 @@ END { print \"SET time = \" time; print \"END\" }" - [ $? -ne 0 ] && echo >&2 "apcupsd: failed to get values" && return 1 + [ $? -ne 0 ] && error "failed to get values" && return 1 return 0 } diff --git a/charts.d/cpu_apps.chart.sh b/charts.d/cpu_apps.chart.sh index 6b2513dcf..8e075831a 100755 --- a/charts.d/cpu_apps.chart.sh +++ b/charts.d/cpu_apps.chart.sh @@ -1,5 +1,10 @@ # no need for shebang - this file is loaded from charts.d.plugin +# netdata +# real-time performance and health monitoring, done right! +# (C) 2016 Costa Tsaousis <costa@tsaousis.gr> +# GPL v3+ +# # THIS PLUGIN IS OBSOLETE # USE apps.plugin INSTEAD @@ -19,7 +24,7 @@ cpu_apps_check() { if [ -z "$cpu_apps_apps" ] then - echo >&2 "$PROGRAM_NAME: cpu_apps: Please set cpu_apps_apps='command1 command2 ...' in $confd/cpu_apps_apps.conf" + error "manual configuration required: please set cpu_apps_apps='command1 command2 ...' in $confd/cpu_apps_apps.conf" return 1 fi return 0 diff --git a/charts.d/cpufreq.chart.sh b/charts.d/cpufreq.chart.sh index 06f692fa6..b21504a0e 100755 --- a/charts.d/cpufreq.chart.sh +++ b/charts.d/cpufreq.chart.sh @@ -1,9 +1,15 @@ # no need for shebang - this file is loaded from charts.d.plugin +# netdata +# real-time performance and health monitoring, done right! +# (C) 2016 Costa Tsaousis <costa@tsaousis.gr> +# GPL v3+ +# + # if this chart is called X.chart.sh, then all functions and global variables # must start with X_ -cpufreq_sys_dir="/sys/devices" +cpufreq_sys_dir="${NETDATA_HOST_PREFIX}/sys/devices" cpufreq_sys_depth=10 cpufreq_source_update=1 @@ -51,7 +57,7 @@ cpufreq_create() { id="$( fixid "cpu$cpu" )" - echo >&2 "charts.d: cpufreq: on file='$file', dir='$dir', cpu='$cpu', id='$id'" + debug "file='$file', dir='$dir', cpu='$cpu', id='$id'" echo "DIMENSION $id '$id' absolute 1 1000" echo >>$TMP_DIR/cpufreq.sh "echo \"SET $id = \"\$(< $file )" @@ -59,7 +65,6 @@ cpufreq_create() { echo >>$TMP_DIR/cpufreq.sh "echo END" [ $cpufreq_source_update -eq 1 ] && echo >>$TMP_DIR/cpufreq.sh "}" - # cat >&2 $TMP_DIR/cpufreq.sh # ok, load the function cpufreq_update() we created [ $cpufreq_source_update -eq 1 ] && . $TMP_DIR/cpufreq.sh diff --git a/charts.d/example.chart.sh b/charts.d/example.chart.sh index 93f1cf4fd..86fde4901 100755 --- a/charts.d/example.chart.sh +++ b/charts.d/example.chart.sh @@ -1,5 +1,11 @@ # no need for shebang - this file is loaded from charts.d.plugin +# netdata +# real-time performance and health monitoring, done right! +# (C) 2016 Costa Tsaousis <costa@tsaousis.gr> +# GPL v3+ +# + # if this chart is called X.chart.sh, then all functions and global variables # must start with X_ @@ -67,7 +73,7 @@ example_check() { # - 1 to disable the chart # check something - [ "${example_magic_number}" != "12345" ] && echo >&2 "example: you have to set example_magic_number=$example_magic_number in example.conf to start example chart." && return 1 + [ "${example_magic_number}" != "12345" ] && error "manual configuration required: you have to set example_magic_number=$example_magic_number in example.conf to start example chart." && return 1 # check that we can collect data example_get || return 1 @@ -108,7 +114,6 @@ BEGIN example.random2 $1 SET random = $example_value4 END VALUESEOF - # echo >&2 "example_count = $example_count value = $value4" return 0 } diff --git a/charts.d/exim.chart.sh b/charts.d/exim.chart.sh index c60ae9460..4c70f2c19 100644 --- a/charts.d/exim.chart.sh +++ b/charts.d/exim.chart.sh @@ -1,5 +1,13 @@ # no need for shebang - this file is loaded from charts.d.plugin +# netdata +# real-time performance and health monitoring, done right! +# (C) 2016 Costa Tsaousis <costa@tsaousis.gr> +# GPL v3+ +# +# Contributed by @jsveiga with PR #480 + +# the exim command to run exim_command= # how frequently to collect queue size @@ -8,28 +16,15 @@ exim_update_every=5 exim_priority=60000 exim_check() { - if [ -z "$exim_command" -o ! -x "$exim_command" ] - then - local d= - for d in /sbin /usr/sbin /usr/local/sbin - do - if [ -x "$d/exim" ] - then - exim_command="$d/exim" - break - fi - done - fi - - if [ -z "$exim_command" -o ! -x "$exim_command" ] - then - echo >&2 "$PROGRAM_NAME: exim: cannot find exim executable. Please set 'exim_command=/path/to/exim' in $confd/exim.conf" - return 1 - fi + if [ -z "${exim_command}" ] + then + require_cmd exim || return 1 + exim_command="${EXIM_CMD}" + fi - if [ `$exim_command -bpc 2>&1 | grep -c denied` -ne 0 ] + if [ $(${exim_command} -bpc 2>&1 | grep -c denied) -ne 0 ] then - echo >&2 "$PROGRAM_NAME: exim: permission denied. Please set 'queue_list_requires_admin = false' in your exim options file" + error "permission denied - please set 'queue_list_requires_admin = false' in your exim options file" return 1 fi @@ -37,16 +32,16 @@ exim_check() { } exim_create() { -cat <<EOF + cat <<EOF CHART exim_local.qemails '' "Exim Queue Emails" "emails" queue exim.queued.emails line $((exim_priority + 1)) $exim_update_every DIMENSION emails '' absolute 1 1 EOF -return 0 + return 0 } exim_update() { -echo "BEGIN exim_local.qemails $1" -echo "SET emails = " `$exim_command -bpc` -echo "END" -return 0 + echo "BEGIN exim_local.qemails $1" + echo "SET emails = " $(run ${exim_command} -bpc) + echo "END" + return 0 } diff --git a/charts.d/hddtemp.chart.sh b/charts.d/hddtemp.chart.sh index 41c3e2478..15895c5e1 100755 --- a/charts.d/hddtemp.chart.sh +++ b/charts.d/hddtemp.chart.sh @@ -1,5 +1,12 @@ # no need for shebang - this file is loaded from charts.d.plugin +# netdata +# real-time performance and health monitoring, done right! +# (C) 2016 Costa Tsaousis <costa@tsaousis.gr> +# GPL v3+ +# +# contributed by @paulfantom with PR #511 + # if this chart is called X.chart.sh, then all functions and global variables # must start with X_ hddtemp_host="localhost" @@ -13,7 +20,8 @@ hddtemp_priority=90000 # _check is called once, to find out if this chart should be enabled or not hddtemp_check() { - nc $hddtemp_host $hddtemp_port &>/dev/null && return 0 || return 1 + require_cmd nc || return 1 + run nc $hddtemp_host $hddtemp_port && return 0 || return 1 } # _create is called once, to create the charts diff --git a/charts.d/load_average.chart.sh b/charts.d/load_average.chart.sh index e6790d807..70d3aec7a 100755 --- a/charts.d/load_average.chart.sh +++ b/charts.d/load_average.chart.sh @@ -1,5 +1,11 @@ # no need for shebang - this file is loaded from charts.d.plugin +# netdata +# real-time performance and health monitoring, done right! +# (C) 2016 Costa Tsaousis <costa@tsaousis.gr> +# GPL v3+ +# + load_average_update_every=5 load_priority=100 diff --git a/charts.d/mem_apps.chart.sh b/charts.d/mem_apps.chart.sh index ab95b361c..3bc65fe24 100755 --- a/charts.d/mem_apps.chart.sh +++ b/charts.d/mem_apps.chart.sh @@ -1,5 +1,11 @@ # no need for shebang - this file is loaded from charts.d.plugin +# netdata +# real-time performance and health monitoring, done right! +# (C) 2016 Costa Tsaousis <costa@tsaousis.gr> +# GPL v3+ +# + mem_apps_apps= # these are required for computing memory in bytes and cpu in seconds @@ -15,7 +21,7 @@ mem_apps_check() { if [ -z "$mem_apps_apps" ] then - echo >&2 "$PROGRAM_NAME: mem_apps: not configured. Please set mem_apps_apps='command1 command2 ...' in $confd/mem_apps_apps.conf" + error "manual configuration required: please set mem_apps_apps='command1 command2 ...' in $confd/mem_apps_apps.conf" return 1 fi return 0 diff --git a/charts.d/mysql.chart.sh b/charts.d/mysql.chart.sh index 120fec66e..1363d01f4 100755 --- a/charts.d/mysql.chart.sh +++ b/charts.d/mysql.chart.sh @@ -1,5 +1,11 @@ # no need for shebang - this file is loaded from charts.d.plugin +# netdata +# real-time performance and health monitoring, done right! +# (C) 2016 Costa Tsaousis <costa@tsaousis.gr> +# GPL v3+ +# + # http://dev.mysql.com/doc/refman/5.0/en/server-status-variables.html # # https://dev.mysql.com/doc/refman/5.1/en/show-status.html @@ -17,9 +23,9 @@ mysql_get() { local oIFS="${IFS}" mysql_data=() IFS=$'\t'$'\n' - #arr=($("${@}" -e "SHOW GLOBAL STATUS WHERE value REGEXP '^[0-9]';" | egrep "^(Bytes|Slow_|Que|Handl|Table|Selec|Sort_|Creat|Conne|Abort|Binlo|Threa|Innod|Qcach|Key_|Open)" )) - #arr=($("${@}" -N -e "SHOW GLOBAL STATUS;" | egrep "^(Bytes|Slow_|Que|Handl|Table|Selec|Sort_|Creat|Conne|Abort|Binlo|Threa|Innod|Qcach|Key_|Open)[^ ]+\s[0-9]" )) - arr=($("${@}" -N -e "SHOW GLOBAL STATUS;" | egrep "^(Bytes|Slow_|Que|Handl|Table|Selec|Sort_|Creat|Conne|Abort|Binlo|Threa|Innod|Qcach|Key_|Open)[^[:space:]]+[[:space:]]+[0-9]+" )) + #arr=($(run "${@}" -e "SHOW GLOBAL STATUS WHERE value REGEXP '^[0-9]';" | egrep "^(Bytes|Slow_|Que|Handl|Table|Selec|Sort_|Creat|Conne|Abort|Binlo|Threa|Innod|Qcach|Key_|Open)" )) + #arr=($(run "${@}" -N -e "SHOW GLOBAL STATUS;" | egrep "^(Bytes|Slow_|Que|Handl|Table|Selec|Sort_|Creat|Conne|Abort|Binlo|Threa|Innod|Qcach|Key_|Open)[^ ]+\s[0-9]" )) + arr=($(run "${@}" -N -e "SHOW GLOBAL STATUS;" | egrep "^(Bytes|Slow_|Que|Handl|Table|Selec|Sort_|Creat|Conne|Abort|Binlo|Threa|Innod|Qcach|Key_|Open)[^[:space:]]+[[:space:]]+[0-9]+" )) IFS="${oIFS}" [ "${#arr[@]}" -lt 3 ] && return 1 @@ -49,7 +55,7 @@ mysql_check() { shift fi - [ -z "${mysql_cmd}" ] && mysql_cmd="$(which mysql)" + [ -z "${mysql_cmd}" ] && mysql_cmd="$(which mysql 2>/dev/null || command -v mysql 2>/dev/null)" if [ ${#mysql_opts[@]} -eq 0 ] then @@ -74,13 +80,13 @@ mysql_check() { [ -z "${mysql_cmds[$m]}" ] && mysql_cmds[$m]="$mysql_cmd" if [ -z "${mysql_cmds[$m]}" ] then - echo >&2 "$PROGRAM_NAME: mysql: cannot get mysql command for '$m'. Please set mysql_cmds[$m]='/path/to/mysql', in $confd/mysql.conf" + error "cannot get mysql command for '$m'. Please set mysql_cmds[$m]='/path/to/mysql', in $confd/mysql.conf" fi mysql_get "${mysql_cmds[$m]}" ${mysql_opts[$m]} if [ ! $? -eq 0 ] then - echo >&2 "$PROGRAM_NAME: mysql: cannot get global status for '$m'. Please set mysql_opts[$m]='options' to whatever needed to get connected to the mysql server, in $confd/mysql.conf" + error "cannot get global status for '$m'. Please set mysql_opts[$m]='options' to whatever needed to get connected to the mysql server, in $confd/mysql.conf" unset mysql_cmds[$m] unset mysql_opts[$m] unset mysql_ids[$m] @@ -97,7 +103,7 @@ mysql_check() { mysql_check tryroot "${@}" return $? else - echo >&2 "$PROGRAM_NAME: mysql: no mysql servers found. Please set mysql_opts[name]='options' to whatever needed to get connected to the mysql server, in $confd/mysql.conf" + error "no mysql servers found. Please set mysql_opts[name]='options' to whatever needed to get connected to the mysql server, in $confd/mysql.conf" return 1 fi fi @@ -318,7 +324,7 @@ mysql_update() { unset mysql_ids[$m] unset mysql_opts[$m] unset mysql_cmds[$m] - echo >&2 "$PROGRAM_NAME: mysql: failed to get values for '$m', disabling it." + error "failed to get values for '$m', disabling it." continue fi @@ -510,7 +516,7 @@ VALUESEOF fi done - [ ${#mysql_ids[@]} -eq 0 ] && echo >&2 "$PROGRAM_NAME: mysql: no mysql servers left active." && return 1 + [ ${#mysql_ids[@]} -eq 0 ] && error "no mysql servers left active." && return 1 return 0 } diff --git a/charts.d/nginx.chart.sh b/charts.d/nginx.chart.sh index a2a9b320f..0ae7d6307 100755 --- a/charts.d/nginx.chart.sh +++ b/charts.d/nginx.chart.sh @@ -1,5 +1,11 @@ # no need for shebang - this file is loaded from charts.d.plugin +# netdata +# real-time performance and health monitoring, done right! +# (C) 2016 Costa Tsaousis <costa@tsaousis.gr> +# GPL v3+ +# + # if this chart is called X.chart.sh, then all functions and global variables # must start with X_ @@ -20,7 +26,7 @@ nginx_reading=0 nginx_writing=0 nginx_waiting=0 nginx_get() { - nginx_response=($(curl -Ss ${nginx_curl_opts} "${nginx_url}")) + nginx_response=($(run curl -Ss ${nginx_curl_opts} "${nginx_url}")) [ $? -ne 0 -o "${#nginx_response[@]}" -eq 0 ] && return 1 if [ "${nginx_response[0]}" != "Active" \ @@ -34,7 +40,7 @@ nginx_get() { -o "${nginx_response[14]}" != "Waiting:" \ ] then - echo >&2 "nginx: Invalid response from nginx server: ${nginx_response[*]}" + error "Invalid response from nginx server: ${nginx_response[*]}" return 1 fi @@ -55,7 +61,7 @@ nginx_get() { -o -z "${nginx_waiting}" \ ] then - echo >&2 "nginx: empty values got from nginx server: ${nginx_response[*]}" + error "empty values got from nginx server: ${nginx_response[*]}" return 1 fi @@ -68,7 +74,7 @@ nginx_check() { nginx_get if [ $? -ne 0 ] then - echo >&2 "nginx: cannot find stub_status on URL '${nginx_url}'. Please set nginx_url='http://nginx.server/stub_status' in $confd/nginx.conf" + error "cannot find stub_status on URL '${nginx_url}'. Please set nginx_url='http://nginx.server/stub_status' in $confd/nginx.conf" return 1 fi diff --git a/charts.d/nut.chart.sh b/charts.d/nut.chart.sh index 3c8e1c9d0..e0b1b4cf9 100755 --- a/charts.d/nut.chart.sh +++ b/charts.d/nut.chart.sh @@ -1,5 +1,11 @@ # no need for shebang - this file is loaded from charts.d.plugin +# netdata +# real-time performance and health monitoring, done right! +# (C) 2016 Costa Tsaousis <costa@tsaousis.gr> +# GPL v3+ +# + # a space separated list of UPS names # if empty, the list returned by 'upsc -l' will be used nut_ups= @@ -15,11 +21,11 @@ nut_priority=90000 declare -A nut_ids=() nut_get_all() { - timeout $nut_timeout upsc -l + run -t $nut_timeout upsc -l } nut_get() { - timeout $nut_timeout upsc "$1" + run -t $nut_timeout upsc "$1" } nut_check() { @@ -42,12 +48,12 @@ nut_check() { nut_ids[$x]="$( fixid "$x" )" continue fi - echo >&2 "nut: ERROR: Cannot get information for NUT UPS '$x'." + error "cannot get information for NUT UPS '$x'." done if [ ${#nut_ids[@]} -eq 0 ] then - echo >&2 "nut: Please set nut_ups='ups_name' in $confd/nut.conf" + error "Cannot find UPSes - please set nut_ups='ups_name' in $confd/nut.conf" return 1 fi @@ -179,9 +185,9 @@ END { print \"SET temp = \" temp; print \"END\" }" - [ $? -ne 0 ] && unset nut_ids[$i] && echo >&2 "nut: failed to get values for '$i', disabling it." + [ $? -ne 0 ] && unset nut_ids[$i] && error "failed to get values for '$i', disabling it." done - [ ${#nut_ids[@]} -eq 0 ] && echo >&2 "nut: no UPSes left active." && return 1 + [ ${#nut_ids[@]} -eq 0 ] && error "no UPSes left active." && return 1 return 0 } diff --git a/charts.d/opensips.chart.sh b/charts.d/opensips.chart.sh index 779087e35..2a0249dae 100755 --- a/charts.d/opensips.chart.sh +++ b/charts.d/opensips.chart.sh @@ -1,5 +1,11 @@ # no need for shebang - this file is loaded from charts.d.plugin +# netdata +# real-time performance and health monitoring, done right! +# (C) 2016 Costa Tsaousis <costa@tsaousis.gr> +# GPL v3+ +# + opensips_opts="fifo get_statistics all" opensips_cmd= opensips_update_every=5 @@ -7,7 +13,7 @@ opensips_timeout=2 opensips_priority=80000 opensips_get_stats() { - timeout $opensips_timeout "$opensips_cmd" $opensips_opts |\ + run -t $opensips_timeout "$opensips_cmd" $opensips_opts |\ grep "^\(core\|dialog\|net\|registrar\|shmem\|siptrace\|sl\|tm\|uri\|usrloc\):[a-zA-Z0-9_-]\+[[:space:]]*[=:]\+[[:space:]]*[0-9]\+[[:space:]]*$" |\ sed \ -e "s|[[:space:]]*[=:]\+[[:space:]]*\([0-9]\+\)[[:space:]]*$|=\1|g" \ @@ -31,7 +37,7 @@ opensips_check() { local x="$(opensips_get_stats | grep "^opensips_core_")" if [ ! $? -eq 0 -o -z "$x" ] then - echo >&2 "$PROGRAM_NAME: opensips: cannot get global status. Please set opensips_opts='options' whatever needed to get connected to opensips server, in $confd/opensips.conf" + error "cannot get global status. Please set opensips_opts='options' whatever needed to get connected to opensips server, in $confd/opensips.conf" return 1 fi @@ -214,7 +220,7 @@ opensips_update() { eval "local $(opensips_get_stats)" [ $? -ne 0 ] && return 1 - [ $opensips_command_failed -eq 1 ] && echo >&2 "$PROGRAM_NAME: opensips: failed to get values, disabling." && return 1 + [ $opensips_command_failed -eq 1 ] && error "failed to get values, disabling." && return 1 # write the result of the work. cat <<VALUESEOF diff --git a/charts.d/phpfpm.chart.sh b/charts.d/phpfpm.chart.sh index 976ce91b1..a5ee0ad56 100755 --- a/charts.d/phpfpm.chart.sh +++ b/charts.d/phpfpm.chart.sh @@ -1,9 +1,13 @@ # no need for shebang - this file is loaded from charts.d.plugin -# if this chart is called X.chart.sh, then all functions and global variables -# must start with X_ - -# first, you need open php-fpm status in php-fpm.conf +# netdata +# real-time performance and health monitoring, done right! +# (C) 2016 Costa Tsaousis <costa@tsaousis.gr> +# GPL v3+ +# +# Contributed by @safeie with PR #276 + +# first, you need open php-fpm status in php-fpm.conf # second, you need add status location in nginx.conf # you can see, https://easyengine.io/tutorials/php/fpm-status-page/ @@ -32,7 +36,7 @@ phpfpm_slow_requests=0 phpfpm_get() { local opts="${1}" url="${2}" - phpfpm_response=($(curl -Ss ${opts} "${url}")) + phpfpm_response=($(run curl -Ss ${opts} "${url}")) [ $? -ne 0 -o "${#phpfpm_response[@]}" -eq 0 ] && return 1 if [[ "${phpfpm_response[0]}" != "pool:" \ @@ -46,7 +50,7 @@ phpfpm_get() { || "${phpfpm_response[32]}" != "total" \ ]] then - echo >&2 "phpfpm: invalid response from phpfpm status server: ${phpfpm_response[*]}" + error "invalid response from phpfpm status server: ${phpfpm_response[*]}" return 1 fi @@ -83,7 +87,7 @@ phpfpm_get() { || -z "${phpfpm_max_children_reached}" \ ]] then - echo >&2 "phpfpm: empty values got from phpfpm status server: ${phpfpm_response[*]}" + error "empty values got from phpfpm status server: ${phpfpm_response[*]}" return 1 fi @@ -101,14 +105,14 @@ phpfpm_check() { do phpfpm_get "${phpfpm_curl_opts[$m]}" "${phpfpm_urls[$m]}" if [ $? -ne 0 ]; then - echo >&2 "phpfpm: cannot find status on URL '${phpfpm_url[$m]}'. Please set phpfpm_urls[$m]='http://localhost/status' in $confd/phpfpm.conf" + error "cannot find status on URL '${phpfpm_url[$m]}'. Please set phpfpm_urls[$m]='http://localhost/status' in $confd/phpfpm.conf" unset phpfpm_urls[$m] continue fi done if [ ${#phpfpm_urls[@]} -eq 0 ]; then - echo >&2 "phpfpm: no phpfpm servers found. Please set phpfpm_urls[name]='url' to whatever needed to get status to the phpfpm server, in $confd/phpfpm.conf" + error "no phpfpm servers found. Please set phpfpm_urls[name]='url' to whatever needed to get status to the phpfpm server, in $confd/phpfpm.conf" return 1 fi diff --git a/charts.d/postfix.chart.sh b/charts.d/postfix.chart.sh index 7f07a1868..85604fcbe 100755 --- a/charts.d/postfix.chart.sh +++ b/charts.d/postfix.chart.sh @@ -1,5 +1,11 @@ # no need for shebang - this file is loaded from charts.d.plugin +# netdata +# real-time performance and health monitoring, done right! +# (C) 2016 Costa Tsaousis <costa@tsaousis.gr> +# GPL v3+ +# + # the postqueue command # if empty, it will use the one found in the system path postfix_postqueue= @@ -17,24 +23,12 @@ postfix_check() { # try to find the postqueue executable if [ -z "$postfix_postqueue" -o ! -x "$postfix_postqueue" ] then - postfix_postqueue="`which postqueue 2>/dev/null`" - if [ -z "$postfix_postqueue" -o ! -x "$postfix_postqueue" ] - then - local d= - for d in /sbin /usr/sbin /usr/local/sbin - do - if [ -x "$d/postqueue" ] - then - postfix_postqueue="$d/postqueue" - break - fi - done - fi + postfix_postqueue="$(which postqueue 2>/dev/null || command -v postqueue 2>/dev/null)" fi if [ -z "$postfix_postqueue" -o ! -x "$postfix_postqueue" ] then - echo >&2 "$PROGRAM_NAME: postfix: cannot find postqueue. Please set 'postfix_postqueue=/path/to/postqueue' in $confd/postfix.conf" + error "cannot find postqueue. Please set 'postfix_postqueue=/path/to/postqueue' in $confd/postfix.conf" return 1 fi @@ -73,10 +67,10 @@ postfix_update() { postfix_q_emails=0 postfix_q_size=0 - eval "`$postfix_postqueue -p |\ + eval "$(run $postfix_postqueue -p |\ grep "^--" |\ sed -e "s/-- \([0-9]\+\) Kbytes in \([0-9]\+\) Requests.$/local postfix_q_size=\1\nlocal postfix_q_emails=\2/g" |\ - egrep "^local postfix_q_(emails|size)=[0-9]+$"`" + egrep "^local postfix_q_(emails|size)=[0-9]+$")" # write the result of the work. cat <<VALUESEOF diff --git a/charts.d/sensors.chart.sh b/charts.d/sensors.chart.sh index 9652f896a..125c925da 100755 --- a/charts.d/sensors.chart.sh +++ b/charts.d/sensors.chart.sh @@ -1,5 +1,11 @@ # no need for shebang - this file is loaded from charts.d.plugin +# netdata +# real-time performance and health monitoring, done right! +# (C) 2016 Costa Tsaousis <costa@tsaousis.gr> +# GPL v3+ +# + # sensors docs # https://www.kernel.org/doc/Documentation/hwmon/sysfs-interface @@ -43,7 +49,7 @@ sensors_check() { # - 0 to enable the chart # - 1 to disable the chart - [ -z "$( sensors_find_all_files $sensors_sys_dir )" ] && echo >&2 "$PROGRAM_NAME: sensors: no sensors found in '$sensors_sys_dir'." && return 1 + [ -z "$( sensors_find_all_files $sensors_sys_dir )" ] && error "no sensors found in '$sensors_sys_dir'." && return 1 return 0 } @@ -64,7 +70,7 @@ sensors_check_files() { [ $v -ne 0 ] && echo "$f" && continue excluded= - echo >&2 "$PROGRAM_NAME: sensors: $f gives zero values" + error "$f gives zero values" done } @@ -83,7 +89,7 @@ sensors_check_temp_type() { v=$(( v + 1 - 1 )) [ $v -ne 0 ] && echo "$f" && continue - echo >&2 "$PROGRAM_NAME: sensors: $f is disabled" + error "$f is disabled" done } @@ -121,7 +127,7 @@ sensors_create() { id="$( fixid "$device.$subsystem.$dir" )" - echo >&2 "charts.d: sensors: on path='$path', dir='$dir', device='$device', subsystem='$subsystem', id='$id', name='$name'" + debug "path='$path', dir='$dir', device='$device', subsystem='$subsystem', id='$id', name='$name'" for mode in temperature voltage fans power current energy humidity do @@ -221,7 +227,6 @@ sensors_create() { done [ $sensors_source_update -eq 1 ] && echo >>$TMP_DIR/sensors.sh "}" - # cat >&2 $TMP_DIR/sensors.sh # ok, load the function sensors_update() we created [ $sensors_source_update -eq 1 ] && . $TMP_DIR/sensors.sh diff --git a/charts.d/squid.chart.sh b/charts.d/squid.chart.sh index 3e72ba6df..2c19c35d5 100755 --- a/charts.d/squid.chart.sh +++ b/charts.d/squid.chart.sh @@ -1,5 +1,11 @@ # no need for shebang - this file is loaded from charts.d.plugin +# netdata +# real-time performance and health monitoring, done right! +# (C) 2016 Costa Tsaousis <costa@tsaousis.gr> +# GPL v3+ +# + squid_host= squid_port= squid_url= @@ -9,7 +15,7 @@ squid_priority=60000 squid_get_stats_internal() { local host="$1" port="$2" url="$3" - squidclient -h $host -p $port $url + run squidclient -h $host -p $port $url } squid_get_stats() { @@ -29,13 +35,13 @@ squid_autodetect() { squid_host="$host" squid_port="$port" squid_url="$url" - echo >&2 "squid: found squid at '$host:$port' with url '$url'" + debug "found squid at '$host:$port' with url '$url'" return 0 fi done done - echo >&2 "squid: cannot find squid running in localhost. Please set squid_url='url' and squid_host='IP' and squid_port='PORT' in $confd/squid.conf" + error "cannot find squid running in localhost. Please set squid_url='url' and squid_host='IP' and squid_port='PORT' in $confd/squid.conf" return 1 } @@ -53,7 +59,7 @@ squid_check() { local x="$(squid_get_stats | grep client_http.requests)" if [ ! $? -eq 0 -o -z "$x" ] then - echo >&2 "squid: cannot fetch URL '$squid_url' by connecting to $squid_host:$squid_port. Please set squid_url='url' and squid_host='host' and squid_port='port' in $confd/squid.conf" + error "cannot fetch URL '$squid_url' by connecting to $squid_host:$squid_port. Please set squid_url='url' and squid_host='host' and squid_port='port' in $confd/squid.conf" return 1 fi diff --git a/charts.d/tomcat.chart.sh b/charts.d/tomcat.chart.sh index cc6baea1a..44a92c86e 100755 --- a/charts.d/tomcat.chart.sh +++ b/charts.d/tomcat.chart.sh @@ -1,5 +1,12 @@ # no need for shebang - this file is loaded from charts.d.plugin +# netdata +# real-time performance and health monitoring, done right! +# (C) 2016 Costa Tsaousis <costa@tsaousis.gr> +# GPL v3+ +# +# Contributed by @jgeromero with PR #277 + # Description: Tomcat netdata charts.d plugin # Author: Jorge Romero @@ -34,13 +41,13 @@ tomcat_check() { # check if url, username, passwords are set if [ -z "${tomcat_url}" ]; then - echo >&2 "tomcat url is unset or set to the empty string" + error "tomcat url is unset or set to the empty string" return 1 fi if [ -z "${tomcat_user}" ]; then # check backwards compatibility if [ -z "${tomcatUser}" ]; then - echo >&2 "tomcat user is unset or set to the empty string" + error "tomcat user is unset or set to the empty string" return 1 else tomcat_user="${tomcatUser}" @@ -49,7 +56,7 @@ tomcat_check() { if [ -z "${tomcat_password}" ]; then # check backwards compatibility if [ -z "${tomcatPassword}" ]; then - echo >&2 "tomcat password is unset or set to the empty string" + error "tomcat password is unset or set to the empty string" return 1 else tomcat_password="${tomcatPassword}" @@ -60,8 +67,7 @@ tomcat_check() { tomcat_get if [ $? -ne 0 ] then - echo >&2 "tomcat: couldn't get to status page on URL '${tomcat_url}'."\ - "Please make sure tomcat url, username and password are correct." + error "cannot get to status page on URL '${tomcat_url}'. Please make sure tomcat url, username and password are correct." return 1 fi @@ -75,8 +81,8 @@ tomcat_check() { tomcat_get() { # collect tomcat values tomcat_port="$(IFS=/ read -ra a <<< "$tomcat_url"; hostport=${a[2]}; echo "${hostport#*:}")" - mapfile -t lines < <(curl -u "$tomcat_user":"$tomcat_password" -Ss ${tomcat_curl_opts} "$tomcat_url" |\ - xmlstarlet sel \ + mapfile -t lines < <(run curl -u "$tomcat_user":"$tomcat_password" -Ss ${tomcat_curl_opts} "$tomcat_url" |\ + run xmlstarlet sel \ -t -m "/status/jvm/memory" -v @free \ -n -m "/status/connector[@name='\"http-bio-$tomcat_port\"']/threadInfo" -v @currentThreadCount \ -n -v @currentThreadsBusy \ @@ -3,7 +3,7 @@ scriptversion=2012-10-14.11; # UTC -# Copyright (C) 1999-2013 Free Software Foundation, Inc. +# Copyright (C) 1999-2014 Free Software Foundation, Inc. # Written by Tom Tromey <tromey@cygnus.com>. # # This program is free software; you can redistribute it and/or modify diff --git a/conf.d/Makefile.am b/conf.d/Makefile.am index 066744cab..b725e249e 100644 --- a/conf.d/Makefile.am +++ b/conf.d/Makefile.am @@ -4,61 +4,110 @@ MAINTAINERCLEANFILES= $(srcdir)/Makefile.in dist_config_DATA = \ - apps_groups.conf \ - charts.d.conf \ - python.d.conf \ - health_alarm_notify.conf \ - health_email_recipients.conf \ - $(NULL) - -chartsconfigdir=$(configdir)/charts.d -dist_chartsconfig_DATA = \ - $(NULL) + apps_groups.conf \ + charts.d.conf \ + fping.conf \ + node.d.conf \ + python.d.conf \ + health_alarm_notify.conf \ + health_email_recipients.conf \ + $(NULL) nodeconfigdir=$(configdir)/node.d dist_nodeconfig_DATA = \ - $(NULL) + node.d/README.md \ + node.d/named.conf.md \ + node.d/sma_webbox.conf.md \ + node.d/snmp.conf.md \ + $(NULL) pythonconfigdir=$(configdir)/python.d dist_pythonconfig_DATA = \ - 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 \ - python.d/nginx_log.conf \ - python.d/phpfpm.conf \ - python.d/postfix.conf \ - python.d/redis.conf \ - python.d/retroshare.conf \ - python.d/sensors.conf \ - python.d/squid.conf \ - python.d/tomcat.conf \ - $(NULL) + python.d/apache.conf \ + python.d/apache_cache.conf \ + python.d/bind_rndc.conf \ + python.d/cpufreq.conf \ + python.d/dovecot.conf \ + python.d/elasticsearch.conf \ + python.d/example.conf \ + python.d/exim.conf \ + python.d/fail2ban.conf \ + python.d/freeradius.conf \ + python.d/gunicorn_log.conf \ + python.d/haproxy.conf \ + python.d/hddtemp.conf \ + python.d/ipfs.conf \ + python.d/isc_dhcpd.conf \ + python.d/mdstat.conf \ + python.d/memcached.conf \ + python.d/mysql.conf \ + python.d/nginx.conf \ + python.d/nginx_log.conf \ + python.d/ovpn_status_log.conf \ + python.d/phpfpm.conf \ + python.d/postfix.conf \ + python.d/postgres.conf \ + python.d/redis.conf \ + python.d/retroshare.conf \ + python.d/sensors.conf \ + python.d/squid.conf \ + python.d/tomcat.conf \ + python.d/varnish.conf \ + $(NULL) healthconfigdir=$(configdir)/health.d dist_healthconfig_DATA = \ - health.d/apache.conf \ - health.d/cpu.conf \ - health.d/disks.conf \ - health.d/entropy.conf \ - health.d/tcp_resets.conf \ - health.d/memcached.conf \ - health.d/mysql.conf \ - health.d/named.conf \ - health.d/net.conf \ - health.d/nginx.conf \ - health.d/qos.conf \ - health.d/ram.conf \ - health.d/redis.conf \ - health.d/retroshare.conf \ - health.d/softnet.conf \ - health.d/swap.conf \ - health.d/squid.conf \ - $(NULL) + health.d/apache.conf \ + health.d/backend.conf \ + health.d/bind_rndc.conf \ + health.d/cpu.conf \ + health.d/disks.conf \ + health.d/elasticsearch.conf \ + health.d/entropy.conf \ + health.d/haproxy.conf \ + health.d/ipc.conf \ + health.d/ipfs.conf \ + health.d/isc_dhcpd.conf \ + health.d/mdstat.conf \ + health.d/memcached.conf \ + health.d/memory.conf \ + health.d/mysql.conf \ + health.d/named.conf \ + health.d/net.conf \ + health.d/netfilter.conf \ + health.d/nginx.conf \ + health.d/postgres.conf \ + health.d/qos.conf \ + health.d/ram.conf \ + health.d/redis.conf \ + health.d/retroshare.conf \ + health.d/softnet.conf \ + health.d/squid.conf \ + health.d/swap.conf \ + health.d/tcp_resets.conf \ + health.d/udp_errors.conf \ + health.d/varnish.conf \ + $(NULL) + +chartsconfigdir=$(configdir)/charts.d +dist_chartsconfig_DATA = \ + charts.d/apache.conf \ + charts.d/apcupsd.conf \ + charts.d/cpufreq.conf \ + charts.d/exim.conf \ + charts.d/load_average.conf \ + charts.d/mysql.conf \ + charts.d/nut.conf \ + charts.d/phpfpm.conf \ + charts.d/sensors.conf \ + charts.d/tomcat.conf \ + charts.d/ap.conf \ + charts.d/cpu_apps.conf \ + charts.d/example.conf \ + charts.d/hddtemp.conf \ + charts.d/mem_apps.conf \ + charts.d/nginx.conf \ + charts.d/opensips.conf \ + charts.d/postfix.conf \ + charts.d/squid.conf \ + $(NULL) diff --git a/conf.d/Makefile.in b/conf.d/Makefile.in index 823713bfa..344f1c416 100644 --- a/conf.d/Makefile.in +++ b/conf.d/Makefile.in @@ -1,7 +1,7 @@ -# Makefile.in generated by automake 1.14.1 from Makefile.am. +# Makefile.in generated by automake 1.15 from Makefile.am. # @configure_input@ -# Copyright (C) 1994-2013 Free Software Foundation, Inc. +# Copyright (C) 1994-2014 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -15,7 +15,17 @@ @SET_MAKE@ VPATH = @srcdir@ -am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ @@ -79,20 +89,21 @@ POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = conf.d -DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ - $(dist_chartsconfig_DATA) $(dist_config_DATA) \ - $(dist_healthconfig_DATA) $(dist_nodeconfig_DATA) \ - $(dist_pythonconfig_DATA) ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_c___atomic.m4 \ $(top_srcdir)/m4/ax_c__generic.m4 \ $(top_srcdir)/m4/ax_c_mallinfo.m4 \ $(top_srcdir)/m4/ax_c_mallopt.m4 \ $(top_srcdir)/m4/ax_check_compile_flag.m4 \ + $(top_srcdir)/m4/ax_gcc_func_attribute.m4 \ $(top_srcdir)/m4/ax_pthread.m4 $(top_srcdir)/m4/jemalloc.m4 \ $(top_srcdir)/m4/tcmalloc.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(dist_chartsconfig_DATA) \ + $(dist_config_DATA) $(dist_healthconfig_DATA) \ + $(dist_nodeconfig_DATA) $(dist_pythonconfig_DATA) \ + $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = @@ -150,6 +161,7 @@ DATA = $(dist_chartsconfig_DATA) $(dist_config_DATA) \ $(dist_healthconfig_DATA) $(dist_nodeconfig_DATA) \ $(dist_pythonconfig_DATA) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +am__DIST_COMMON = $(srcdir)/Makefile.in DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ @@ -293,64 +305,113 @@ webdir = @webdir@ # MAINTAINERCLEANFILES = $(srcdir)/Makefile.in dist_config_DATA = \ - apps_groups.conf \ - charts.d.conf \ - python.d.conf \ - health_alarm_notify.conf \ - health_email_recipients.conf \ - $(NULL) - -chartsconfigdir = $(configdir)/charts.d -dist_chartsconfig_DATA = \ - $(NULL) + apps_groups.conf \ + charts.d.conf \ + fping.conf \ + node.d.conf \ + python.d.conf \ + health_alarm_notify.conf \ + health_email_recipients.conf \ + $(NULL) nodeconfigdir = $(configdir)/node.d dist_nodeconfig_DATA = \ - $(NULL) + node.d/README.md \ + node.d/named.conf.md \ + node.d/sma_webbox.conf.md \ + node.d/snmp.conf.md \ + $(NULL) pythonconfigdir = $(configdir)/python.d dist_pythonconfig_DATA = \ - 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 \ - python.d/nginx_log.conf \ - python.d/phpfpm.conf \ - python.d/postfix.conf \ - python.d/redis.conf \ - python.d/retroshare.conf \ - python.d/sensors.conf \ - python.d/squid.conf \ - python.d/tomcat.conf \ - $(NULL) + python.d/apache.conf \ + python.d/apache_cache.conf \ + python.d/bind_rndc.conf \ + python.d/cpufreq.conf \ + python.d/dovecot.conf \ + python.d/elasticsearch.conf \ + python.d/example.conf \ + python.d/exim.conf \ + python.d/fail2ban.conf \ + python.d/freeradius.conf \ + python.d/gunicorn_log.conf \ + python.d/haproxy.conf \ + python.d/hddtemp.conf \ + python.d/ipfs.conf \ + python.d/isc_dhcpd.conf \ + python.d/mdstat.conf \ + python.d/memcached.conf \ + python.d/mysql.conf \ + python.d/nginx.conf \ + python.d/nginx_log.conf \ + python.d/ovpn_status_log.conf \ + python.d/phpfpm.conf \ + python.d/postfix.conf \ + python.d/postgres.conf \ + python.d/redis.conf \ + python.d/retroshare.conf \ + python.d/sensors.conf \ + python.d/squid.conf \ + python.d/tomcat.conf \ + python.d/varnish.conf \ + $(NULL) healthconfigdir = $(configdir)/health.d dist_healthconfig_DATA = \ - health.d/apache.conf \ - health.d/cpu.conf \ - health.d/disks.conf \ - health.d/entropy.conf \ - health.d/tcp_resets.conf \ - health.d/memcached.conf \ - health.d/mysql.conf \ - health.d/named.conf \ - health.d/net.conf \ - health.d/nginx.conf \ - health.d/qos.conf \ - health.d/ram.conf \ - health.d/redis.conf \ - health.d/retroshare.conf \ - health.d/softnet.conf \ - health.d/swap.conf \ - health.d/squid.conf \ - $(NULL) + health.d/apache.conf \ + health.d/backend.conf \ + health.d/bind_rndc.conf \ + health.d/cpu.conf \ + health.d/disks.conf \ + health.d/elasticsearch.conf \ + health.d/entropy.conf \ + health.d/haproxy.conf \ + health.d/ipc.conf \ + health.d/ipfs.conf \ + health.d/isc_dhcpd.conf \ + health.d/mdstat.conf \ + health.d/memcached.conf \ + health.d/memory.conf \ + health.d/mysql.conf \ + health.d/named.conf \ + health.d/net.conf \ + health.d/netfilter.conf \ + health.d/nginx.conf \ + health.d/postgres.conf \ + health.d/qos.conf \ + health.d/ram.conf \ + health.d/redis.conf \ + health.d/retroshare.conf \ + health.d/softnet.conf \ + health.d/squid.conf \ + health.d/swap.conf \ + health.d/tcp_resets.conf \ + health.d/udp_errors.conf \ + health.d/varnish.conf \ + $(NULL) + +chartsconfigdir = $(configdir)/charts.d +dist_chartsconfig_DATA = \ + charts.d/apache.conf \ + charts.d/apcupsd.conf \ + charts.d/cpufreq.conf \ + charts.d/exim.conf \ + charts.d/load_average.conf \ + charts.d/mysql.conf \ + charts.d/nut.conf \ + charts.d/phpfpm.conf \ + charts.d/sensors.conf \ + charts.d/tomcat.conf \ + charts.d/ap.conf \ + charts.d/cpu_apps.conf \ + charts.d/example.conf \ + charts.d/hddtemp.conf \ + charts.d/mem_apps.conf \ + charts.d/nginx.conf \ + charts.d/opensips.conf \ + charts.d/postfix.conf \ + charts.d/squid.conf \ + $(NULL) all: all-am @@ -367,7 +428,6 @@ $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__confi echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu conf.d/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu conf.d/Makefile -.PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ @@ -653,6 +713,8 @@ uninstall-am: uninstall-dist_chartsconfigDATA \ uninstall-dist_configDATA uninstall-dist_healthconfigDATA \ uninstall-dist_nodeconfigDATA uninstall-dist_pythonconfigDATA +.PRECIOUS: Makefile + # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. diff --git a/conf.d/apps_groups.conf b/conf.d/apps_groups.conf index 57357a873..e2836877c 100644 --- a/conf.d/apps_groups.conf +++ b/conf.d/apps_groups.conf @@ -3,7 +3,7 @@ # # The apps.plugin displays charts with information about the processes running. # This config allows grouping processes together, so that several processes -# will be reported together. +# will be reported as one. # # Only groups in this file are reported. All other processes will be reported # as 'other'. @@ -13,32 +13,43 @@ # # The format is: # -# group_name: process1 process2 process3 ... +# group: process1 process2 process3 ... # -# The process names are the same to the ones returned by: ps -e -# or /proc/PID/stat +# Each group can be given multiple times, to add more processes to it. +# +# The process names are the ones returned by: +# +# - ps -e or /proc/PID/stat +# - in case of substring mode (see below): /proc/PID/cmdline # # To add process names with spaces, enclose them in quotes (single or double) -# example: 'Plex Media Serv' "my other process" +# example: 'Plex Media Serv' "my other process". # # Wildcard support: -# You can add an asterisk (*) at the beginning and/or the end of a process name: -# *name suffix mode: will search for processes ending with 'name' (/proc/PID/stat) -# name* prefix mode: will search for processes beginning with 'name' (/proc/PID/stat) -# *name* substring mode: will search for 'name' in the whole command line (/proc/PID/cmdline) +# You can add an asterisk (*) at the beginning and/or the end of a process: +# +# *name suffix mode: will search for processes ending with 'name' +# (/proc/PID/stat) +# +# name* prefix mode: will search for processes beginning with 'name' +# (/proc/PID/stat) +# +# *name* substring mode: will search for 'name' in the whole command line +# (/proc/PID/cmdline) # # If you enter even just one *name* (substring), apps.plugin will process # /proc/PID/cmdline for all processes, just once (when they are first seen). # -# To add process names with single quotes, enclose them in double quotes +# To add processes with single quotes, enclose them in double quotes # example: "process with this ' single quote" # -# To add process names with double quotes, enclose them in single quotes: +# To add processes with double quotes, enclose them in single quotes: # example: 'process with this " double quote' # -# If a group name starts with a -, the dimension will be hidden (cpu chart only) +# If a group or process name starts with a -, the dimension will be hidden +# (cpu chart only). # -# If any process name starts with a +, debugging will be enabled for it +# If a process starts with a +, debugging will be enabled for it # (debugging produces a lot of output - do not enable it in production systems) # # You can add any number of groups you like. Only the ones found running will @@ -86,8 +97,8 @@ unicorn: *unicorn* # ----------------------------------------------------------------------------- # database servers -sql: mysqld* mariad* postgres* -nosql: mongod redis* +sql: mysqld* mariad* postgres* oracle_* ora_* +nosql: mongod redis* memcached # ----------------------------------------------------------------------------- # email servers @@ -106,22 +117,26 @@ wifi: hostapd wpa_supplicant camo: *camo* balancer: ipvs_* haproxy -ha: corosync hs_logd ha_logd stonithd +ha: corosync hs_logd ha_logd stonithd pacemakerd lrmd crmd # ----------------------------------------------------------------------------- # telephony pbx: asterisk safe_asterisk *vicidial* sip: opensips* stund -murmur: murmurd -xmpp: *vines* *prosody* + +# ----------------------------------------------------------------------------- +# chat + +chat: irssi *vines* *prosody* murmurd # ----------------------------------------------------------------------------- # monitoring -logs: ulogd* syslog* rsyslog* logrotate +logs: ulogd* syslog* rsyslog* logrotate systemd-journald nms: snmpd vnstatd smokeping zabbix* monit munin* mon openhpid watchdog tailon nrpe splunk: splunkd +azure: mdsd *waagent* *omiserver* *omiagent* hv_kvp_daemon hv_vss_daemon # ----------------------------------------------------------------------------- # file systems and file servers @@ -130,6 +145,7 @@ samba: smbd nmbd winbindd nfs: rpcbind rpc.* nfs* zfs: spl_* z_* txg_* zil_* arc_* l2arc* btrfs: btrfs* +iscsi: iscsid iscsi_eh # ----------------------------------------------------------------------------- # containers & virtual machines @@ -150,7 +166,7 @@ print: cups* lpd lpq # ----------------------------------------------------------------------------- # time servers and clients -time: ntp* +time: ntp* systemd-timesyncd # ----------------------------------------------------------------------------- # dhcp servers and clients @@ -165,7 +181,8 @@ named: named rncd dig # ----------------------------------------------------------------------------- # installation / compilation / debugging -build: cc1 cc1plus as gcc* ld make automake autoconf autoreconf git valgrind* +build: cc1 cc1plus as gcc* cppcheck ld make cmake automake autoconf autoreconf +build: git gdb valgrind* # ----------------------------------------------------------------------------- # antivirus @@ -175,7 +192,7 @@ antivirus: clam* *clam # ----------------------------------------------------------------------------- # torrent clients -torrents: *deluge* transmission* *SickBeard* +torrents: *deluge* transmission* *SickBeard* *CouchPotato* *rtorrent* # ----------------------------------------------------------------------------- # backup servers and clients @@ -185,7 +202,7 @@ backup: rsync bacula* # ----------------------------------------------------------------------------- # cron -cron: cron atd anacron +cron: cron* atd anacron systemd-cron* # ----------------------------------------------------------------------------- # UPS @@ -193,21 +210,29 @@ cron: cron atd anacron ups: upsmon upsd */nut/* # ----------------------------------------------------------------------------- -# Kernel / System +# media players, servers, clients -system: systemd* udisks* udevd* *udevd connmand ipv6_addrconf dbus-* inetd xinetd mdadm -kernel: kthreadd kauditd lockd khelper kdevtmpfs khungtaskd rpciod fsnotify_mark kthrotld iscsi_eh deferwq -ksmd: ksmd +media: mplayer vlc xine mediatomb omxplayer* kodi* xbmc* mediacenter eventlircd +media: mpd minidlnad mt-daapd avahi* Plex* # ----------------------------------------------------------------------------- -# media players, servers, clients +# X -media: mplayer vlc xine mediatomb omxplayer* kodi* xbmc* mediacenter eventlircd mpd minidlnad mt-daapd avahi* +X: X Xorg xinit lightdm xdm pulseaudio gkrellm xfwm4 xfdesktop xfce* Thunar +X: xfsettingsd xfconfd gnome-* gdm gconf* dconf* xfconf* *gvfs gvfs* kdm slim +X: evolution-* firefox chromium opera epiphany WebKit* # ----------------------------------------------------------------------------- -# X +# Kernel / System + +ksmd: ksmd + +system: systemd* udisks* udevd* *udevd connmand ipv6_addrconf dbus-* rtkit* +system: inetd xinetd mdadm polkitd acpid uuidd packagekitd upowerd colord +system: accounts-daemon rngd haveged -X: X lightdm xdm pulseaudio gkrellm xfwm4 xfdesktop xfce* Thunar xfsettingsd xfconfd gnome-* gdm gconfd-2 *gvfsd gvfsd* kdm slim +kernel: kthreadd kauditd lockd khelper kdevtmpfs khungtaskd rpciod +kernel: fsnotify_mark kthrotld deferwq scsi_* # ----------------------------------------------------------------------------- # other application servers @@ -215,5 +240,4 @@ X: X lightdm xdm pulseaudio gkrellm xfwm4 xfdesktop xfce* Thunar xfsettingsd xfc crsproxy: crsproxy sidekiq: *sidekiq* java: java -chat: irssi ipfs: ipfs diff --git a/conf.d/charts.d/ap.conf b/conf.d/charts.d/ap.conf new file mode 100644 index 000000000..88a447eb9 --- /dev/null +++ b/conf.d/charts.d/ap.conf @@ -0,0 +1,19 @@ +# no need for shebang - this file is loaded from charts.d.plugin + +# netdata +# real-time performance and health monitoring, done right! +# (C) 2016 Costa Tsaousis <costa@tsaousis.gr> +# GPL v3+ + +# nothing fancy to configure. +# this module will run +# iw dev - to find wireless devices in AP mode +# iw ${dev} station dump - to get connected clients +# based on the above, it generates several charts + +# the data collection frequency +# if unset, will inherit the netdata update frequency +#ap_update_every= + +# the charts priority on the dashboard +#ap_priority=6900 diff --git a/conf.d/charts.d/apache.conf b/conf.d/charts.d/apache.conf new file mode 100644 index 000000000..b82c2a7fb --- /dev/null +++ b/conf.d/charts.d/apache.conf @@ -0,0 +1,26 @@ +# no need for shebang - this file is loaded from charts.d.plugin + +# netdata +# real-time performance and health monitoring, done right! +# (C) 2016 Costa Tsaousis <costa@tsaousis.gr> +# GPL v3+ + +# THIS PLUGIN IS DEPRECATED +# USE THE PYTHON.D ONE + +# the URL to download apache status info +#apache_url="http://127.0.0.1:80/server-status?auto" +#apache_curl_opts= + +# convert apache floating point values +# to integer using this multiplier +# this only affects precision - the values +# will be in the proper units +#apache_decimal_detail=1000000 + +# the data collection frequency +# if unset, will inherit the netdata update frequency +#apache_update_every= + +# the charts priority on the dashboard +#apache_priority=60000 diff --git a/conf.d/charts.d/apcupsd.conf b/conf.d/charts.d/apcupsd.conf new file mode 100644 index 000000000..f8bf7ed60 --- /dev/null +++ b/conf.d/charts.d/apcupsd.conf @@ -0,0 +1,19 @@ +# no need for shebang - this file is loaded from charts.d.plugin + +# netdata +# real-time performance and health monitoring, done right! +# (C) 2016 Costa Tsaousis <costa@tsaousis.gr> +# GPL v3+ + +#apcupsd_ip=127.0.0.1 +#apcupsd_port=3551 + +# how long to wait for apcupsd to respond +#apcupsd_timeout=3 + +# the data collection frequency +# if unset, will inherit the netdata update frequency +#apcupsd_update_every=10 + +# the charts priority on the dashboard +#apcupsd_priority=90000 diff --git a/conf.d/charts.d/cpu_apps.conf b/conf.d/charts.d/cpu_apps.conf new file mode 100644 index 000000000..46d70362e --- /dev/null +++ b/conf.d/charts.d/cpu_apps.conf @@ -0,0 +1,15 @@ +# no need for shebang - this file is loaded from charts.d.plugin + +# netdata +# real-time performance and health monitoring, done right! +# (C) 2016 Costa Tsaousis <costa@tsaousis.gr> +# GPL v3+ + +# THIS PLUGIN IS DEPRECATED +# app.plugin can do better + +#cpu_apps_apps= + +# the data collection frequency +# if unset, will inherit the netdata update frequency +#cpu_apps_update_every=2 diff --git a/conf.d/charts.d/cpufreq.conf b/conf.d/charts.d/cpufreq.conf new file mode 100644 index 000000000..4f26562ec --- /dev/null +++ b/conf.d/charts.d/cpufreq.conf @@ -0,0 +1,20 @@ +# no need for shebang - this file is loaded from charts.d.plugin + +# netdata +# real-time performance and health monitoring, done right! +# (C) 2016 Costa Tsaousis <costa@tsaousis.gr> +# GPL v3+ + +# THIS PLUGIN IS DEPRECATED +# USE THE PYTHON.D ONE + +#cpufreq_sys_dir="/sys/devices" +#cpufreq_sys_depth=10 +#cpufreq_source_update=1 + +# the data collection frequency +# if unset, will inherit the netdata update frequency +#cpufreq_update_every= + +# the charts priority on the dashboard +#cpufreq_priority=10000 diff --git a/conf.d/charts.d/example.conf b/conf.d/charts.d/example.conf new file mode 100644 index 000000000..dc4b6900e --- /dev/null +++ b/conf.d/charts.d/example.conf @@ -0,0 +1,17 @@ +# no need for shebang - this file is loaded from charts.d.plugin + +# netdata +# real-time performance and health monitoring, done right! +# (C) 2016 Costa Tsaousis <costa@tsaousis.gr> +# GPL v3+ + +# to enable this chart, you have to set this to 12345 +# (just a demonstration for something that needs to be checked) +#example_magic_number=12345 + +# the data collection frequency +# if unset, will inherit the netdata update frequency +#example_update_every= + +# the charts priority on the dashboard +#example_priority=150000 diff --git a/conf.d/charts.d/exim.conf b/conf.d/charts.d/exim.conf new file mode 100644 index 000000000..4a1464bbd --- /dev/null +++ b/conf.d/charts.d/exim.conf @@ -0,0 +1,20 @@ +# no need for shebang - this file is loaded from charts.d.plugin + +# netdata +# real-time performance and health monitoring, done right! +# (C) 2016 Costa Tsaousis <costa@tsaousis.gr> +# GPL v3+ + +# THIS PLUGIN IS DEPRECATED +# USE THE PYTHON.D ONE + +# the exim command to run +# if empty, it will use the one found in the system path +#exim_command= + +# the data collection frequency +# if unset, will inherit the netdata update frequency +#exim_update_every=5 + +# the charts priority on the dashboard +#exim_priority=60000 diff --git a/conf.d/charts.d/hddtemp.conf b/conf.d/charts.d/hddtemp.conf new file mode 100644 index 000000000..535cb0173 --- /dev/null +++ b/conf.d/charts.d/hddtemp.conf @@ -0,0 +1,20 @@ +# no need for shebang - this file is loaded from charts.d.plugin + +# netdata +# real-time performance and health monitoring, done right! +# (C) 2016 Costa Tsaousis <costa@tsaousis.gr> +# GPL v3+ + +# THIS PLUGIN IS DEPRECATED +# USE THE PYTHON.D ONE + +#hddtemp_host="localhost" +#hddtemp_port="7634" + +# the data collection frequency +# if unset, will inherit the netdata update frequency +#hddtemp_update_every=3 + +# the charts priority on the dashboard +#hddtemp_priority=90000 + diff --git a/conf.d/charts.d/load_average.conf b/conf.d/charts.d/load_average.conf new file mode 100644 index 000000000..abbe80cad --- /dev/null +++ b/conf.d/charts.d/load_average.conf @@ -0,0 +1,18 @@ +# no need for shebang - this file is loaded from charts.d.plugin + +# netdata +# real-time performance and health monitoring, done right! +# (C) 2016 Costa Tsaousis <costa@tsaousis.gr> +# GPL v3+ + +# THIS PLUGIN IS DEPRECATED +# netdata can collect this metric already + +#load_average_enabled=0 + +# the data collection frequency +# if unset, will inherit the netdata update frequency +#load_average_update_every=5 + +# the charts priority on the dashboard +#load_priority=100 diff --git a/conf.d/charts.d/mem_apps.conf b/conf.d/charts.d/mem_apps.conf new file mode 100644 index 000000000..aa4ac680b --- /dev/null +++ b/conf.d/charts.d/mem_apps.conf @@ -0,0 +1,15 @@ +# no need for shebang - this file is loaded from charts.d.plugin + +# netdata +# real-time performance and health monitoring, done right! +# (C) 2016 Costa Tsaousis <costa@tsaousis.gr> +# GPL v3+ + +# THIS PLUGIN IS DEPRECATED +# app.plugin can do better + +#mem_apps_apps= + +# the data collection frequency +# if unset, will inherit the netdata update frequency +#mem_apps_update_every=2 diff --git a/conf.d/charts.d/mysql.conf b/conf.d/charts.d/mysql.conf new file mode 100644 index 000000000..6a0b55a4b --- /dev/null +++ b/conf.d/charts.d/mysql.conf @@ -0,0 +1,19 @@ +# no need for shebang - this file is loaded from charts.d.plugin + +# netdata +# real-time performance and health monitoring, done right! +# (C) 2016 Costa Tsaousis <costa@tsaousis.gr> +# GPL v3+ + +# THIS PLUGIN IS DEPRECATED +# USE THE PYTHON.D ONE + +#mysql_cmds[name]="" +#mysql_opts[name]="" + +# the data collection frequency +# if unset, will inherit the netdata update frequency +#mysql_update_every=2 + +# the charts priority on the dashboard +#mysql_priority=60000 diff --git a/conf.d/charts.d/nginx.conf b/conf.d/charts.d/nginx.conf new file mode 100644 index 000000000..8b88b0e30 --- /dev/null +++ b/conf.d/charts.d/nginx.conf @@ -0,0 +1,19 @@ +# no need for shebang - this file is loaded from charts.d.plugin + +# netdata +# real-time performance and health monitoring, done right! +# (C) 2016 Costa Tsaousis <costa@tsaousis.gr> +# GPL v3+ + +# THIS PLUGIN IS DEPRECATED +# USE THE PYTHON.D ONE + +#nginx_url="http://127.0.0.1:80/stub_status" +#nginx_curl_opts="" + +# the data collection frequency +# if unset, will inherit the netdata update frequency +#nginx_update_every= + +# the charts priority on the dashboard +#nginx_priority=60000 diff --git a/conf.d/charts.d/nut.conf b/conf.d/charts.d/nut.conf new file mode 100644 index 000000000..2844849de --- /dev/null +++ b/conf.d/charts.d/nut.conf @@ -0,0 +1,20 @@ +# no need for shebang - this file is loaded from charts.d.plugin + +# netdata +# real-time performance and health monitoring, done right! +# (C) 2016 Costa Tsaousis <costa@tsaousis.gr> +# GPL v3+ + +# a space separated list of UPS names +# if empty, the list returned by 'upsc -l' will be used +#nut_ups= + +# how much time in seconds, to wait for nut to respond +#nut_timeout=2 + +# the data collection frequency +# if unset, will inherit the netdata update frequency +#nut_update_every=2 + +# the charts priority on the dashboard +#nut_priority=90000 diff --git a/conf.d/charts.d/opensips.conf b/conf.d/charts.d/opensips.conf new file mode 100644 index 000000000..abc4c70e0 --- /dev/null +++ b/conf.d/charts.d/opensips.conf @@ -0,0 +1,17 @@ +# no need for shebang - this file is loaded from charts.d.plugin + +# netdata +# real-time performance and health monitoring, done right! +# (C) 2016 Costa Tsaousis <costa@tsaousis.gr> +# GPL v3+ + +#opensips_opts="fifo get_statistics all" +#opensips_cmd= +#opensips_timeout=2 + +# the data collection frequency +# if unset, will inherit the netdata update frequency +#opensips_update_every=5 + +# the charts priority on the dashboard +#opensips_priority=80000 diff --git a/conf.d/charts.d/phpfpm.conf b/conf.d/charts.d/phpfpm.conf new file mode 100644 index 000000000..1e8576384 --- /dev/null +++ b/conf.d/charts.d/phpfpm.conf @@ -0,0 +1,22 @@ +# no need for shebang - this file is loaded from charts.d.plugin + +# netdata +# real-time performance and health monitoring, done right! +# (C) 2016 Costa Tsaousis <costa@tsaousis.gr> +# GPL v3+ + +# THIS PLUGIN IS DEPRECATED +# USE THE PYTHON.D ONE + +# first, you need open php-fpm status in php-fpm.conf +# second, you need add status location in nginx.conf +# you can see, https://easyengine.io/tutorials/php/fpm-status-page/ +#phpfpm_urls[name]="" +#phpfpm_curl_opts[name]="" + +# the data collection frequency +# if unset, will inherit the netdata update frequency +#phpfpm_update_every= + +# the charts priority on the dashboard +#phpfpm_priority=60000 diff --git a/conf.d/charts.d/postfix.conf b/conf.d/charts.d/postfix.conf new file mode 100644 index 000000000..7d33d2660 --- /dev/null +++ b/conf.d/charts.d/postfix.conf @@ -0,0 +1,20 @@ +# no need for shebang - this file is loaded from charts.d.plugin + +# netdata +# real-time performance and health monitoring, done right! +# (C) 2016 Costa Tsaousis <costa@tsaousis.gr> +# GPL v3+ + +# THIS PLUGIN IS DEPRECATED +# USE THE PYTHON.D ONE + +# the postqueue command +# if empty, it will use the one found in the system path +#postfix_postqueue= + +# the data collection frequency +# if unset, will inherit the netdata update frequency +#postfix_update_every=15 + +# the charts priority on the dashboard +#postfix_priority=60000 diff --git a/conf.d/charts.d/sensors.conf b/conf.d/charts.d/sensors.conf new file mode 100644 index 000000000..d42d17d27 --- /dev/null +++ b/conf.d/charts.d/sensors.conf @@ -0,0 +1,27 @@ +# no need for shebang - this file is loaded from charts.d.plugin + +# netdata +# real-time performance and health monitoring, done right! +# (C) 2016 Costa Tsaousis <costa@tsaousis.gr> +# GPL v3+ + +# THIS PLUGIN IS DEPRECATED +# USE THE PYTHON.D ONE + +# the directory the kernel keeps sensor data +#sensors_sys_dir="/sys/devices" + +# how deep in the tree to check for sensor data +#sensors_sys_depth=10 + +# if set to 1, the script will overwrite internal +# script functions with code generated ones +# leave to 1, is faster +#sensors_source_update=1 + +# the data collection frequency +# if unset, will inherit the netdata update frequency +#sensors_update_every= + +# the charts priority on the dashboard +#sensors_priority=90000 diff --git a/conf.d/charts.d/squid.conf b/conf.d/charts.d/squid.conf new file mode 100644 index 000000000..cf92c1245 --- /dev/null +++ b/conf.d/charts.d/squid.conf @@ -0,0 +1,21 @@ +# no need for shebang - this file is loaded from charts.d.plugin + +# netdata +# real-time performance and health monitoring, done right! +# (C) 2016 Costa Tsaousis <costa@tsaousis.gr> +# GPL v3+ + +# THIS PLUGIN IS DEPRECATED +# USE THE PYTHON.D ONE + +#squid_host= +#squid_port= +#squid_url= +#squid_timeout=2 + +# the data collection frequency +# if unset, will inherit the netdata update frequency +#squid_update_every=2 + +# the charts priority on the dashboard +#squid_priority=60000 diff --git a/conf.d/charts.d/tomcat.conf b/conf.d/charts.d/tomcat.conf new file mode 100644 index 000000000..710669423 --- /dev/null +++ b/conf.d/charts.d/tomcat.conf @@ -0,0 +1,34 @@ +# no need for shebang - this file is loaded from charts.d.plugin + +# netdata +# real-time performance and health monitoring, done right! +# (C) 2016 Costa Tsaousis <costa@tsaousis.gr> +# GPL v3+ + +# THIS PLUGIN IS DEPRECATED +# USE THE PYTHON.D ONE + +# the URL to download tomcat status info +# usually http://localhost:8080/manager/status?XML=true +#tomcat_url="" +#tomcat_curl_opts="" + +# set tomcat username/password here +#tomcat_user="" +#tomcat_password="" + +# the data collection frequency +# if unset, will inherit the netdata update frequency +#tomcat_update_every=1 + +# the charts priority on the dashboard +#tomcat_priority=60000 + +# convert tomcat floating point values +# to integer using this multiplier +# this only affects precision - the values +# will be in the proper units +#tomcat_decimal_detail=1000000 + +# used by volume chart to convert bytes to KB +#tomcat_decimal_KB_detail=1000 diff --git a/conf.d/fping.conf b/conf.d/fping.conf new file mode 100644 index 000000000..82ee2332a --- /dev/null +++ b/conf.d/fping.conf @@ -0,0 +1,44 @@ +# no need for shebang - this file is sourced from fping.plugin + +# fping.plugin requires a recent version of fping. +# +# You can get it on your system, by running: +# +# /usr/libexec/netdata/plugins.d/fping.plugin install + +# ----------------------------------------------------------------------------- +# configuration options + +# The fping binary to use. We need one that can output netdata friendly info +# (supporting: -N). If you have multiple versions, put here the full filename +# of the right one + +#fping="/usr/local/bin/fping" + + +# a space separated list of hosts to fping +# we suggest to put names here and the IPs of these names in /etc/hosts + +hosts="" + + +# The update frequency of the chart - the default is inherited from netdata + +#update_every=2 + + +# The time in milliseconds (1 sec = 1000 ms) to ping the hosts +# by default 5 pings per host per iteration +# fping will now allow this to be below 20ms + +#ping_every="200" + + +# other fping options - defaults: +# -R = send packets with random data +# -b 56 = the number of bytes per packet +# -i 1 = 1 ms when sending packets to others hosts (switching hosts) +# -r 0 = never retry packets +# -t 5000 = per packet timeout at 5000 ms + +#fping_opts="-R -b 56 -i 1 -r 0 -t 5000" diff --git a/conf.d/health.d/apache.conf b/conf.d/health.d/apache.conf index 0aaf0e003..0c98b8778 100644 --- a/conf.d/health.d/apache.conf +++ b/conf.d/health.d/apache.conf @@ -6,8 +6,8 @@ template: apache_last_collected_secs calc: $now - $last_collected_t units: seconds ago every: 10s - warn: $this > (($status >= $WARNING) ? (0) : ( 5 * $update_every)) - crit: $this > (($status == $CRITICAL) ? (0) : (60 * $update_every)) + warn: $this > (($status >= $WARNING) ? ($update_every) : ( 5 * $update_every)) + crit: $this > (($status == $CRITICAL) ? ($update_every) : (60 * $update_every)) delay: down 5m multiplier 1.5 max 1h info: number of seconds since the last successful data collection to: webmaster diff --git a/conf.d/health.d/backend.conf b/conf.d/health.d/backend.conf new file mode 100644 index 000000000..9c193e7b9 --- /dev/null +++ b/conf.d/health.d/backend.conf @@ -0,0 +1,45 @@ + +# make sure we are sending data to backend + + alarm: backend_last_buffering + on: netdata.backend_metrics + calc: $now - $last_collected_t + units: seconds ago + every: 10s + warn: $this > (($status >= $WARNING) ? ($update_every) : ( 5 * $update_every)) + crit: $this > (($status == $CRITICAL) ? ($update_every) : (60 * $update_every)) + delay: down 5m multiplier 1.5 max 1h + info: number of seconds since the last successful buffering of backend data + to: dba + + alarm: backend_metrics_sent + on: netdata.backend_metrics + units: % + calc: abs($sent) * 100 / abs($buffered) + every: 10s + warn: $this != 100 + delay: down 5m multiplier 1.5 max 1h + info: percentage of metrics sent to the backend server + to: dba + + alarm: backend_metrics_lost + on: netdata.backend_metrics + units: metrics + calc: abs($lost) + every: 10s + crit: $this != 0 + delay: down 5m multiplier 1.5 max 1h + info: number of metrics lost due to repeating failures to contact the backend server + to: dba + +# this chart has been removed from netdata +# alarm: backend_slow +# on: netdata.backend_latency +# units: % +# calc: $latency * 100 / ($update_every * 1000) +# every: 10s +# warn: $this > 50 +# crit: $this > 100 +# delay: down 5m multiplier 1.5 max 1h +# info: the percentage of time between iterations needed by the backend time to process the data sent by netdata +# to: dba diff --git a/conf.d/health.d/bind_rndc.conf b/conf.d/health.d/bind_rndc.conf new file mode 100644 index 000000000..028bc9d08 --- /dev/null +++ b/conf.d/health.d/bind_rndc.conf @@ -0,0 +1,9 @@ + alarm: bind_rndc_stats_file_size + on: bind_rndc.stats_size + units: megabytes + every: 60 + calc: $stats_size + warn: $this > 512 + crit: $this > 1024 + info: Bind stats file is very large! Consider to create logrotate conf file for it! + to: sysadmin diff --git a/conf.d/health.d/cpu.conf b/conf.d/health.d/cpu.conf index 4d79fc799..60f494d70 100644 --- a/conf.d/health.d/cpu.conf +++ b/conf.d/health.d/cpu.conf @@ -4,8 +4,8 @@ template: 10min_cpu_usage lookup: average -10m unaligned of user,system,nice,softirq,irq,guest,guest_nice units: % every: 1m - warn: $this > (($status >= $WARNING) ? (70) : (80)) - crit: $this > (($status == $CRITICAL) ? (80) : (90)) + warn: $this > (($status >= $WARNING) ? (75) : (85)) + crit: $this > (($status == $CRITICAL) ? (85) : (95)) delay: down 15m multiplier 1.5 max 1h info: average cpu utilization for the last 10 minutes to: sysadmin @@ -15,8 +15,8 @@ template: 10min_cpu_iowait lookup: average -10m unaligned of iowait units: % every: 1m - warn: $this > (($status >= $WARNING) ? (5) : (10)) - crit: $this > (($status == $CRITICAL) ? (20) : (30)) + warn: $this > (($status >= $WARNING) ? (20) : (40)) + crit: $this > (($status == $CRITICAL) ? (40) : (50)) delay: down 15m multiplier 1.5 max 1h info: average CPU wait I/O for the last 10 minutes to: sysadmin @@ -28,6 +28,6 @@ template: 20min_steal_cpu every: 5m warn: $this > (($status >= $WARNING) ? (5) : (10)) crit: $this > (($status == $CRITICAL) ? (20) : (30)) - delay: down 15m multiplier 1.5 max 1h + delay: down 1h multiplier 1.5 max 2h info: average CPU steal time for the last 20 minutes to: sysadmin diff --git a/conf.d/health.d/disks.conf b/conf.d/health.d/disks.conf index cc7a47660..0549bac26 100644 --- a/conf.d/health.d/disks.conf +++ b/conf.d/health.d/disks.conf @@ -4,11 +4,12 @@ # for mount points template: disk_space_last_collected_secs on: disk.space +families: * calc: $now - $last_collected_t units: seconds ago every: 10s - warn: $this > (($status >= $WARNING) ? (0) : ( 5 * $update_every)) - crit: $this > (($status == $CRITICAL) ? (0) : (60 * $update_every)) + warn: $this > (($status >= $WARNING) ? ($update_every) : ( 5 * $update_every)) + crit: $this > (($status == $CRITICAL) ? ($update_every) : (60 * $update_every)) delay: down 5m multiplier 1.5 max 1h info: number of seconds since the last successful data collection of the mount point to: sysadmin @@ -16,11 +17,12 @@ template: disk_space_last_collected_secs # for block devices template: disk_last_collected_secs on: disk.io +families: * calc: $now - $last_collected_t units: seconds ago every: 10s - warn: $this > (($status >= $WARNING) ? (0) : ( 5 * $update_every)) - crit: $this > (($status == $CRITICAL) ? (0) : (60 * $update_every)) + warn: $this > (($status >= $WARNING) ? ($update_every) : ( 5 * $update_every)) + crit: $this > (($status == $CRITICAL) ? ($update_every) : (60 * $update_every)) delay: down 5m multiplier 1.5 max 1h info: number of seconds since the last successful data collection of the block device to: sysadmin @@ -35,22 +37,24 @@ template: disk_last_collected_secs template: disk_space_usage on: disk.space +families: * calc: $used * 100 / ($avail + $used) units: % every: 1m - warn: $this > (($status >= $WARNING ) ? (70) : (80)) - crit: $this > (($status == $CRITICAL) ? (85) : (95)) + warn: $this > (($status >= $WARNING ) ? (80) : (90)) + crit: $this > (($status == $CRITICAL) ? (90) : (98)) delay: up 1m down 15m multiplier 1.5 max 1h info: current disk space usage to: sysadmin template: disk_inode_usage on: disk.inodes +families: * calc: $used * 100 / ($avail + $used) units: % every: 1m - warn: $this > (($status >= $WARNING) ? (75) : (80)) - crit: $this > (($status == $CRITICAL) ? (90) : (95)) + warn: $this > (($status >= $WARNING) ? (80) : (90)) + crit: $this > (($status == $CRITICAL) ? (90) : (98)) delay: up 1m down 15m multiplier 1.5 max 1h info: current disk inode usage to: sysadmin @@ -69,6 +73,7 @@ template: disk_inode_usage template: disk_fill_rate on: disk.space +families: * lookup: min -10m at -50m unaligned of avail calc: ($this - $avail) / (($now - $after) / 3600) every: 1m @@ -82,7 +87,8 @@ template: disk_fill_rate template: out_of_disk_space_time on: disk.space - calc: $avail / $disk_fill_rate +families: * + calc: ($disk_fill_rate > 0) ? ($avail / $disk_fill_rate) : (0) units: hours every: 10s warn: $this > 0 and $this < (($status >= $WARNING) ? (48) : (8)) @@ -101,6 +107,7 @@ template: out_of_disk_space_time template: 10min_disk_utilization on: disk.util +families: * lookup: average -10m unaligned units: % every: 1m @@ -120,6 +127,7 @@ template: 10min_disk_utilization template: 10min_disk_backlog on: disk.backlog +families: * lookup: average -10m unaligned units: ms every: 1m diff --git a/conf.d/health.d/elasticsearch.conf b/conf.d/health.d/elasticsearch.conf new file mode 100644 index 000000000..dffd40965 --- /dev/null +++ b/conf.d/health.d/elasticsearch.conf @@ -0,0 +1,9 @@ + alarm: elasticsearch_last_collected + on: elasticsearch_local.cluster_health_status + calc: $now - $last_collected_t + units: seconds ago + every: 10s + warn: $this > (($status >= $WARNING) ? ($update_every) : ( 5 * $update_every)) + crit: $this > (($status == $CRITICAL) ? ($update_every) : (60 * $update_every)) + info: number of seconds since the last successful data collection + to: sysadmin diff --git a/conf.d/health.d/entropy.conf b/conf.d/health.d/entropy.conf index d0eca8a6c..5dd8af502 100644 --- a/conf.d/health.d/entropy.conf +++ b/conf.d/health.d/entropy.conf @@ -3,12 +3,12 @@ # the alarm is checked every 1 minute # and examines the last hour of data - alarm: 1hour_lowest_entropy + alarm: lowest_entropy on: system.entropy - lookup: min -1h unaligned + lookup: min -10m unaligned units: entries every: 5m warn: $this < (($status >= $WARNING) ? (200) : (100)) - delay: down 1h multiplier 1.5 max 1h - info: minimum entries in the random numbers pool in the last 30 minutes + delay: down 1h multiplier 1.5 max 2h + info: minimum entries in the random numbers pool in the last 10 minutes to: silent diff --git a/conf.d/health.d/haproxy.conf b/conf.d/health.d/haproxy.conf new file mode 100644 index 000000000..e49c70d48 --- /dev/null +++ b/conf.d/health.d/haproxy.conf @@ -0,0 +1,27 @@ +template: haproxy_backend_server_status + on: haproxy_hs.down + units: failed servers + every: 10s + lookup: average -10s + crit: $this > 0 + info: number of failed haproxy backend servers + to: sysadmin + +template: haproxy_backend_status + on: haproxy_hb.down + units: failed backend + every: 10s + lookup: average -10s + crit: $this > 0 + info: number of failed haproxy backends + to: sysadmin + +template: haproxy_last_collected + on: haproxy_hb.down + calc: $now - $last_collected_t + units: seconds ago + every: 10s + warn: $this > (($status >= $WARNING) ? ($update_every) : ( 5 * $update_every)) + crit: $this > (($status == $CRITICAL) ? ($update_every) : (60 * $update_every)) + info: number of seconds since the last successful data collection + to: sysadmin diff --git a/conf.d/health.d/ipc.conf b/conf.d/health.d/ipc.conf new file mode 100644 index 000000000..ee7c4badd --- /dev/null +++ b/conf.d/health.d/ipc.conf @@ -0,0 +1,22 @@ + + alarm: semaphores_used + on: system.ipc_semaphores + calc: $semaphores * 100 / $ipc.semaphores.max + units: % + every: 10s + warn: $this > (($status >= $WARNING) ? (70) : (80)) + crit: $this > (($status == $CRITICAL) ? (70) : (90)) + delay: down 5m multiplier 1.5 max 1h + info: the percentage of IPC semaphores used + to: sysadmin + + alarm: semaphore_arrays_used + on: system.ipc_semaphore_arrays + calc: $arrays * 100 / $ipc.semaphores.arrays.max + units: % + every: 10s + warn: $this > (($status >= $WARNING) ? (70) : (80)) + crit: $this > (($status == $CRITICAL) ? (70) : (90)) + delay: down 5m multiplier 1.5 max 1h + info: the percentage of IPC semaphore arrays used + to: sysadmin diff --git a/conf.d/health.d/ipfs.conf b/conf.d/health.d/ipfs.conf new file mode 100644 index 000000000..3f77572d6 --- /dev/null +++ b/conf.d/health.d/ipfs.conf @@ -0,0 +1,11 @@ + +template: ipfs_datastore_usage + on: ipfs.repo_size + calc: $size * 100 / $avail + units: % + every: 10s + warn: $this > (($status >= $WARNING) ? (80) : (90)) + crit: $this > (($status == $CRITICAL) ? (90) : (98)) + delay: down 15m multiplier 1.5 max 1h + info: ipfs Datastore close to running out of space + to: sysadmin diff --git a/conf.d/health.d/isc_dhcpd.conf b/conf.d/health.d/isc_dhcpd.conf new file mode 100644 index 000000000..4345619aa --- /dev/null +++ b/conf.d/health.d/isc_dhcpd.conf @@ -0,0 +1,10 @@ + alarm: isc_dhcpd_parse_time + on: isc_dhcpd.parse_time + units: ms + every: 60 + calc: $ptime + warn: $this > 100 + crit: $this > 250 + delay: up 2m down 5m + info: Parsing too slow! It can slow down your server. Check dhcpd.leases file size. + to: sysadmin diff --git a/conf.d/health.d/mdstat.conf b/conf.d/health.d/mdstat.conf new file mode 100644 index 000000000..c9e7d20db --- /dev/null +++ b/conf.d/health.d/mdstat.conf @@ -0,0 +1,18 @@ +template: mdstat_disks + on: md.disks + units: failed devices + every: 10s + calc: $total - $inuse + crit: $this > 0 + info: Array is degraded! + to: sysadmin + +template: mdstat_last_collected + on: md.disks + calc: $now - $last_collected_t + units: seconds ago + every: 10s + warn: $this > (($status >= $WARNING) ? ($update_every) : ( 5 * $update_every)) + crit: $this > (($status == $CRITICAL) ? ($update_every) : (60 * $update_every)) + info: number of seconds since the last successful data collection + to: sysadmin diff --git a/conf.d/health.d/memcached.conf b/conf.d/health.d/memcached.conf index 46a8ca0e5..7917e36af 100644 --- a/conf.d/health.d/memcached.conf +++ b/conf.d/health.d/memcached.conf @@ -6,8 +6,8 @@ template: memcached_last_collected_secs calc: $now - $last_collected_t units: seconds ago every: 10s - warn: $this > (($status >= $WARNING) ? (0) : ( 5 * $update_every)) - crit: $this > (($status == $CRITICAL) ? (0) : (60 * $update_every)) + warn: $this > (($status >= $WARNING) ? ($update_every) : ( 5 * $update_every)) + crit: $this > (($status == $CRITICAL) ? ($update_every) : (60 * $update_every)) delay: down 5m multiplier 1.5 max 1h info: number of seconds since the last successful data collection to: dba @@ -42,7 +42,7 @@ template: cache_fill_rate template: out_of_cache_space_time on: memcached.cache - calc: $available / $cache_fill_rate + calc: ($cache_fill_rate > 0) ? ($available / $cache_fill_rate) : (0) units: hours every: 10s warn: $this > 0 and $this < (($status >= $WARNING) ? (48) : (8)) diff --git a/conf.d/health.d/memory.conf b/conf.d/health.d/memory.conf new file mode 100644 index 000000000..3c904f6b1 --- /dev/null +++ b/conf.d/health.d/memory.conf @@ -0,0 +1,30 @@ + + alarm: 1hour_ecc_memory_correctable + on: mem.ecc_ce + lookup: sum -10m unaligned + units: errors + every: 1m + warn: $this > 0 + delay: down 1h multiplier 1.5 max 1h + info: number of ECC correctable errors during the last hour + to: sysadmin + + alarm: 1hour_ecc_memory_uncorrectable + on: mem.ecc_ue + lookup: sum -10m unaligned + units: errors + every: 1m + crit: $this > 0 + delay: down 1h multiplier 1.5 max 1h + info: number of ECC uncorrectable errors during the last hour + to: sysadmin + + alarm: 1hour_memory_hw_corrupted + on: mem.hwcorrupt + calc: $HardwareCorrupted + units: MB + every: 10s + warn: $this > 0 + delay: down 1h multiplier 1.5 max 1h + info: amount of memory corrupted due to a hardware failure + to: sysadmin diff --git a/conf.d/health.d/mysql.conf b/conf.d/health.d/mysql.conf index a2cfa3ec5..78773e5b5 100644 --- a/conf.d/health.d/mysql.conf +++ b/conf.d/health.d/mysql.conf @@ -6,8 +6,80 @@ template: mysql_last_collected_secs calc: $now - $last_collected_t units: seconds ago every: 10s - warn: $this > (($status >= $WARNING) ? (0) : ( 5 * $update_every)) - crit: $this > (($status == $CRITICAL) ? (0) : (60 * $update_every)) + warn: $this > (($status >= $WARNING) ? ($update_every) : ( 5 * $update_every)) + crit: $this > (($status == $CRITICAL) ? ($update_every) : (60 * $update_every)) delay: down 5m multiplier 1.5 max 1h info: number of seconds since the last successful data collection to: dba + + +# ----------------------------------------------------------------------------- +# slow queries + +template: mysql_10s_slow_queries + on: mysql.queries + lookup: sum -10s of slow_queries + units: slow queries + every: 10s + warn: $this > (($status >= $WARNING) ? (5) : (10)) + crit: $this > (($status == $CRITICAL) ? (10) : (20)) + delay: down 5m multiplier 1.5 max 1h + info: number of mysql slow queries over the last 10 seconds + to: dba + + +# ----------------------------------------------------------------------------- +# lock waits + +template: mysql_10s_table_locks_immediate + on: mysql.table_locks + lookup: sum -10s absolute of immediate + units: immediate locks + every: 10s + info: number of table immediate locks over the last 10 seconds + to: dba + +template: mysql_10s_table_locks_waited + on: mysql.table_locks + lookup: sum -10s absolute of waited + units: waited locks + every: 10s + info: number of table waited locks over the last 10 seconds + to: dba + +template: mysql_10s_waited_locks_ratio + on: mysql.table_locks + calc: ($mysql_10s_table_locks_waited * 100) / ($mysql_10s_table_locks_waited + $mysql_10s_table_locks_immediate) + units: % + every: 10s + warn: $this > (($status >= $WARNING) ? (10) : (25)) + crit: $this > (($status == $CRITICAL) ? (25) : (50)) + delay: down 30m multiplier 1.5 max 1h + info: the ratio of mysql waited table locks, for the last 10 seconds + to: dba + + +# ----------------------------------------------------------------------------- +# replication + +template: mysql_replication + on: mysql.slave_status + calc: ($sql_running == -1 OR $io_running == -1)?0:1 + units: status + every: 10s + crit: $this == 0 + delay: down 5m multiplier 1.5 max 1h + info: checks if mysql replication has stopped + to: dba + +template: mysql_replication_lag + on: mysql.slave_behind + calc: $seconds + units: seconds + every: 10s + warn: $this > (($status >= $WARNING) ? (5) : (10)) + crit: $this > (($status == $CRITICAL) ? (10) : (30)) + delay: down 15m multiplier 1.5 max 1h + info: the number of seconds mysql replication is behind this master + to: dba + diff --git a/conf.d/health.d/named.conf b/conf.d/health.d/named.conf index f2eaa83c7..4fc65c8ee 100644 --- a/conf.d/health.d/named.conf +++ b/conf.d/health.d/named.conf @@ -6,8 +6,8 @@ template: named_last_collected_secs calc: $now - $last_collected_t units: seconds ago every: 10s - warn: $this > (($status >= $WARNING) ? (0) : ( 5 * $update_every)) - crit: $this > (($status == $CRITICAL) ? (0) : (60 * $update_every)) + warn: $this > (($status >= $WARNING) ? ($update_every) : ( 5 * $update_every)) + crit: $this > (($status == $CRITICAL) ? ($update_every) : (60 * $update_every)) delay: down 5m multiplier 1.5 max 1h info: number of seconds since the last successful data collection to: domainadmin diff --git a/conf.d/health.d/net.conf b/conf.d/health.d/net.conf index 7753aa184..924acccc3 100644 --- a/conf.d/health.d/net.conf +++ b/conf.d/health.d/net.conf @@ -3,46 +3,119 @@ template: interface_last_collected_secs on: net.net +families: * calc: $now - $last_collected_t units: seconds ago every: 10s - warn: $this > (($status >= $WARNING) ? (0) : ( 5 * $update_every)) - crit: $this > (($status == $CRITICAL) ? (0) : (60 * $update_every)) + warn: $this > (($status >= $WARNING) ? ($update_every) : ( 5 * $update_every)) + crit: $this > (($status == $CRITICAL) ? ($update_every) : (60 * $update_every)) delay: down 5m multiplier 1.5 max 1h info: number of seconds since the last successful data collection to: sysadmin # ----------------------------------------------------------------------------- +# dropped packets # check if an interface is dropping packets # the alarm is checked every 1 minute -# and examines the last hour of data +# and examines the last 10 minutes of data -template: 1hour_packet_drops +template: inbound_packets_dropped on: net.drops - lookup: sum -1h unaligned absolute +families: * + lookup: sum -10m unaligned absolute of inbound units: packets every: 1m warn: $this > 0 - delay: down 30m multiplier 1.5 max 1h - info: interface dropped packets in the last hour + delay: down 1h multiplier 1.5 max 2h + info: interface inbound dropped packets in the last 10 minutes + to: sysadmin + +template: outbound_packets_dropped + on: net.drops +families: * + lookup: sum -10m unaligned absolute of outbound + units: packets + every: 1m + warn: $this > 0 + delay: down 1h multiplier 1.5 max 2h + info: interface outbound dropped packets in the last 10 minutes + to: sysadmin + +template: inbound_packets_dropped_ratio + on: net.packets +families: * + lookup: sum -10m unaligned absolute of received + calc: (($inbound_packets_dropped != nan AND $this > 0) ? ($inbound_packets_dropped * 100 / $this) : (0)) + units: % + every: 1m + warn: $this > 0.5 + crit: $this > 3 + delay: down 1h multiplier 1.5 max 2h + info: the ratio of inbound dropped packets vs the total number of received packets of the network interface, during the last 10 minutes + to: sysadmin + +template: outbound_packets_dropped_ratio + on: net.packets +families: * + lookup: sum -10m unaligned absolute of sent + calc: (($outbound_packets_dropped != nan AND $this > 0) ? ($outbound_packets_dropped * 100 / $this) : (0)) + units: % + every: 1m + warn: $this > 0.5 + crit: $this > 3 + delay: down 1h multiplier 1.5 max 2h + info: the ratio of outbound dropped packets vs the total number of sent packets of the network interface, during the last 10 minutes to: sysadmin # ----------------------------------------------------------------------------- +# FIFO errors # check if an interface is having FIFO # buffer errors # the alarm is checked every 1 minute -# and examines the last hour of data +# and examines the last 10 minutes of data -template: 1hour_fifo_errors +template: 10min_fifo_errors on: net.fifo - lookup: sum -1h unaligned absolute +families: * + lookup: sum -10m unaligned absolute units: errors every: 1m warn: $this > 0 - delay: down 30m multiplier 1.5 max 1h - info: interface fifo errors in the last hour + delay: down 1h multiplier 1.5 max 2h + info: interface fifo errors in the last 10 minutes to: sysadmin + + +# ----------------------------------------------------------------------------- +# check for packet storms + +# 1. calculate the rate packets are received in 1m: 1m_received_packets_rate +# 2. do the same for the last 10s +# 3. raise an alarm if the later is 10x or 20x the first +# we assume the minimum packet storm should at least have +# 10000 packets/s, average of the last 10 seconds + +template: 1m_received_packets_rate + on: net.packets +families: * + lookup: average -1m of received + units: packets + every: 10s + info: the average number of packets received during the last minute + +template: 10s_received_packets_storm + on: net.packets +families: * + lookup: average -10s of received + calc: $this * 100 / (($1m_received_packets_rate < 1000)?(1000):($1m_received_packets_rate)) + every: 10s + units: % + warn: $this > (($status >= $WARNING)?(200):(1000)) + crit: $this > (($status >= $WARNING)?(1000):(2000)) + info: the % of the rate of received packets in the last 10 seconds, compared to the rate of the last minute + to: silent + diff --git a/conf.d/health.d/netfilter.conf b/conf.d/health.d/netfilter.conf new file mode 100644 index 000000000..3dd6a67b3 --- /dev/null +++ b/conf.d/health.d/netfilter.conf @@ -0,0 +1,23 @@ + + alarm: netfilter_last_collected_secs + on: netfilter.conntrack_sockets + calc: $now - $last_collected_t + units: seconds ago + every: 10s + warn: $this > (($status >= $WARNING) ? ($update_every) : ( 5 * $update_every)) + crit: $this > (($status == $CRITICAL) ? ($update_every) : (60 * $update_every)) + delay: down 5m multiplier 1.5 max 1h + info: number of seconds since the last successful data collection + to: sysadmin + + alarm: netfilter_conntrack_full + on: netfilter.conntrack_sockets + lookup: max -10s unaligned of connections + calc: $this * 100 / $netfilter.conntrack.max + units: % + every: 10s + warn: $this > (($status >= $WARNING) ? (70) : (80)) + crit: $this > (($status == $CRITICAL) ? (80) : (90)) + delay: down 5m multiplier 1.5 max 1h + info: the number of connections tracked by the netfilter connection tracker, as a percentage of the connection tracker table size + to: sysadmin diff --git a/conf.d/health.d/nginx.conf b/conf.d/health.d/nginx.conf index d70d6a59b..a686c3d99 100644 --- a/conf.d/health.d/nginx.conf +++ b/conf.d/health.d/nginx.conf @@ -6,8 +6,8 @@ template: nginx_last_collected_secs calc: $now - $last_collected_t units: seconds ago every: 10s - warn: $this > (($status >= $WARNING) ? (0) : ( 5 * $update_every)) - crit: $this > (($status == $CRITICAL) ? (0) : (60 * $update_every)) + warn: $this > (($status >= $WARNING) ? ($update_every) : ( 5 * $update_every)) + crit: $this > (($status == $CRITICAL) ? ($update_every) : (60 * $update_every)) delay: down 5m multiplier 1.5 max 1h info: number of seconds since the last successful data collection to: webmaster diff --git a/conf.d/health.d/postgres.conf b/conf.d/health.d/postgres.conf new file mode 100644 index 000000000..4e0583b85 --- /dev/null +++ b/conf.d/health.d/postgres.conf @@ -0,0 +1,13 @@ + +# make sure postgres is running + +template: postgres_last_collected_secs + on: postgres.db_stat_transactions + calc: $now - $last_collected_t + units: seconds ago + every: 10s + warn: $this > (($status >= $WARNING) ? ($update_every) : ( 5 * $update_every)) + crit: $this > (($status == $CRITICAL) ? ($update_every) : (60 * $update_every)) + delay: down 5m multiplier 1.5 max 1h + info: number of seconds since the last successful data collection + to: dba diff --git a/conf.d/health.d/ram.conf b/conf.d/health.d/ram.conf index 216b82fed..d60df75b2 100644 --- a/conf.d/health.d/ram.conf +++ b/conf.d/health.d/ram.conf @@ -4,8 +4,8 @@ calc: $used * 100 / ($used + $cached + $free) units: % every: 10s - warn: $this > (($status >= $WARNING) ? (70) : (80)) - crit: $this > (($status == $CRITICAL) ? (80) : (90)) + warn: $this > (($status >= $WARNING) ? (80) : (90)) + crit: $this > (($status == $CRITICAL) ? (90) : (98)) delay: down 15m multiplier 1.5 max 1h info: system RAM usage to: sysadmin diff --git a/conf.d/health.d/redis.conf b/conf.d/health.d/redis.conf index 3e648d85d..5f6d397ea 100644 --- a/conf.d/health.d/redis.conf +++ b/conf.d/health.d/redis.conf @@ -6,8 +6,8 @@ template: redis_last_collected_secs calc: $now - $last_collected_t units: seconds ago every: 10s - warn: $this > (($status >= $WARNING) ? (0) : ( 5 * $update_every)) - crit: $this > (($status == $CRITICAL) ? (0) : (60 * $update_every)) + warn: $this > (($status >= $WARNING) ? ($update_every) : ( 5 * $update_every)) + crit: $this > (($status == $CRITICAL) ? ($update_every) : (60 * $update_every)) delay: down 5m multiplier 1.5 max 1h info: number of seconds since the last successful data collection to: dba diff --git a/conf.d/health.d/retroshare.conf b/conf.d/health.d/retroshare.conf index 1af7b4686..2344b60ec 100644 --- a/conf.d/health.d/retroshare.conf +++ b/conf.d/health.d/retroshare.conf @@ -5,8 +5,8 @@ template: retroshare_last_collected_secs calc: $now - $last_collected_t units: seconds ago every: 10s - warn: $this > (($status >= $WARNING) ? (0) : ( 5 * $update_every)) - crit: $this > (($status == $CRITICAL) ? (0) : (60 * $update_every)) + warn: $this > (($status >= $WARNING) ? ($update_every) : ( 5 * $update_every)) + crit: $this > (($status == $CRITICAL) ? ($update_every) : (60 * $update_every)) delay: down 5m multiplier 1.5 max 1h info: number of seconds since the last successful data collection to: sysadmin diff --git a/conf.d/health.d/softnet.conf b/conf.d/health.d/softnet.conf index 0c3709f46..5faf9a9ee 100644 --- a/conf.d/health.d/softnet.conf +++ b/conf.d/health.d/softnet.conf @@ -1,21 +1,21 @@ # check for common /proc/net/softnet_stat errors - alarm: 1hour_netdev_backlog_exceeded + alarm: 10min_netdev_backlog_exceeded on: system.softnet_stat - lookup: sum -1h unaligned absolute of dropped + lookup: sum -10m unaligned absolute of dropped units: packets every: 1m warn: $this > 0 - delay: down 30m multiplier 1.5 max 1h - info: number of packets dropped because sysctl net.core.netdev_max_backlog was exceeded (this can be a cause for dropped packets) + delay: down 1h multiplier 1.5 max 2h + info: number of packets dropped in the last 10min, because sysctl net.core.netdev_max_backlog was exceeded (this can be a cause for dropped packets) to: sysadmin - alarm: 1hour_netdev_budget_ran_outs + alarm: 10min_netdev_budget_ran_outs on: system.softnet_stat - lookup: sum -1h unaligned absolute of squeezed + lookup: sum -10m unaligned absolute of squeezed units: events every: 1m - warn: $this > 0 - delay: down 30m multiplier 1.5 max 1h - info: number of times ksoftirq ran out of sysctl net.core.netdev_budget or time slice, with work remaining (this can be a cause for dropped packets) + warn: $this > (($status >= $WARNING) ? (0) : (10)) + delay: down 1h multiplier 1.5 max 2h + info: number of times, during the last 10min, ksoftirq ran out of sysctl net.core.netdev_budget or time slice, with work remaining (this can be a cause for dropped packets) to: silent diff --git a/conf.d/health.d/squid.conf b/conf.d/health.d/squid.conf index 76143c5d7..06cc9678f 100644 --- a/conf.d/health.d/squid.conf +++ b/conf.d/health.d/squid.conf @@ -6,8 +6,8 @@ template: squid_last_collected_secs calc: $now - $last_collected_t units: seconds ago every: 10s - warn: $this > (($status >= $WARNING) ? (0) : ( 5 * $update_every)) - crit: $this > (($status == $CRITICAL) ? (0) : (60 * $update_every)) + warn: $this > (($status >= $WARNING) ? ($update_every) : ( 5 * $update_every)) + crit: $this > (($status == $CRITICAL) ? ($update_every) : (60 * $update_every)) delay: down 5m multiplier 1.5 max 1h info: number of seconds since the last successful data collection to: proxyadmin diff --git a/conf.d/health.d/swap.conf b/conf.d/health.d/swap.conf index 0cfa888c4..7f57560e2 100644 --- a/conf.d/health.d/swap.conf +++ b/conf.d/health.d/swap.conf @@ -6,13 +6,13 @@ calc: $this / 1024 * 100 / ( $system.ram.used + $system.ram.cached + $system.ram.free ) units: % of RAM every: 1m - warn: $this > (($status >= $WARNING) ? (5) : (10)) - crit: $this > (($status == $CRITICAL) ? (15) : (20)) + warn: $this > (($status >= $WARNING) ? (10) : (20)) + crit: $this > (($status == $CRITICAL) ? (20) : (30)) delay: up 0 down 15m multiplier 1.5 max 1h info: the amount of memory swapped in the last 30 minutes, as a percentage of the system RAM to: sysadmin - alarm: used_swap_space + alarm: ram_in_swap on: system.swap calc: $used * 100 / ( $system.ram.used + $system.ram.cached + $system.ram.free ) units: % of RAM @@ -22,3 +22,14 @@ delay: up 0 down 15m multiplier 1.5 max 1h info: the swap memory used, as a percentage of the system RAM to: sysadmin + + alarm: used_swap + on: system.swap + calc: $used * 100 / ( $used + $free ) + units: % + every: 10s + warn: $this > (($status >= $WARNING) ? (80) : (90)) + crit: $this > (($status == $CRITICAL) ? (90) : (98)) + delay: up 0 down 15m multiplier 1.5 max 1h + info: the percentage of swap memory used + to: sysadmin diff --git a/conf.d/health.d/tcp_resets.conf b/conf.d/health.d/tcp_resets.conf index 8e93c4793..daf24a1cd 100644 --- a/conf.d/health.d/tcp_resets.conf +++ b/conf.d/health.d/tcp_resets.conf @@ -5,28 +5,48 @@ calc: $now - $last_collected_t units: seconds ago every: 10s - warn: $this > (($status >= $WARNING) ? (0) : ( 5 * $update_every)) - crit: $this > (($status == $CRITICAL) ? (0) : (60 * $update_every)) + warn: $this > (($status >= $WARNING) ? ($update_every) : ( 5 * $update_every)) + crit: $this > (($status == $CRITICAL) ? ($update_every) : (60 * $update_every)) delay: up 0 down 5m multiplier 1.5 max 1h info: number of seconds since the last successful data collection to: sysadmin # ----------------------------------------------------------------------------- +# tcp resets this host sends - alarm: 1m_ipv4_tcp_resets + alarm: 1m_ipv4_tcp_resets_sent on: ipv4.tcphandshake lookup: average -1m at -10s unaligned absolute of OutRsts units: tcp resets/s every: 10s info: average TCP RESETS this host is sending, over the last minute - alarm: 10s_ipv4_tcp_resets + alarm: 10s_ipv4_tcp_resets_sent on: ipv4.tcphandshake lookup: average -10s unaligned absolute of OutRsts units: tcp resets/s every: 10s - warn: $this > ((($1m_ipv4_tcp_resets < 5)?(5):($1m_ipv4_tcp_resets)) * (($status >= $WARNING) ? (1) : (4))) + warn: $this > ((($1m_ipv4_tcp_resets_sent < 5)?(5):($1m_ipv4_tcp_resets_sent)) * (($status >= $WARNING) ? (1) : (4))) delay: up 0 down 60m multiplier 1.2 max 2h info: average TCP RESETS this host is sending, over the last 10 seconds (this can be an indication that a port scan is made, or that a service running on this host has crashed) - to: sysadmin + to: silent + +# ----------------------------------------------------------------------------- +# tcp resets this host receives + + alarm: 1m_ipv4_tcp_resets_received + on: ipv4.tcphandshake + lookup: average -1m at -10s unaligned absolute of AttemptFails + units: tcp resets/s + every: 10s + info: average TCP RESETS this host is sending, over the last minute + alarm: 10s_ipv4_tcp_resets_received + on: ipv4.tcphandshake + lookup: average -10s unaligned absolute of AttemptFails + units: tcp resets/s + every: 10s + warn: $this > ((($1m_ipv4_tcp_resets_received < 5)?(5):($1m_ipv4_tcp_resets_received)) * (($status >= $WARNING) ? (1) : (4))) + delay: up 0 down 60m multiplier 1.2 max 2h + info: average TCP RESETS this host is receiving, over the last 10 seconds (this can be an indication that a service this host needs, has crashed) + to: silent diff --git a/conf.d/health.d/udp_errors.conf b/conf.d/health.d/udp_errors.conf new file mode 100644 index 000000000..98e955c02 --- /dev/null +++ b/conf.d/health.d/udp_errors.conf @@ -0,0 +1,40 @@ +# ----------------------------------------------------------------------------- + + alarm: ipv4_udperrors_last_collected_secs + on: ipv4.udperrors + calc: $now - $last_collected_t + units: seconds ago + every: 10s + warn: $this > (($status >= $WARNING) ? ($update_every) : ( 5 * $update_every)) + crit: $this > (($status == $CRITICAL) ? ($update_every) : (60 * $update_every)) + delay: up 0 down 5m multiplier 1.5 max 1h + info: number of seconds since the last successful data collection + to: sysadmin + +# ----------------------------------------------------------------------------- +# UDP receive buffer errors + + alarm: 1m_ipv4_udp_receive_buffer_errors + on: ipv4.udperrors + lookup: sum -1m unaligned absolute of RcvbufErrors + units: errors + every: 10s + warn: $this > 0 + crit: $this > 100 + info: number of UDP receive buffer errors during the last minute + delay: up 0 down 60m multiplier 1.2 max 2h + to: sysadmin + +# ----------------------------------------------------------------------------- +# UDP send buffer errors + + alarm: 1m_ipv4_udp_send_buffer_errors + on: ipv4.udperrors + lookup: sum -1m unaligned absolute of SndbufErrors + units: errors + every: 10s + warn: $this > 0 + crit: $this > 100 + info: number of UDP send buffer errors during the last minute + delay: up 0 down 60m multiplier 1.2 max 2h + to: sysadmin diff --git a/conf.d/health.d/varnish.conf b/conf.d/health.d/varnish.conf new file mode 100644 index 000000000..cca7446b4 --- /dev/null +++ b/conf.d/health.d/varnish.conf @@ -0,0 +1,9 @@ + alarm: varnish_last_collected + on: varnish.uptime + calc: $now - $last_collected_t + units: seconds ago + every: 10s + warn: $this > (($status >= $WARNING) ? ($update_every) : ( 5 * $update_every)) + crit: $this > (($status == $CRITICAL) ? ($update_every) : (60 * $update_every)) + info: number of seconds since the last successful data collection + to: sysadmin diff --git a/conf.d/health_alarm_notify.conf b/conf.d/health_alarm_notify.conf index d451cafed..b838e8024 100644 --- a/conf.d/health_alarm_notify.conf +++ b/conf.d/health_alarm_notify.conf @@ -8,6 +8,9 @@ # - push notifications to your mobile phone (pushover.net), # - messages to your slack team (slack.com), # - messages to your telegram chat / group chat (telegram.org) +# - sms messages to your cell phone or any sms enabled device (twilio.com) +# - sms messages to your cell phone or any sms enabled device (messagebird.com) +# - notifications to users on pagerduty.com # # The 'to' line given at netdata alarms defines a *role*, so that many # people can be notified for each role. @@ -15,16 +18,16 @@ # This file is a BASH script itself. # # -############################################################################### +#------------------------------------------------------------------------------ # proxy configuration # -# If you need to send curl based notifications (pushover, slack, telegram) -# via a proxy, set these to your proxy address: +# If you need to send curl based notifications (pushover, pushbullet, slack, +# telegram) via a proxy, set these to your proxy address: #export http_proxy="http://10.0.0.1:3128/" #export https_proxy="http://10.0.0.1:3128/" -############################################################################### +#------------------------------------------------------------------------------ # notifications images # # Images in notifications need to be downloaded from an Internet facing site. @@ -36,7 +39,7 @@ #images_base_url="http://my.public.netdata.server:19999" -############################################################################### +#------------------------------------------------------------------------------ # external commands # The full path to the sendmail command. @@ -46,12 +49,11 @@ sendmail="" # The full path of the curl command. # If empty, the system $PATH will be searched for it. -# If not found, pushover, telegram and slack notifications will be -# silently disabled. +# If not found, most notifications will be silently disabled. curl="" -############################################################################### +#------------------------------------------------------------------------------ # NOTE ABOUT RECIPIENTS # # When you define recipients (all types): @@ -60,24 +62,30 @@ curl="" # - pushover user tokens # - telegram chat ids # - slack channels +# - hipchat rooms +# - sms phone numbers +# - pagerduty.com (pd) services # # You can append |critical to limit the notifications to be sent. # # In these examples, the first recipient receives all the alarms # while the second one receives only the critical ones: # -# email : "user1@example.com user2@example.com|critical" -# pushover: "2987343...9437837 8756278...2362736|critical" -# telegram: "111827421 112746832|critical" -# slack : "alarms disasters|critical" +# email : "user1@example.com user2@example.com|critical" +# pushover : "2987343...9437837 8756278...2362736|critical" +# telegram : "111827421 112746832|critical" +# slack : "alarms disasters|critical" +# twilio : "+15555555555 +17777777777|critical" +# messagebird: "+15555555555 +17777777777|critical" +# pd : "<pd_service_key_1> <pd_service_key_2>|critical" # # If a recipient is set to empty string, the default recipient of the given -# notification method (email, pushover, telegram, slack) will be used. +# notification method (email, pushover, telegram, slack, etc) will be used. # To disable a notification, use the recipient called: disabled # This works for all notification methods (including the default recipients). -############################################################################### +#------------------------------------------------------------------------------ # email global notification options # multiple recipients can be given like this: @@ -91,7 +99,7 @@ DEFAULT_RECIPIENT_EMAIL="root" # to receive only critical alarms, set it to "root|critical" -############################################################################### +#------------------------------------------------------------------------------ # pushover (pushover.net) global notification options # multiple recipients can be given like this: @@ -112,14 +120,79 @@ PUSHOVER_APP_TOKEN="" DEFAULT_RECIPIENT_PUSHOVER="" -############################################################################### +#------------------------------------------------------------------------------ +# pushbullet (pushbullet.com) push notification options + +# multiple recipients can be given like this: +# "user1@email.com user2@mail.com" + +# enable/disable sending pushbullet notifications +SEND_PUSHBULLET="YES" + +# Signup and Login to pushbullet.com +# To get your Access Token, go to https://www.pushbullet.com/#settings/account +# Create a new access token and paste it below. +# Then just set the recipients' emails. +# Please note that the if the email in the DEFAULT_RECIPIENT_PUSHBULLET does +# not have a pushbullet account, the pushbullet service will send an email +# to that address instead. + +# Without an access token, netdata cannot send pushbullet notifications. +PUSHBULLET_ACCESS_TOKEN="" +DEFAULT_RECIPIENT_PUSHBULLET="" + + +#------------------------------------------------------------------------------ +# Twilio (twilio.com) SMS options + +# multiple recipients can be given like this: +# "+15555555555 +17777777777" + +# enable/disable sending twilio SMS +SEND_TWILIO="YES" + +# Signup for free trial and select a SMS capable Twilio Number +# To get your Account SID and Token, go to https://www.twilio.com/console +# Place your sid, token and number below. +# Then just set the recipients' phone numbers. +# The trial account is only allowed to use the number specified when set up. + +# Without an account sid and token, netdata cannot send Twilio text messages. +TWILIO_ACCOUNT_SID="" +TWILIO_ACCOUNT_TOKEN="" +TWILIO_NUMBER="" +DEFAULT_RECIPIENT_TWILIO="" + + +#------------------------------------------------------------------------------ +# Messagebird (messagebird.com) SMS options + +# multiple recipients can be given like this: +# "+15555555555 +17777777777" + +# enable/disable sending messagebird SMS +SEND_MESSAGEBIRD="YES" + +# to get an access key, create a free account at https://www.messagebird.com +# verify and activate the account (no CC info needed) +# login to your account and enter your phonenumber to get some free credits +# to get the API key, click on 'API' in the sidebar, then 'API Access (REST)' +# click 'Add access key' and fill in data (you want a live key to send SMS) + +# Without an access key, netdata cannot send Messagebird text messages. +MESSAGEBIRD_ACCESS_KEY="" +MESSAGEBIRD_NUMBER="" +DEFAULT_RECIPIENT_MESSAGEBIRD="" + + +#------------------------------------------------------------------------------ # telegram (telegram.org) global notification options # To get your chat ID send the command /my_id to telegram bot @get_id. # Users also need to open a query with the bot (see below). # note: multiple recipients can be given like this: -# "CHAT_ID_1 CHAT_ID_1 ..." +# "CHAT_ID_1 CHAT_ID_2 ..." # enable/disable sending telegram messages SEND_TELEGRAM="YES" @@ -133,7 +206,7 @@ TELEGRAM_BOT_TOKEN="" DEFAULT_RECIPIENT_TELEGRAM="" -############################################################################### +#------------------------------------------------------------------------------ # slack (slack.com) global notification options # multiple recipients can be given like this: @@ -154,6 +227,57 @@ SLACK_WEBHOOK_URL="" DEFAULT_RECIPIENT_SLACK="" +#------------------------------------------------------------------------------ +# hipchat global notification options + +# multiple recipients can be given like this: +# "ROOM1 ROOM2 ..." + +# enable/disable sending hipchat notifications +SEND_HIPCHAT="YES" + +# api.hipchat.com authorization token +# Without this, netdata cannot send hipchat notifications. +HIPCHAT_AUTH_TOKEN="" + +# if a role's recipients are not configured, a notification will be send to +# this hipchat room (empty = do not send a notification for unconfigured +# roles): +DEFAULT_RECIPIENT_HIPCHAT="" + + +#------------------------------------------------------------------------------ +# kafka notification options + +# enable/disable sending kafka notifications +SEND_KAFKA="YES" + +# The URL to POST kafka alarm data to. It should be the full URL. +KAFKA_URL="" + +# The IP to be used in the kafka message as the sender. +KAFKA_SENDER_IP="" + + +#------------------------------------------------------------------------------ +# pagerduty.com notification options +# +# pagerduty.com notifications require the pagerduty agent to be installed and +# a "Generic API" pagerduty service. +# https://www.pagerduty.com/docs/guides/agent-install-guide/ + +# multiple recipients can be given like this: +# "<pd_service_key_1> <pd_service_key_2> ..." + +# enable/disable sending pagerduty notifications +SEND_PD="YES" + +# if a role's recipients are not configured, a notification will be sent to +# the "General API" pagerduty.com service that uses this service key. +# (empty = do not send a notification for unconfigured roles): +DEFAULT_RECIPIENT_PD="" + + ############################################################################### # RECIPIENTS PER ROLE @@ -165,10 +289,19 @@ role_recipients_email[sysadmin]="${DEFAULT_RECIPIENT_EMAIL}" role_recipients_pushover[sysadmin]="${DEFAULT_RECIPIENT_PUSHOVER}" +role_recipients_pushbullet[sysadmin]="${DEFAULT_RECIPIENT_PUSHBULLET}" + role_recipients_telegram[sysadmin]="${DEFAULT_RECIPIENT_TELEGRAM}" role_recipients_slack[sysadmin]="${DEFAULT_RECIPIENT_SLACK}" +role_recipients_hipchat[sysadmin]="${DEFAULT_RECIPIENT_HIPCHAT}" + +role_recipients_twilio[sysadmin]="${DEFAULT_RECIPIENT_TWILIO}" + +role_recipients_messagebird[sysadmin]="${DEFAULT_RECIPIENT_MESSAGEBIRD}" + +role_recipients_pd[sysadmin]="${DEFAULT_RECIPIENT_PD}" # ----------------------------------------------------------------------------- # DNS related alarms @@ -177,46 +310,82 @@ role_recipients_email[domainadmin]="${DEFAULT_RECIPIENT_EMAIL}" role_recipients_pushover[domainadmin]="${DEFAULT_RECIPIENT_PUSHOVER}" +role_recipients_pushbullet[domainadmin]="${DEFAULT_RECIPIENT_PUSHBULLET}" + role_recipients_telegram[domainadmin]="${DEFAULT_RECIPIENT_TELEGRAM}" role_recipients_slack[domainadmin]="${DEFAULT_RECIPIENT_SLACK}" +role_recipients_hipchat[domainadmin]="${DEFAULT_RECIPIENT_HIPCHAT}" + +role_recipients_twilio[domainadmin]="${DEFAULT_RECIPIENT_TWILIO}" + +role_recipients_messagebird[domainadmin]="${DEFAULT_RECIPIENT_MESSAGEBIRD}" + +role_recipients_pd[domainadmin]="${DEFAULT_RECIPIENT_PD}" # ----------------------------------------------------------------------------- # database servers alarms -# mysql, redis, memcached, etc +# mysql, redis, memcached, postgres, etc role_recipients_email[dba]="${DEFAULT_RECIPIENT_EMAIL}" role_recipients_pushover[dba]="${DEFAULT_RECIPIENT_PUSHOVER}" +role_recipients_pushbullet[dba]="${DEFAULT_RECIPIENT_PUSHBULLET}" + role_recipients_telegram[dba]="${DEFAULT_RECIPIENT_TELEGRAM}" role_recipients_slack[dba]="${DEFAULT_RECIPIENT_SLACK}" +role_recipients_hipchat[dba]="${DEFAULT_RECIPIENT_HIPCHAT}" + +role_recipients_twilio[dba]="${DEFAULT_RECIPIENT_TWILIO}" + +role_recipients_messagebird[dba]="${DEFAULT_RECIPIENT_MESSAGEBIRD}" + +role_recipients_pd[dba]="${DEFAULT_RECIPIENT_PD}" # ----------------------------------------------------------------------------- # web servers alarms -# apache, nginx, etc +# apache, nginx, lighttpd, etc role_recipients_email[webmaster]="${DEFAULT_RECIPIENT_EMAIL}" role_recipients_pushover[webmaster]="${DEFAULT_RECIPIENT_PUSHOVER}" +role_recipients_pushbullet[webmaster]="${DEFAULT_RECIPIENT_PUSHBULLET}" + role_recipients_telegram[webmaster]="${DEFAULT_RECIPIENT_TELEGRAM}" role_recipients_slack[webmaster]="${DEFAULT_RECIPIENT_SLACK}" +role_recipients_hipchat[webmaster]="${DEFAULT_RECIPIENT_HIPCHAT}" + +role_recipients_twilio[webmaster]="${DEFAULT_RECIPIENT_TWILIO}" + +role_recipients_messagebird[webmaster]="${DEFAULT_RECIPIENT_MESSAGEBIRD}" + +role_recipients_pd[webmaster]="${DEFAULT_RECIPIENT_PD}" # ----------------------------------------------------------------------------- # proxy servers alarms -# apache, nginx, etc +# squid, etc role_recipients_email[proxyadmin]="${DEFAULT_RECIPIENT_EMAIL}" role_recipients_pushover[proxyadmin]="${DEFAULT_RECIPIENT_PUSHOVER}" +role_recipients_pushbullet[proxyadmin]="${DEFAULT_RECIPIENT_PUSHBULLET}" + role_recipients_telegram[proxyadmin]="${DEFAULT_RECIPIENT_TELEGRAM}" role_recipients_slack[proxyadmin]="${DEFAULT_RECIPIENT_SLACK}" +role_recipients_hipchat[proxyadmin]="${DEFAULT_RECIPIENT_HIPCHAT}" + +role_recipients_twilio[proxyadmin]="${DEFAULT_RECIPIENT_TWILIO}" + +role_recipients_messagebird[proxyadmin]="${DEFAULT_RECIPIENT_MESSAGEBIRD}" + +role_recipients_pd[proxyadmin]="${DEFAULT_RECIPIENT_PD}" diff --git a/conf.d/node.d.conf b/conf.d/node.d.conf new file mode 100644 index 000000000..95aec99ce --- /dev/null +++ b/conf.d/node.d.conf @@ -0,0 +1,39 @@ +{
+ "___help_1": "Default options for node.d.plugin - this is a JSON file.",
+ "___help_2": "Use http://jsonlint.com/ to verify it is valid JSON.",
+ "___help_3": "------------------------------------------------------------",
+
+ "___help_update_every": "Minimum data collection frequency for all node.d/*.node.js modules. Set it to 0 to inherit it from netdata.",
+ "update_every": 0,
+
+ "___help_modules_enable_autodetect": "Enable/disable auto-detection for node.d/*.node.js modules that support it.",
+ "modules_enable_autodetect": true,
+
+ "___help_modules_enable_all": "Enable all node.d/*.node.js modules by default.",
+ "modules_enable_all": true,
+
+ "___help_modules": "Enable/disable the following modules. Give only XXX for node.d/XXX.node.js",
+ "modules": {
+ "named": {
+ "enabled": true
+ },
+ "sma_webbox": {
+ "enabled": true
+ },
+ "snmp": {
+ "enabled": true
+ }
+ },
+
+ "___help_paths": "Paths that control the operation of node.d.plugin",
+ "paths": {
+ "___help_plugins": "The full path to the modules javascript node.d/ directory",
+ "plugins": null,
+
+ "___help_config": "The full path to the modules configs node.d/ directory",
+ "config": null,
+
+ "___help_modules": "Array of paths to add to node.js when searching for node_modules",
+ "modules": []
+ }
+}
diff --git a/conf.d/node.d/README.md b/conf.d/node.d/README.md new file mode 100644 index 000000000..45e3d02a6 --- /dev/null +++ b/conf.d/node.d/README.md @@ -0,0 +1,7 @@ +`node.d.plugin` modules accept configuration in JSON format. + +Unfortunately, JSON files do not accept comments. So, the best way to describe them is to have markdown text files with instructions. + +JSON has a very strict formatting. If you get errors from netdata at `/var/log/netdata/error.log` that a certain configuration file cannot be loaded, we suggest to verify it at [http://jsonlint.com/](http://jsonlint.com/). + +The files in this directory, provide usable examples for configuring each `node.d.plugin` module. diff --git a/conf.d/node.d/named.conf.md b/conf.d/node.d/named.conf.md new file mode 100644 index 000000000..fa843dd58 --- /dev/null +++ b/conf.d/node.d/named.conf.md @@ -0,0 +1,344 @@ +# ISC Bind Statistics
+
+Using this netdata collector, you can monitor one or more ISC Bind servers.
+
+The source code for this plugin in [here](https://github.com/firehol/netdata/blob/master/node.d/named.node.js).
+
+## Example netdata charts
+
+Depending on the number of views your bind has, you may get a large number of charts.
+Here this is with just one view:
+
+![image](https://cloud.githubusercontent.com/assets/2662304/12765473/879b8e04-ca07-11e5-817d-b0651996c42b.png)
+![image](https://cloud.githubusercontent.com/assets/2662304/12766538/12b272fa-ca0d-11e5-81e1-6a9f8ff488ff.png)
+
+## How it works
+
+The plugin will execute (from within node.js) the equivalent of:
+
+```sh
+curl "http://localhost:8888/json/v1/server"
+```
+
+Here is a sample of the output this command produces.
+
+```js
+{
+ "json-stats-version":"1.0",
+ "boot-time":"2016-01-31T08:20:48Z",
+ "config-time":"2016-01-31T09:28:03Z",
+ "current-time":"2016-02-02T22:22:20Z",
+ "opcodes":{
+ "QUERY":247816,
+ "IQUERY":0,
+ "STATUS":0,
+ "RESERVED3":0,
+ "NOTIFY":0,
+ "UPDATE":3813,
+ "RESERVED6":0,
+ "RESERVED7":0,
+ "RESERVED8":0,
+ "RESERVED9":0,
+ "RESERVED10":0,
+ "RESERVED11":0,
+ "RESERVED12":0,
+ "RESERVED13":0,
+ "RESERVED14":0,
+ "RESERVED15":0
+ },
+ "qtypes":{
+ "A":89519,
+ "NS":863,
+ "CNAME":1,
+ "SOA":1,
+ "PTR":116779,
+ "MX":276,
+ "TXT":198,
+ "AAAA":39324,
+ "SRV":850,
+ "ANY":5
+ },
+ "nsstats":{
+ "Requestv4":251630,
+ "ReqEdns0":1255,
+ "ReqTSIG":3813,
+ "ReqTCP":57,
+ "AuthQryRej":1455,
+ "RecQryRej":122,
+ "Response":245918,
+ "TruncatedResp":44,
+ "RespEDNS0":1255,
+ "RespTSIG":3813,
+ "QrySuccess":205159,
+ "QryAuthAns":119495,
+ "QryNoauthAns":120770,
+ "QryNxrrset":32711,
+ "QrySERVFAIL":262,
+ "QryNXDOMAIN":2395,
+ "QryRecursion":40885,
+ "QryDuplicate":5712,
+ "QryFailure":1577,
+ "UpdateDone":2514,
+ "UpdateFail":1299,
+ "UpdateBadPrereq":1276,
+ "QryUDP":246194,
+ "QryTCP":45,
+ "OtherOpt":101
+ },
+ "views":{
+ "local":{
+ "resolver":{
+ "stats":{
+ "Queryv4":74577,
+ "Responsev4":67032,
+ "NXDOMAIN":601,
+ "SERVFAIL":5,
+ "FORMERR":7,
+ "EDNS0Fail":7,
+ "Truncated":3071,
+ "Lame":4,
+ "Retry":11826,
+ "QueryTimeout":1838,
+ "GlueFetchv4":6864,
+ "GlueFetchv4Fail":30,
+ "QryRTT10":112,
+ "QryRTT100":42900,
+ "QryRTT500":23275,
+ "QryRTT800":534,
+ "QryRTT1600":97,
+ "QryRTT1600+":20,
+ "BucketSize":31,
+ "REFUSED":13
+ },
+ "qtypes":{
+ "A":64931,
+ "NS":870,
+ "CNAME":185,
+ "PTR":5,
+ "MX":49,
+ "TXT":149,
+ "AAAA":7972,
+ "SRV":416
+ },
+ "cache":{
+ "A":40356,
+ "NS":8032,
+ "CNAME":14477,
+ "PTR":2,
+ "MX":21,
+ "TXT":32,
+ "AAAA":3301,
+ "SRV":94,
+ "DS":237,
+ "RRSIG":2301,
+ "NSEC":126,
+ "!A":52,
+ "!NS":4,
+ "!TXT":1,
+ "!AAAA":3797,
+ "!SRV":9,
+ "NXDOMAIN":590
+ },
+ "cachestats":{
+ "CacheHits":1085188,
+ "CacheMisses":109,
+ "QueryHits":464755,
+ "QueryMisses":55624,
+ "DeleteLRU":0,
+ "DeleteTTL":42615,
+ "CacheNodes":5188,
+ "CacheBuckets":2079,
+ "TreeMemTotal":2326026,
+ "TreeMemInUse":1508075,
+ "HeapMemMax":132096,
+ "HeapMemTotal":393216,
+ "HeapMemInUse":132096
+ },
+ "adb":{
+ "nentries":1021,
+ "entriescnt":3157,
+ "nnames":1021,
+ "namescnt":3022
+ }
+ }
+ },
+ "public":{
+ "resolver":{
+ "stats":{
+ "BucketSize":31
+ },
+ "qtypes":{
+ },
+ "cache":{
+ },
+ "cachestats":{
+ "CacheHits":0,
+ "CacheMisses":0,
+ "QueryHits":0,
+ "QueryMisses":0,
+ "DeleteLRU":0,
+ "DeleteTTL":0,
+ "CacheNodes":0,
+ "CacheBuckets":64,
+ "TreeMemTotal":287392,
+ "TreeMemInUse":29608,
+ "HeapMemMax":1024,
+ "HeapMemTotal":262144,
+ "HeapMemInUse":1024
+ },
+ "adb":{
+ "nentries":1021,
+ "nnames":1021
+ }
+ }
+ },
+ "_bind":{
+ "resolver":{
+ "stats":{
+ "BucketSize":31
+ },
+ "qtypes":{
+ },
+ "cache":{
+ },
+ "cachestats":{
+ "CacheHits":0,
+ "CacheMisses":0,
+ "QueryHits":0,
+ "QueryMisses":0,
+ "DeleteLRU":0,
+ "DeleteTTL":0,
+ "CacheNodes":0,
+ "CacheBuckets":64,
+ "TreeMemTotal":287392,
+ "TreeMemInUse":29608,
+ "HeapMemMax":1024,
+ "HeapMemTotal":262144,
+ "HeapMemInUse":1024
+ },
+ "adb":{
+ "nentries":1021,
+ "nnames":1021
+ }
+ }
+ }
+ }
+}
+```
+
+
+From this output it collects:
+
+- Global Received Requests by IP version (IPv4, IPv6)
+- Global Successful Queries
+- Current Recursive Clients
+- Global Queries by IP Protocol (TCP, UDP)
+- Global Queries Analysis
+- Global Received Updates
+- Global Query Failures
+- Global Query Failures Analysis
+- Other Global Server Statistics
+- Global Incoming Requests by OpCode
+- Global Incoming Requests by Query Type
+- Global Socket Statistics (will only work if the url is `http://127.0.0.1:8888/json/v1`, i.e. without `/server`, but keep in mind this produces a very long output and probably will account for 0.5% CPU overhead alone, per bind server added)
+- Per View Statistics (the following set will be added for each bind view):
+ - View, Resolver Active Queries
+ - View, Resolver Statistics
+ - View, Resolver Round Trip Timings
+ - View, Requests by Query Type
+
+## Configuration
+
+The collector (optionally) reads a configuration file named `/etc/netdata/node.d/named.conf`, with the following contents:
+
+```js
+{
+ "enable_autodetect": true,
+ "update_every": 5,
+ "servers": [
+ {
+ "name": "bind1",
+ "url": "http://127.0.0.1:8888/json/v1/server",
+ "update_every": 1
+ },
+ {
+ "name": "bind2",
+ "url": "http://10.1.2.3:8888/json/v1/server",
+ "update_every": 2
+ }
+ ]
+}
+```
+
+You can add any number of bind servers.
+
+If the configuration file is missing, or the key `enable_autodetect` is `true`, the collector will also attempt to fetch `http://localhost:8888/json/v1/server` which, if successful will be added too.
+
+### XML instead of JSON, from bind
+
+The collector can also accept bind URLs that return XML output. This might required if you cannot have bind 9.10+ with JSON but you have an version of bind that supports XML statistics v3. Check [this](https://www.isc.org/blogs/bind-9-10-statistics-troubleshooting-and-zone-configuration/) for versions supported.
+
+In such cases, use a URL like this:
+
+```sh
+curl "http://localhost:8888/xml/v3/server"
+```
+
+Only `xml` and `v3` has been tested.
+
+Keep in mind though, that XML parsing is done using javascript code, which requires a triple conversion:
+
+1. from XML to JSON using a javascript XML parser (**CPU intensive**),
+2. which is then transformed to emulate the output of the JSON output of bind (**CPU intensive** - and yes the converted JSON from XML is different to the native JSON - even bind produces different names for various attributes),
+3. which is then processed to generate the data for the charts (this will happen even if bind is producing JSON).
+
+In general, expect XML parsing to be 2 to 3 times more CPU intensive than JSON.
+
+**So, if you can use the JSON output of bind, prefer it over XML**. Keep also in mind that even bind will use more CPU when generating XML instead of JSON.
+
+The XML interface of bind is not autodetected.
+You will have to provide the config file `/etc/netdata/node.d/named.conf`, like this:
+
+```js
+{
+ "enable_autodetect": false,
+ "update_every": 1,
+ "servers": [
+ {
+ "name": "local",
+ "url": "http://localhost:8888/xml/v3/server",
+ "update_every": 1
+ }
+ ]
+}
+```
+
+Of course, you can monitor more than one bind servers. Each one can be configured with either JSON or XML output.
+
+## Auto-detection
+
+Auto-detection is controlled by `enable_autodetect` in the config file. The default is enabled, so that if the collector can connect to `http://localhost:8888/json/v1/server` to receive bind statistics, it will automatically enable it.
+
+## Bind (named) configuration
+
+To use this plugin, you have to have bind v9.10+ properly compiled to provide statistics in `JSON` format.
+
+For more information on how to get your bind installation ready, please refer to the [bind statistics channel developer comments](http://jpmens.net/2013/03/18/json-in-bind-9-s-statistics-server/) and to [bind documentation](https://ftp.isc.org/isc/bind/9.10.3/doc/arm/Bv9ARM.ch06.html#statistics) or [bind Knowledge Base article AA-01123](https://kb.isc.org/article/AA-01123/0).
+
+Normally, you will need something like this in your `named.conf`:
+
+```
+statistics-channels {
+ inet 127.0.0.1 port 8888 allow { 127.0.0.1; };
+ inet ::1 port 8888 allow { ::1; };
+};
+```
+
+(use the IPv4 or IPv6 line depending on what you are using, you can also use both)
+
+Verify it works by running the following command (the collector is written in node.js and will query your bind server directly, but if this command works, the collector should be able to work too):
+
+```sh
+curl "http://localhost:8888/json/v1/server"
+```
+
diff --git a/conf.d/node.d/sma_webbox.conf.md b/conf.d/node.d/sma_webbox.conf.md new file mode 100644 index 000000000..19fdc9dd3 --- /dev/null +++ b/conf.d/node.d/sma_webbox.conf.md @@ -0,0 +1,25 @@ +
+[SMA Sunny Webbox](http://www.solar-is-future.com/sma-technology-for-our-future/products/sunny-webbox/index.html)
+
+Example netdata configuration for node.d/sma_webbox.conf
+
+The module supports any number of name servers, like this:
+
+```json
+{
+ "enable_autodetect": false,
+ "update_every": 5,
+ "servers": [
+ {
+ "name": "plant1",
+ "hostname": "10.0.1.1",
+ "update_every": 10
+ },
+ {
+ "name": "plant2",
+ "hostname": "10.0.2.1",
+ "update_every": 15
+ }
+ ]
+}
+```
diff --git a/conf.d/node.d/snmp.conf.md b/conf.d/node.d/snmp.conf.md new file mode 100644 index 000000000..bae5bf207 --- /dev/null +++ b/conf.d/node.d/snmp.conf.md @@ -0,0 +1,341 @@ +# SNMP Data Collector
+
+Using this collector, netdata can collect data from any SNMP device.
+
+This collector supports:
+
+- any number of SNMP devices
+- each SNMP device can be used to collect data for any number of charts
+- each chart may have any number of dimensions
+- each SNMP device may have a different update frequency
+- each SNMP device will accept one or more batches to report values (you can set `max_request_size` per SNMP server, to control the size of batches).
+
+The source code of the plugin is [here](https://github.com/firehol/netdata/blob/master/node.d/snmp.node.js).
+
+## Configuration
+
+You will need to create the file `/etc/netdata/node.d/snmp.conf` with data like the following.
+
+In this example:
+
+ - the SNMP device is `10.11.12.8`.
+ - the SNMP community is `public`.
+ - we will update the values every 10 seconds (`update_every: 10` under the server `10.11.12.8`).
+ - we define 2 charts `snmp_switch.bandwidth_port1` and `snmp_switch.bandwidth_port2`, each having 2 dimensions: `in` and `out`.
+
+```js
+{
+ "enable_autodetect": false,
+ "update_every": 5,
+ "max_request_size": 100,
+ "servers": [
+ {
+ "hostname": "10.11.12.8",
+ "community": "public",
+ "update_every": 10,
+ "max_request_size": 50,
+ "options": { "timeout": 10000 },
+ "charts": {
+ "snmp_switch.bandwidth_port1": {
+ "title": "Switch Bandwidth for port 1",
+ "units": "kilobits/s",
+ "type": "area",
+ "priority": 1,
+ "family": "ports",
+ "dimensions": {
+ "in": {
+ "oid": "1.3.6.1.2.1.2.2.1.10.1",
+ "algorithm": "incremental",
+ "multiplier": 8,
+ "divisor": 1024
+ },
+ "out": {
+ "oid": "1.3.6.1.2.1.2.2.1.16.1",
+ "algorithm": "incremental",
+ "multiplier": -8,
+ "divisor": 1024
+ }
+ }
+ },
+ "snmp_switch.bandwidth_port2": {
+ "title": "Switch Bandwidth for port 2",
+ "units": "kilobits/s",
+ "type": "area",
+ "priority": 1,
+ "family": "ports",
+ "dimensions": {
+ "in": {
+ "oid": "1.3.6.1.2.1.2.2.1.10.2",
+ "algorithm": "incremental",
+ "multiplier": 8,
+ "divisor": 1024
+ },
+ "out": {
+ "oid": "1.3.6.1.2.1.2.2.1.16.2",
+ "algorithm": "incremental",
+ "multiplier": -8,
+ "divisor": 1024
+ }
+ }
+ }
+ }
+ }
+ ]
+}
+```
+
+`update_every` is the update frequency for each server, in seconds.
+
+`max_request_size` limits the maximum number of OIDs that will be requested in a single call. The default is 50. Lower this number of you get `TooBig` errors in netdata error.log.
+
+`family` sets the name of the submenu of the dashboard each chart will appear under.
+
+If you need to define many charts using incremental OIDs, you can use something like this:
+
+This is like the previous, but the option `multiply_range` given, will multiply the current chart from `1` to `24` inclusive, producing 24 charts in total for the 24 ports of the switch `10.11.12.8`.
+
+Each of the 24 new charts will have its id (1-24) appended at:
+
+1. its chart unique id, i.e. `snmp_switch.bandwidth_port1` to `snmp_switch.bandwidth_port24`
+2. its `title`, i.e. `Switch Bandwidth for port 1` to `Switch Bandwidth for port 24`
+3. its `oid` (for all dimensions), i.e. dimension `in` will be `1.3.6.1.2.1.2.2.1.10.1` to `1.3.6.1.2.1.2.2.1.10.24`
+3. its priority (which will be incremented for each chart so that the charts will appear on the dashboard in this order)
+
+```js
+{
+ "enable_autodetect": false,
+ "update_every": 10,
+ "servers": [
+ {
+ "hostname": "10.11.12.8",
+ "community": "public",
+ "update_every": 10,
+ "options": { "timeout": 20000 },
+ "charts": {
+ "snmp_switch.bandwidth_port": {
+ "title": "Switch Bandwidth for port ",
+ "units": "kilobits/s",
+ "type": "area",
+ "priority": 1,
+ "family": "ports",
+ "multiply_range": [ 1, 24 ],
+ "dimensions": {
+ "in": {
+ "oid": "1.3.6.1.2.1.2.2.1.10.",
+ "algorithm": "incremental",
+ "multiplier": 8,
+ "divisor": 1024
+ },
+ "out": {
+ "oid": "1.3.6.1.2.1.2.2.1.16.",
+ "algorithm": "incremental",
+ "multiplier": -8,
+ "divisor": 1024
+ }
+ }
+ }
+ }
+ }
+ ]
+}
+```
+
+The `options` given for each server, are:
+
+ - `timeout`, the time to wait for the SNMP device to respond. The default is 5000 ms.
+ - `version`, the SNMP version to use. `0` is Version 1, `1` is Version 2c. The default is Version 1 (`0`).
+ - `transport`, the default is `udp4`.
+ - `port`, the port of the SNMP device to connect to. The default is `161`.
+ - `retries`, the number of attempts to make to fetch the data. The default is `1`.
+
+## Retreiving names from snmp
+
+You can append a value retrieved from SNMP to the title, by adding `titleoid` to the chart.
+
+You can set a dimension name to a value retrieved from SNMP, by adding `oidname` to the dimension.
+
+Both of the above will participate in `multiply_range`.
+
+
+## Testing the configuration
+
+To test it, you can run:
+
+```sh
+/usr/libexec/netdata/plugins.d/node.d.plugin 1 snmp
+```
+
+The above will run it on your console and you will be able to see what netdata sees, but also errors. You can get a very detailed output by appending `debug` to the command line.
+
+If it works, restart netdata to activate the snmp collector and refresh the dashboard (if your SNMP device responds with a delay, you may need to refresh the dashboard in a few seconds).
+
+## Data collection speed
+
+Keep in mind that many SNMP switches are routers are very slow. They may not be able to report values per second. If you run `node.d.plugin` in `debug` mode, it will report the time it took for the SNMP device to respond. My switch, for example, needs 7-8 seconds to respond for the traffic on 24 ports (48 OIDs, in/out).
+
+Also, if you use many SNMP clients on the same SNMP device at the same time, values may be skipped. This is a problem of the SNMP device, not this collector.
+
+## Finding OIDs
+
+Use `snmpwalk`, like this:
+
+```sh
+snmpwalk -t 20 -v 1 -O fn -c public 10.11.12.8
+```
+
+- `-t 20` is the timeout in seconds
+- `-v 1` is the SNMP version
+- `-O fn` will display full OIDs in numeric format (you may want to run it also without this option to see human readable output of OIDs)
+- `-c public` is the SNMP community
+- `10.11.12.8` is the SNMP device
+
+Keep in mind that `snmpwalk` outputs the OIDs with a dot in front them. You should remove this dot when adding OIDs to the configuration file of this collector.
+
+## Example: Linksys SRW2024P
+
+This is what I use for my Linksys SRW2024P. It creates:
+
+1. A chart for power consumption (it is a PoE switch)
+2. Two charts for packets received (total packets received and packets received with errors)
+3. One chart for packets output
+4. 24 charts, one for each port of the switch. It also appends the port names, as defined at the switch, to the chart titles.
+
+This switch also reports various other metrics, like snmp, packets per port, etc. Unfortunately it does not report CPU utilization or backplane utilization.
+
+This switch has a very slow SNMP processors. To respond, it needs about 8 seconds, so I have set the refresh frequency (`update_every`) to 15 seconds.
+
+```js
+{
+ "enable_autodetect": false,
+ "update_every": 5,
+ "servers": [
+ {
+ "hostname": "10.11.12.8",
+ "community": "public",
+ "update_every": 15,
+ "options": { "timeout": 20000, "version": 1 },
+ "charts": {
+ "snmp_switch.power": {
+ "title": "Switch Power Supply",
+ "units": "watts",
+ "type": "line",
+ "priority": 10,
+ "family": "power",
+ "dimensions": {
+ "supply": {
+ "oid": ".1.3.6.1.2.1.105.1.3.1.1.2.1",
+ "algorithm": "absolute",
+ "multiplier": 1,
+ "divisor": 1
+ },
+ "used": {
+ "oid": ".1.3.6.1.2.1.105.1.3.1.1.4.1",
+ "algorithm": "absolute",
+ "multiplier": 1,
+ "divisor": 1
+ }
+ }
+ }
+ , "snmp_switch.input": {
+ "title": "Switch Packets Input",
+ "units": "packets/s",
+ "type": "area",
+ "priority": 20,
+ "family": "IP",
+ "dimensions": {
+ "receives": {
+ "oid": ".1.3.6.1.2.1.4.3.0",
+ "algorithm": "incremental",
+ "multiplier": 1,
+ "divisor": 1
+ }
+ , "discards": {
+ "oid": ".1.3.6.1.2.1.4.8.0",
+ "algorithm": "incremental",
+ "multiplier": 1,
+ "divisor": 1
+ }
+ }
+ }
+ , "snmp_switch.input_errors": {
+ "title": "Switch Received Packets with Errors",
+ "units": "packets/s",
+ "type": "line",
+ "priority": 30,
+ "family": "IP",
+ "dimensions": {
+ "bad_header": {
+ "oid": ".1.3.6.1.2.1.4.4.0",
+ "algorithm": "incremental",
+ "multiplier": 1,
+ "divisor": 1
+ }
+ , "bad_address": {
+ "oid": ".1.3.6.1.2.1.4.5.0",
+ "algorithm": "incremental",
+ "multiplier": 1,
+ "divisor": 1
+ }
+ , "unknown_protocol": {
+ "oid": ".1.3.6.1.2.1.4.7.0",
+ "algorithm": "incremental",
+ "multiplier": 1,
+ "divisor": 1
+ }
+ }
+ }
+ , "snmp_switch.output": {
+ "title": "Switch Output Packets",
+ "units": "packets/s",
+ "type": "line",
+ "priority": 40,
+ "family": "IP",
+ "dimensions": {
+ "requests": {
+ "oid": ".1.3.6.1.2.1.4.10.0",
+ "algorithm": "incremental",
+ "multiplier": 1,
+ "divisor": 1
+ }
+ , "discards": {
+ "oid": ".1.3.6.1.2.1.4.11.0",
+ "algorithm": "incremental",
+ "multiplier": -1,
+ "divisor": 1
+ }
+ , "no_route": {
+ "oid": ".1.3.6.1.2.1.4.12.0",
+ "algorithm": "incremental",
+ "multiplier": -1,
+ "divisor": 1
+ }
+ }
+ }
+ , "snmp_switch.bandwidth_port": {
+ "title": "Switch Bandwidth for port ",
+ "titleoid": ".1.3.6.1.2.1.31.1.1.1.18.",
+ "units": "kilobits/s",
+ "type": "area",
+ "priority": 100,
+ "family": "ports",
+ "multiply_range": [ 1, 24 ],
+ "dimensions": {
+ "in": {
+ "oid": ".1.3.6.1.2.1.2.2.1.10.",
+ "algorithm": "incremental",
+ "multiplier": 8,
+ "divisor": 1024
+ }
+ , "out": {
+ "oid": ".1.3.6.1.2.1.2.2.1.16.",
+ "algorithm": "incremental",
+ "multiplier": -8,
+ "divisor": 1024
+ }
+ }
+ }
+ }
+ }
+ ]
+}
+```
diff --git a/conf.d/python.d.conf b/conf.d/python.d.conf index 940bd9183..7e4fa801f 100644 --- a/conf.d/python.d.conf +++ b/conf.d/python.d.conf @@ -30,6 +30,7 @@ example: no # exim: yes # hddtemp: yes # ipfs: yes +# isc_dhcpd: yes # memcached: yes # mysql: yes # nginx: yes @@ -40,3 +41,5 @@ example: no # sensors: yes # squid: yes # tomcat: yes +# freeradius: yes +# ovpn_status_log: yes diff --git a/conf.d/python.d/bind_rndc.conf b/conf.d/python.d/bind_rndc.conf new file mode 100644 index 000000000..e4f7ac825 --- /dev/null +++ b/conf.d/python.d/bind_rndc.conf @@ -0,0 +1,109 @@ +# netdata python.d.plugin configuration for bind_rndc +# +# 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, bind_rndc also supports the following: +# +# named_stats_path: 'path to named.stats' # Default: '/var/log/bind/named.stats' +#------------------------------------------------------------------------------------------------------------------ +# IMPORTANT Information +# +# BIND APPEND logs at EVERY RUN. Its NOT RECOMMENDED to set update_every below 30 sec. +# STRONGLY RECOMMENDED to create a bind-rndc conf file for logrotate +# +# To set up your BIND to dump stats do the following: +# +# 1. add to 'named.conf.options' options {}: +# statistics-file "/var/log/bind/named.stats"; +# +# 2. Create bind/ directory in /var/log +# cd /var/log/ && mkdir bind +# +# 3. Change owner of directory to 'bind' user +# chown bind bind/ +# +# 4. RELOAD (NOT restart) BIND +# systemctl reload bind9.serice +# +# 5. Run as a root 'rndc stats' to dump (BIND will create named.stats in new directory) +# +# +# To ALLOW NETDATA TO RUN 'rndc stats' change '/etc/bind/rndc.key' group to netdata +# chown :netdata rndc.key +# +# The last BUT NOT least is to create bind-rndc.conf in logrotate.d/ +# The working one +# /var/log/bind/named.stats { +# +# daily +# rotate 4 +# compress +# delaycompress +# create 0644 bind bind +# missingok +# postrotate +# rndc reload > /dev/null +# endscript +# } +# +# To test your logrotate conf file run as root: +# +# logrotate /etc/logrotate.d/bind-rndc -d (debug dry-run mode) +# ------------------------------------------------------------------------------------------------------------------ +# AUTO-DETECTION JOBS +# only one of them will run (they have the same name) +# +#local: +# named_stats_path: '/var/log/bind/named.stats' diff --git a/conf.d/python.d/elasticsearch.conf b/conf.d/python.d/elasticsearch.conf new file mode 100644 index 000000000..1faee8582 --- /dev/null +++ b/conf.d/python.d/elasticsearch.conf @@ -0,0 +1,72 @@ +# netdata python.d.plugin configuration for elasticsearch stats +# +# 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, elasticsearch plugin also supports the following: +# +# host: 'ipaddress' # Server ip address or hostname. +# port: 'port' # Port on which elasticsearch listen. +# cluster_health: False/True # Calls to cluster health elasticsearch API. Enabled by default. +# cluster_stats: False/True # Calls to cluster stats elasticsearch API. Enabled by default. +# +# ---------------------------------------------------------------------- +# AUTO-DETECTION JOBS +# only one of them will run (they have the same name) +# +#local: +# host: '127.0.0.1' +# port: '9200' +# cluster_health: True +# cluster_stats: True diff --git a/conf.d/python.d/exim.conf b/conf.d/python.d/exim.conf index 6aca13c34..07d72c5a3 100644 --- a/conf.d/python.d/exim.conf +++ b/conf.d/python.d/exim.conf @@ -1,4 +1,4 @@ -# netdata python.d.plugin configuration for postfix +# netdata python.d.plugin configuration for exim # # This file is in YaML format. Generally the format is: # @@ -55,7 +55,7 @@ update_every: 10 # priority: 60000 # the JOB's order on the dashboard # retries: 5 # the JOB's number of restoration attempts # -# Additionally to the above, postfix also supports the following: +# Additionally to the above, exim also supports the following: # # command: 'exim -bpc' # the command to run # diff --git a/conf.d/python.d/fail2ban.conf b/conf.d/python.d/fail2ban.conf new file mode 100644 index 000000000..cd805be8d --- /dev/null +++ b/conf.d/python.d/fail2ban.conf @@ -0,0 +1,77 @@ +# netdata python.d.plugin configuration for fail2ban +# +# 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, fail2ban also supports the following: +# +# log_path: 'path to fail2ban.log' # Default: '/var/log/fail2ban.log' +# conf_path: 'path to jail.local/jail.conf' # Default: '/etc/fail2ban/jail.local' +# exclude: 'jails you want to exclude from autodetection' # Default: '[]' empty list +#------------------------------------------------------------------------------------------------------------------ +# IMPORTANT Information +# +# fail2ban.log file MUST BE readable by netdata. +# A good idea is to do this by adding the +# # create 0640 root netdata +# to fail2ban conf at logrotate.d +# +# ------------------------------------------------------------------------------------------------------------------ +# AUTO-DETECTION JOBS +# only one of them will run (they have the same name) + +#local: +# log_path: '/var/log/fail2ban.log' +# conf_path: '/etc/fail2ban/jail.local' +# exclude: 'dropbear apache' diff --git a/conf.d/python.d/freeradius.conf b/conf.d/python.d/freeradius.conf new file mode 100644 index 000000000..b2c8abf6b --- /dev/null +++ b/conf.d/python.d/freeradius.conf @@ -0,0 +1,86 @@ +# netdata python.d.plugin configuration for freeradius +# +# 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, freeradius also supports the following: +# +# host: 'host' # Default: 'localhost'. Server ip address or hostname. +# port: 'port' # Default: '18121'. Port on which freeradius server listen (type = status). +# secret: 'secret' # Default: 'adminsecret'. +# acct: True/False # Defalt: False. Freeradius accounting statistics. +# proxy_auth: True/False # Default: False. Freeradius proxy authentication statistics. +# proxy_acct: True/False # Default: False. Freeradius proxy accounting statistics. +# +# ------------------------------------------------------------------------------------------------------------------ +# Freeradius server configuration: +# The configuration for the status server is automatically created in the sites-available directory. +# By default, server is enabled and can be queried from every client. +# FreeRADIUS will only respond to status-server messages, if the status-server virtual server has been enabled. +# To do this, create a link from the sites-enabled directory to the status file in the sites-available directory: +# cd sites-enabled +# ln -s ../sites-available/status status +# and restart/reload your FREERADIUS server. +# ------------------------------------------------------------------------------------------------------------------ +# +# AUTO-DETECTION JOBS +# only one of them will run (they have the same name) + +local: + host: 'localhost' + port: '18121' + secret: 'adminsecret' +#acct: False +#proxy_auth: False +#proxy_acct: False diff --git a/conf.d/python.d/gunicorn_log.conf b/conf.d/python.d/gunicorn_log.conf new file mode 100644 index 000000000..8fea483f0 --- /dev/null +++ b/conf.d/python.d/gunicorn_log.conf @@ -0,0 +1,73 @@ +# netdata python.d.plugin configuration for nginx gunicorn log +# +# 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, gunicorn_log also supports the following: +# +# path: 'PATH' # the path to gunicorn's access.log +# + +# ---------------------------------------------------------------------- +# AUTO-DETECTION JOBS +# only one of them will run (they have the same name) + +gunicorn_log: + name: 'local' + path: '/var/log/gunicorn/access.log' + +gunicorn_log2: + name: 'local' + path: '/var/log/gunicorn/gunicorn-access.log' + diff --git a/conf.d/python.d/haproxy.conf b/conf.d/python.d/haproxy.conf new file mode 100644 index 000000000..a9e048791 --- /dev/null +++ b/conf.d/python.d/haproxy.conf @@ -0,0 +1,78 @@ +# netdata python.d.plugin configuration for haproxy +# +# 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, haproxy also supports the following: +# +# IMPORTANT: socket MUST BE readable AND writable by netdata user +# +# socket: 'path/to/haproxy/sock' +# +# OR +# url: 'http://<ip.address>:<port>/<url>;csv;norefresh' +# [user: USERNAME] only if stats auth is used +# [pass: PASSWORD] only if stats auth is used + +# ---------------------------------------------------------------------- +# AUTO-DETECTION JOBS +# only one of them will run (they have the same name) + +#via_url: +# user : 'admin' +# pass : 'password' +# url : 'http://127.0.0.1:7000/haproxy_stats;csv;norefresh' + +#via_socket: +# socket: '/var/run/haproxy/admin.sock' diff --git a/conf.d/python.d/hddtemp.conf b/conf.d/python.d/hddtemp.conf index 0c78449b4..f74a09803 100644 --- a/conf.d/python.d/hddtemp.conf +++ b/conf.d/python.d/hddtemp.conf @@ -58,6 +58,7 @@ # # host: 'IP or HOSTNAME' # the host to connect to # port: PORT # the port to connect to +# fahrenheit: True/False # fahrenheit instead of celsius. Default is False # # By default this module will try to autodetect disks @@ -77,11 +78,13 @@ localhost: name: 'local' host: 'localhost' + fahrenheit: False port: 7634 localipv4: name: 'local' host: '127.0.0.1' + fahrenheit: False port: 7634 localipv6: diff --git a/conf.d/python.d/isc_dhcpd.conf b/conf.d/python.d/isc_dhcpd.conf new file mode 100644 index 000000000..7c8fe3ceb --- /dev/null +++ b/conf.d/python.d/isc_dhcpd.conf @@ -0,0 +1,78 @@ +# netdata python.d.plugin configuration for isc dhcpd leases +# +# 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, isc_dhcpd supports the following: +# +# leases_path: 'PATH' # the path to dhcpd.leases file +# pools: 'dhcpd pools list' # Pools in CIDR format +# +#----------------------------------------------------------------------- +# IMPORTANT notes +# +# 1. Make sure leases file is readable by netdata. +# 2. Current implementation works only with 'default' db-time-format +# (weekday year/month/day hour:minute:second). +# This is the default, so it will work in most cases. +# 3. Pools MUST BE in CIDR format. +# +#----------------------------------------------------------------------- +# AUTO-DETECTION JOBS +# This is disabled by default. +# To enable it, uncomment the following. +# +#leases: +# leases_path : '/var/lib/dhcp/dhcpd.leases' +# pools : '192.168.3.0/24 192.168.4.0/24 192.168.5.0/24' diff --git a/conf.d/python.d/mdstat.conf b/conf.d/python.d/mdstat.conf new file mode 100644 index 000000000..c89d463be --- /dev/null +++ b/conf.d/python.d/mdstat.conf @@ -0,0 +1,26 @@ +# netdata python.d.plugin configuration for mdstat +# +# This file is in YaML format. Generally the format is: +# +# name: value +# + +# ---------------------------------------------------------------------- +# 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 diff --git a/conf.d/python.d/mysql.conf b/conf.d/python.d/mysql.conf index 8fbbe6513..63d635174 100644 --- a/conf.d/python.d/mysql.conf +++ b/conf.d/python.d/mysql.conf @@ -69,6 +69,23 @@ # # ---------------------------------------------------------------------- +# mySQL CONFIGURATION +# +# netdata does not need any privilege - only the ability to connect +# to the mysql server (netdata will not be able to see any data). +# +# Execute these commands to give the local user 'netdata' the ability +# to connect to the mysql server on localhost, without a password: +# +# > create user 'netdata'@'localhost'; +# > grant usage on *.* to 'netdata'@'localhost' with grant option; +# > flush privileges; +# +# with the above statements, netdata will be able to gather mysql +# statistics, without the ability to see or alter any data or affect +# mysql operation in any way. No change is required below. + +# ---------------------------------------------------------------------- # AUTO-DETECTION JOBS # only one of them will run (they have the same name) @@ -80,6 +97,10 @@ mycnf2: name : 'local' 'my.cnf' : '/etc/mysql/my.cnf' +debiancnf: + name : 'local' + 'my.cnf' : '/etc/mysql/debian.cnf' + socket1: name : 'local' # user : '' @@ -90,12 +111,18 @@ socket2: name : 'local' # user : '' # pass : '' - socket : '/var/lib/mysql/mysql.sock' + socket : '/var/run/mysqld/mysql.sock' socket3: name : 'local' # user : '' # pass : '' + socket : '/var/lib/mysql/mysql.sock' + +socket4: + name : 'local' + # user : '' + # pass : '' socket : '/tmp/mysql.sock' tcp: @@ -146,12 +173,18 @@ socket2_root: name : 'local' user : 'root' # pass : '' - socket : '/var/lib/mysql/mysql.sock' + socket : '/var/run/mysqld/mysql.sock' socket3_root: name : 'local' user : 'root' # pass : '' + socket : '/var/lib/mysql/mysql.sock' + +socket4_root: + name : 'local' + user : 'root' + # pass : '' socket : '/tmp/mysql.sock' tcp_root: @@ -177,3 +210,63 @@ tcpipv6_root: host : '::1' port : '3306' + +# Now we try the same as above with user: netdata + +mycnf1_netdata: + name : 'local' + user : 'netdata' + 'my.cnf' : '/etc/my.cnf' + +mycnf2_netdata: + name : 'local' + user : 'netdata' + 'my.cnf' : '/etc/mysql/my.cnf' + +socket1_netdata: + name : 'local' + user : 'netdata' + # pass : '' + socket : '/var/run/mysqld/mysqld.sock' + +socket2_netdata: + name : 'local' + user : 'netdata' + # pass : '' + socket : '/var/run/mysqld/mysql.sock' + +socket3_netdata: + name : 'local' + user : 'netdata' + # pass : '' + socket : '/var/lib/mysql/mysql.sock' + +socket4_netdata: + name : 'local' + user : 'netdata' + # pass : '' + socket : '/tmp/mysql.sock' + +tcp_netdata: + name : 'local' + user : 'netdata' + # pass : '' + host : 'localhost' + port : '3306' + # keep in mind port might be ignored by mysql, if host = 'localhost' + # http://serverfault.com/questions/337818/how-to-force-mysql-to-connect-by-tcp-instead-of-a-unix-socket/337844#337844 + +tcpipv4_netdata: + name : 'local' + user : 'netdata' + # pass : '' + host : '127.0.0.1' + port : '3306' + +tcpipv6_netdata: + name : 'local' + user : 'netdata' + # pass : '' + host : '::1' + port : '3306' + diff --git a/conf.d/python.d/ovpn_status_log.conf b/conf.d/python.d/ovpn_status_log.conf new file mode 100644 index 000000000..39bc8e9d4 --- /dev/null +++ b/conf.d/python.d/ovpn_status_log.conf @@ -0,0 +1,86 @@ +# netdata python.d.plugin configuration for openvpn status log +# +# 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, openvpn status log also supports the following: +# +# log_path: 'PATH' # the path to openvpn status log file +# +# ---------------------------------------------------------------------- +# AUTO-DETECTION JOBS +# only one of them will run (they have the same name) +# +# IMPORTANT information +# +# 1. If you are running multiple OpenVPN instances out of the same directory, MAKE SURE TO EDIT DIRECTIVES which create output files +# so that multiple instances do not overwrite each other's output files. +# 2. Make sure NETDATA USER CAN READ openvpn-status.log +# +# * cd into directory with openvpn-status.log and run the following commands as root +# * #chown :netdata openvpn-status.log && chmod 640 openvpn-status.log +# * To check permission and group membership run +# * #ls -l openvpn-status.log +# -rw-r----- 1 root netdata 359 dec 21 21:22 openvpn-status.log +# +# 3. Update_every interval MUST MATCH interval on which OpenVPN writes operational status to log file. +# If its not true traffic chart WILL DISPLAY WRONG values +# +# Default OpenVPN update interval is 10 second on Debian 8 +# # ps -C openvpn -o command= +# /usr/sbin/openvpn --daemon ovpn-server --status /run/openvpn/server.status 10 --cd /etc/openvpn --config /etc/openvpn/server.conf +# +# +#default: +# log_path: '/var/log/openvpn-status.log' diff --git a/conf.d/python.d/postgres.conf b/conf.d/python.d/postgres.conf new file mode 100644 index 000000000..d4d2bafcc --- /dev/null +++ b/conf.d/python.d/postgres.conf @@ -0,0 +1,104 @@ +# netdata python.d.plugin configuration for postgresql +# +# 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 +# +# A single connection is required in order to pull statistics. +# +# Connections can be configured with the following options: +# +# database : 'example_db_name' +# user : 'example_user' +# password : 'example_pass' +# host : 'localhost' +# port : 5432 +# +# Additionally, the following options allow selective disabling of charts +# +# table_stats : false +# index_stats : false +# +# Postfix permissions are configured at its pg_hba.conf file. You can +# "trust" local clients to allow netdata to connect, or you can create +# a postgres user for netdata and add its password below to allow +# netdata connect. +# +# ---------------------------------------------------------------------- + +socket: + name : 'local' + user : 'postgres' + database : 'postgres' + +tcp: + name : 'local' + database : 'postgres' + user : 'postgres' + host : 'localhost' + port : 5432 + +tcpipv4: + name : 'local' + database : 'postgres' + user : 'postgres' + host : '127.0.0.1' + port : 5432 + +tcpipv6: + name : 'local' + database : 'postgres' + user : 'postgres' + host : '::1' + port : 5432 + diff --git a/conf.d/python.d/redis.conf b/conf.d/python.d/redis.conf index 9935bff77..983fbfbdb 100644 --- a/conf.d/python.d/redis.conf +++ b/conf.d/python.d/redis.conf @@ -56,12 +56,14 @@ # # Additionally to the above, redis also supports the following: # -# socket: 'path/to/mysql.sock' +# socket: 'path/to/redis.sock' # # or # host: 'IP or HOSTNAME' # the host to connect to # port: PORT # the port to connect to # +# and +# pass: 'password' # the redis password to use for AUTH command # # ---------------------------------------------------------------------- @@ -71,27 +73,33 @@ socket1: name : 'local' socket : '/tmp/redis.sock' + # pass : '' socket2: name : 'local' socket : '/var/run/redis/redis.sock' + # pass : '' socket3: name : 'local' socket : '/var/lib/redis/redis.sock' + # pass : '' localhost: name : 'local' host : 'localhost' port : 6379 + # pass : '' localipv4: name : 'local' host : '127.0.0.1' port : 6379 + # pass : '' localipv6: name : 'local' host : '::1' port : 6379 + # pass : '' diff --git a/conf.d/python.d/sensors.conf b/conf.d/python.d/sensors.conf index 7d895c348..2e9a41338 100644 --- a/conf.d/python.d/sensors.conf +++ b/conf.d/python.d/sensors.conf @@ -52,3 +52,7 @@ types: # # chip names can be found using the sensors shell command # the prefix is matched (anything that starts like that) +# +#---------------------------------------------------------------------- +# To change celsius to fahrenheit uncomment line below +#fahrenheit: True diff --git a/conf.d/python.d/varnish.conf b/conf.d/python.d/varnish.conf new file mode 100644 index 000000000..56dc6334d --- /dev/null +++ b/conf.d/python.d/varnish.conf @@ -0,0 +1,65 @@ +# netdata python.d.plugin configuration for varnish +# +# 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 +# +# +# +# The only you need is to add netdata to 'varnish' group +# +# Check it from cmd +# id netdata +# +# uid=999(netdata) gid=999(netdata) группы=999(netdata),118(varnish) +# diff --git a/config.guess b/config.guess index b79252d6b..a2eeec38e 100755 --- a/config.guess +++ b/config.guess @@ -1,8 +1,8 @@ #! /bin/sh # Attempt to guess a canonical system name. -# Copyright 1992-2013 Free Software Foundation, Inc. +# Copyright 1992-2016 Free Software Foundation, Inc. -timestamp='2013-06-10' +timestamp='2016-04-02' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by @@ -24,12 +24,12 @@ timestamp='2013-06-10' # program. This Exception is an additional permission under section 7 # of the GNU General Public License, version 3 ("GPLv3"). # -# Originally written by Per Bothner. +# Originally written by Per Bothner; maintained since 2000 by Ben Elliston. # # You can get the latest version of this script from: -# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD +# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess # -# Please send patches with a ChangeLog entry to config-patches@gnu.org. +# Please send patches to <config-patches@gnu.org>. me=`echo "$0" | sed -e 's,.*/,,'` @@ -50,7 +50,7 @@ version="\ GNU config.guess ($timestamp) Originally written by Per Bothner. -Copyright 1992-2013 Free Software Foundation, Inc. +Copyright 1992-2016 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." @@ -149,7 +149,7 @@ Linux|GNU|GNU/*) LIBC=gnu #endif EOF - eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC'` + eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC' | sed 's, ,,g'` ;; esac @@ -168,20 +168,27 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in # Note: NetBSD doesn't particularly care about the vendor # portion of the name. We always set it to "unknown". sysctl="sysctl -n hw.machine_arch" - UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \ - /usr/sbin/$sysctl 2>/dev/null || echo unknown)` + UNAME_MACHINE_ARCH=`(uname -p 2>/dev/null || \ + /sbin/$sysctl 2>/dev/null || \ + /usr/sbin/$sysctl 2>/dev/null || \ + echo unknown)` case "${UNAME_MACHINE_ARCH}" in armeb) machine=armeb-unknown ;; arm*) machine=arm-unknown ;; sh3el) machine=shl-unknown ;; sh3eb) machine=sh-unknown ;; sh5el) machine=sh5le-unknown ;; + earmv*) + arch=`echo ${UNAME_MACHINE_ARCH} | sed -e 's,^e\(armv[0-9]\).*$,\1,'` + endian=`echo ${UNAME_MACHINE_ARCH} | sed -ne 's,^.*\(eb\)$,\1,p'` + machine=${arch}${endian}-unknown + ;; *) machine=${UNAME_MACHINE_ARCH}-unknown ;; esac # The Operating System including object format, if it has switched # to ELF recently, or will in the future. case "${UNAME_MACHINE_ARCH}" in - arm*|i386|m68k|ns32k|sh3*|sparc|vax) + arm*|earm*|i386|m68k|ns32k|sh3*|sparc|vax) eval $set_cc_for_build if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ELF__ @@ -197,6 +204,13 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in os=netbsd ;; esac + # Determine ABI tags. + case "${UNAME_MACHINE_ARCH}" in + earm*) + expr='s/^earmv[0-9]/-eabi/;s/eb$//' + abi=`echo ${UNAME_MACHINE_ARCH} | sed -e "$expr"` + ;; + esac # The OS release # Debian GNU/NetBSD machines have a different userland, and # thus, need a distinct triplet. However, they do not need @@ -207,13 +221,13 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in release='-gnu' ;; *) - release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'` + release=`echo ${UNAME_RELEASE} | sed -e 's/[-_].*//' | cut -d. -f1,2` ;; esac # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: # contains redundant information, the shorter form: # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. - echo "${machine}-${os}${release}" + echo "${machine}-${os}${release}${abi}" exit ;; *:Bitrig:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'` @@ -223,6 +237,10 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'` echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE} exit ;; + *:LibertyBSD:*:*) + UNAME_MACHINE_ARCH=`arch | sed 's/^.*BSD\.//'` + echo ${UNAME_MACHINE_ARCH}-unknown-libertybsd${UNAME_RELEASE} + exit ;; *:ekkoBSD:*:*) echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE} exit ;; @@ -235,6 +253,9 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in *:MirBSD:*:*) echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE} exit ;; + *:Sortix:*:*) + echo ${UNAME_MACHINE}-unknown-sortix + exit ;; alpha:OSF1:*:*) case $UNAME_RELEASE in *4.0) @@ -251,42 +272,42 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1` case "$ALPHA_CPU_TYPE" in "EV4 (21064)") - UNAME_MACHINE="alpha" ;; + UNAME_MACHINE=alpha ;; "EV4.5 (21064)") - UNAME_MACHINE="alpha" ;; + UNAME_MACHINE=alpha ;; "LCA4 (21066/21068)") - UNAME_MACHINE="alpha" ;; + UNAME_MACHINE=alpha ;; "EV5 (21164)") - UNAME_MACHINE="alphaev5" ;; + UNAME_MACHINE=alphaev5 ;; "EV5.6 (21164A)") - UNAME_MACHINE="alphaev56" ;; + UNAME_MACHINE=alphaev56 ;; "EV5.6 (21164PC)") - UNAME_MACHINE="alphapca56" ;; + UNAME_MACHINE=alphapca56 ;; "EV5.7 (21164PC)") - UNAME_MACHINE="alphapca57" ;; + UNAME_MACHINE=alphapca57 ;; "EV6 (21264)") - UNAME_MACHINE="alphaev6" ;; + UNAME_MACHINE=alphaev6 ;; "EV6.7 (21264A)") - UNAME_MACHINE="alphaev67" ;; + UNAME_MACHINE=alphaev67 ;; "EV6.8CB (21264C)") - UNAME_MACHINE="alphaev68" ;; + UNAME_MACHINE=alphaev68 ;; "EV6.8AL (21264B)") - UNAME_MACHINE="alphaev68" ;; + UNAME_MACHINE=alphaev68 ;; "EV6.8CX (21264D)") - UNAME_MACHINE="alphaev68" ;; + UNAME_MACHINE=alphaev68 ;; "EV6.9A (21264/EV69A)") - UNAME_MACHINE="alphaev69" ;; + UNAME_MACHINE=alphaev69 ;; "EV7 (21364)") - UNAME_MACHINE="alphaev7" ;; + UNAME_MACHINE=alphaev7 ;; "EV7.9 (21364A)") - UNAME_MACHINE="alphaev79" ;; + UNAME_MACHINE=alphaev79 ;; esac # A Pn.n version is a patched version. # A Vn.n version is a released version. # A Tn.n version is a released field test version. # A Xn.n version is an unreleased experimental baselevel. # 1.2 uses "1.2" for uname -r. - echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` + echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz` # Reset EXIT trap before exiting to avoid spurious non-zero exit code. exitcode=$? trap '' 0 @@ -359,16 +380,16 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in exit ;; i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*) eval $set_cc_for_build - SUN_ARCH="i386" + SUN_ARCH=i386 # If there is a compiler, see if it is configured for 64-bit objects. # Note that the Sun cc does not turn __LP64__ into 1 like gcc does. # This test works for both compilers. - if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then + if [ "$CC_FOR_BUILD" != no_compiler_found ]; then if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \ - (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ + (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_64BIT_ARCH >/dev/null then - SUN_ARCH="x86_64" + SUN_ARCH=x86_64 fi fi echo ${SUN_ARCH}-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` @@ -393,7 +414,7 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in exit ;; sun*:*:4.2BSD:*) UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` - test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3 + test "x${UNAME_RELEASE}" = x && UNAME_RELEASE=3 case "`/bin/arch`" in sun3) echo m68k-sun-sunos${UNAME_RELEASE} @@ -579,8 +600,9 @@ EOF else IBM_ARCH=powerpc fi - if [ -x /usr/bin/oslevel ] ; then - IBM_REV=`/usr/bin/oslevel` + if [ -x /usr/bin/lslpp ] ; then + IBM_REV=`/usr/bin/lslpp -Lqc bos.rte.libc | + awk -F: '{ print $3 }' | sed s/[0-9]*$/0/` else IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} fi @@ -617,13 +639,13 @@ EOF sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` case "${sc_cpu_version}" in - 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0 - 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1 + 523) HP_ARCH=hppa1.0 ;; # CPU_PA_RISC1_0 + 528) HP_ARCH=hppa1.1 ;; # CPU_PA_RISC1_1 532) # CPU_PA_RISC2_0 case "${sc_kernel_bits}" in - 32) HP_ARCH="hppa2.0n" ;; - 64) HP_ARCH="hppa2.0w" ;; - '') HP_ARCH="hppa2.0" ;; # HP-UX 10.20 + 32) HP_ARCH=hppa2.0n ;; + 64) HP_ARCH=hppa2.0w ;; + '') HP_ARCH=hppa2.0 ;; # HP-UX 10.20 esac ;; esac fi @@ -662,11 +684,11 @@ EOF exit (0); } EOF - (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy` + (CCOPTS="" $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy` test -z "$HP_ARCH" && HP_ARCH=hppa fi ;; esac - if [ ${HP_ARCH} = "hppa2.0w" ] + if [ ${HP_ARCH} = hppa2.0w ] then eval $set_cc_for_build @@ -679,12 +701,12 @@ EOF # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess # => hppa64-hp-hpux11.23 - if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | + if echo __LP64__ | (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | grep -q __LP64__ then - HP_ARCH="hppa2.0w" + HP_ARCH=hppa2.0w else - HP_ARCH="hppa64" + HP_ARCH=hppa64 fi fi echo ${HP_ARCH}-hp-hpux${HPUX_REV} @@ -789,14 +811,14 @@ EOF echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) - FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` - FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` + FUJITSU_PROC=`uname -m | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz` + FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'` FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'` echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" exit ;; 5000:UNIX_System_V:4.*:*) - FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` - FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'` + FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'` + FUJITSU_REL=`echo ${UNAME_RELEASE} | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/ /_/'` echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" exit ;; i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) @@ -826,7 +848,7 @@ EOF *:MINGW*:*) echo ${UNAME_MACHINE}-pc-mingw32 exit ;; - i*:MSYS*:*) + *:MSYS*:*) echo ${UNAME_MACHINE}-pc-msys exit ;; i*:windows32*:*) @@ -878,7 +900,7 @@ EOF exit ;; *:GNU/*:*:*) # other systems with GNU libc and userland - echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-${LIBC} + echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr "[:upper:]" "[:lower:]"``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-${LIBC} exit ;; i*86:Minix:*:*) echo ${UNAME_MACHINE}-pc-minix @@ -901,7 +923,7 @@ EOF EV68*) UNAME_MACHINE=alphaev68 ;; esac objdump --private-headers /bin/sh | grep -q ld.so.1 - if test "$?" = 0 ; then LIBC="gnulibc1" ; fi + if test "$?" = 0 ; then LIBC=gnulibc1 ; fi echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; arc:Linux:*:* | arceb:Linux:*:*) @@ -932,6 +954,9 @@ EOF crisv32:Linux:*:*) echo ${UNAME_MACHINE}-axis-linux-${LIBC} exit ;; + e2k:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; frv:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; @@ -944,6 +969,9 @@ EOF ia64:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; + k1om:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; m32r*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; @@ -969,10 +997,10 @@ EOF eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'` test x"${CPU}" != x && { echo "${CPU}-unknown-linux-${LIBC}"; exit; } ;; - or1k:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + openrisc*:Linux:*:*) + echo or1k-unknown-linux-${LIBC} exit ;; - or32:Linux:*:*) + or32:Linux:*:* | or1k*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; padre:Linux:*:*) @@ -1020,7 +1048,18 @@ EOF echo ${UNAME_MACHINE}-dec-linux-${LIBC} exit ;; x86_64:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + eval $set_cc_for_build + X86_64_ABI= + # If there is a compiler, see if it is configured for 32-bit objects. + if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then + if (echo '#ifdef __ILP32__'; echo IS_X32; echo '#endif') | \ + (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ + grep IS_X32 >/dev/null + then + X86_64_ABI=x32 + fi + fi + echo ${UNAME_MACHINE}-pc-linux-${LIBC}${X86_64_ABI} exit ;; xtensa*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} @@ -1099,7 +1138,7 @@ EOF # uname -m prints for DJGPP always 'pc', but it prints nothing about # the processor, so we play safe by assuming i586. # Note: whatever this is, it MUST be the same as what config.sub - # prints for the "djgpp" host, or else GDB configury will decide that + # prints for the "djgpp" host, or else GDB configure will decide that # this is a cross-build. echo i586-pc-msdosdjgpp exit ;; @@ -1248,6 +1287,9 @@ EOF SX-8R:SUPER-UX:*:*) echo sx8r-nec-superux${UNAME_RELEASE} exit ;; + SX-ACE:SUPER-UX:*:*) + echo sxace-nec-superux${UNAME_RELEASE} + exit ;; Power*:Rhapsody:*:*) echo powerpc-apple-rhapsody${UNAME_RELEASE} exit ;; @@ -1260,22 +1302,32 @@ EOF if test "$UNAME_PROCESSOR" = unknown ; then UNAME_PROCESSOR=powerpc fi - if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then - if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ - (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ - grep IS_64BIT_ARCH >/dev/null - then - case $UNAME_PROCESSOR in - i386) UNAME_PROCESSOR=x86_64 ;; - powerpc) UNAME_PROCESSOR=powerpc64 ;; - esac + if test `echo "$UNAME_RELEASE" | sed -e 's/\..*//'` -le 10 ; then + if [ "$CC_FOR_BUILD" != no_compiler_found ]; then + if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ + (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ + grep IS_64BIT_ARCH >/dev/null + then + case $UNAME_PROCESSOR in + i386) UNAME_PROCESSOR=x86_64 ;; + powerpc) UNAME_PROCESSOR=powerpc64 ;; + esac + fi fi + elif test "$UNAME_PROCESSOR" = i386 ; then + # Avoid executing cc on OS X 10.9, as it ships with a stub + # that puts up a graphical alert prompting to install + # developer tools. Any system running Mac OS X 10.7 or + # later (Darwin 11 and later) is required to have a 64-bit + # processor. This is not true of the ARM version of Darwin + # that Apple uses in portable devices. + UNAME_PROCESSOR=x86_64 fi echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE} exit ;; *:procnto*:*:* | *:QNX:[0123456789]*:*) UNAME_PROCESSOR=`uname -p` - if test "$UNAME_PROCESSOR" = "x86"; then + if test "$UNAME_PROCESSOR" = x86; then UNAME_PROCESSOR=i386 UNAME_MACHINE=pc fi @@ -1306,7 +1358,7 @@ EOF # "uname -m" is not consistent, so use $cputype instead. 386 # is converted to i386 for consistency with other x86 # operating systems. - if test "$cputype" = "386"; then + if test "$cputype" = 386; then UNAME_MACHINE=i386 else UNAME_MACHINE="$cputype" @@ -1348,7 +1400,7 @@ EOF echo i386-pc-xenix exit ;; i*86:skyos:*:*) - echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE}` | sed -e 's/ .*$//' + echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE} | sed -e 's/ .*$//'` exit ;; i*86:rdos:*:*) echo ${UNAME_MACHINE}-pc-rdos @@ -1359,155 +1411,10 @@ EOF x86_64:VMkernel:*:*) echo ${UNAME_MACHINE}-unknown-esx exit ;; -esac - -eval $set_cc_for_build -cat >$dummy.c <<EOF -#ifdef _SEQUENT_ -# include <sys/types.h> -# include <sys/utsname.h> -#endif -main () -{ -#if defined (sony) -#if defined (MIPSEB) - /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed, - I don't know.... */ - printf ("mips-sony-bsd\n"); exit (0); -#else -#include <sys/param.h> - printf ("m68k-sony-newsos%s\n", -#ifdef NEWSOS4 - "4" -#else - "" -#endif - ); exit (0); -#endif -#endif - -#if defined (__arm) && defined (__acorn) && defined (__unix) - printf ("arm-acorn-riscix\n"); exit (0); -#endif - -#if defined (hp300) && !defined (hpux) - printf ("m68k-hp-bsd\n"); exit (0); -#endif - -#if defined (NeXT) -#if !defined (__ARCHITECTURE__) -#define __ARCHITECTURE__ "m68k" -#endif - int version; - version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`; - if (version < 4) - printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version); - else - printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version); - exit (0); -#endif - -#if defined (MULTIMAX) || defined (n16) -#if defined (UMAXV) - printf ("ns32k-encore-sysv\n"); exit (0); -#else -#if defined (CMU) - printf ("ns32k-encore-mach\n"); exit (0); -#else - printf ("ns32k-encore-bsd\n"); exit (0); -#endif -#endif -#endif - -#if defined (__386BSD__) - printf ("i386-pc-bsd\n"); exit (0); -#endif - -#if defined (sequent) -#if defined (i386) - printf ("i386-sequent-dynix\n"); exit (0); -#endif -#if defined (ns32000) - printf ("ns32k-sequent-dynix\n"); exit (0); -#endif -#endif - -#if defined (_SEQUENT_) - struct utsname un; - - uname(&un); - - if (strncmp(un.version, "V2", 2) == 0) { - printf ("i386-sequent-ptx2\n"); exit (0); - } - if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */ - printf ("i386-sequent-ptx1\n"); exit (0); - } - printf ("i386-sequent-ptx\n"); exit (0); - -#endif - -#if defined (vax) -# if !defined (ultrix) -# include <sys/param.h> -# if defined (BSD) -# if BSD == 43 - printf ("vax-dec-bsd4.3\n"); exit (0); -# else -# if BSD == 199006 - printf ("vax-dec-bsd4.3reno\n"); exit (0); -# else - printf ("vax-dec-bsd\n"); exit (0); -# endif -# endif -# else - printf ("vax-dec-bsd\n"); exit (0); -# endif -# else - printf ("vax-dec-ultrix\n"); exit (0); -# endif -#endif - -#if defined (alliant) && defined (i860) - printf ("i860-alliant-bsd\n"); exit (0); -#endif - - exit (1); -} -EOF - -$CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null && SYSTEM_NAME=`$dummy` && - { echo "$SYSTEM_NAME"; exit; } - -# Apollos put the system type in the environment. - -test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit; } - -# Convex versions that predate uname can use getsysinfo(1) - -if [ -x /usr/convex/getsysinfo ] -then - case `getsysinfo -f cpu_type` in - c1*) - echo c1-convex-bsd + amd64:Isilon\ OneFS:*:*) + echo x86_64-unknown-onefs exit ;; - c2*) - if getsysinfo -f scalar_acc - then echo c32-convex-bsd - else echo c2-convex-bsd - fi - exit ;; - c34*) - echo c34-convex-bsd - exit ;; - c38*) - echo c38-convex-bsd - exit ;; - c4*) - echo c4-convex-bsd - exit ;; - esac -fi +esac cat >&2 <<EOF $0: unable to guess system type @@ -1516,9 +1423,9 @@ This script, last modified $timestamp, has failed to recognize the operating system you are using. It is advised that you download the most up to date version of the config scripts from - http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD + http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess and - http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD + http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub If the version you run ($0) is already up to date, please send the following data and any information you think might be diff --git a/config.h.in b/config.h.in index 274cb25a4..0ac78cf26 100644 --- a/config.h.in +++ b/config.h.in @@ -9,6 +9,15 @@ /* Define to 1 if you have the `accept4' function. */ #undef HAVE_ACCEPT4 +/* Define to 1 if you have the <arpa/nameser.h> header file. */ +#undef HAVE_ARPA_NAMESER_H + +/* Define to 1 if the system has the type `clockid_t'. */ +#undef HAVE_CLOCKID_T + +/* Define to 1 if you have the `clock_gettime' function. */ +#undef HAVE_CLOCK_GETTIME + /* Define to 1 if glibc mallinfo exists. */ #undef HAVE_C_MALLINFO @@ -25,6 +34,22 @@ don't. */ #undef HAVE_DECL_STRERROR_R +/* Define to 1 if the system has the `format' function attribute */ +#undef HAVE_FUNC_ATTRIBUTE_FORMAT + +/* Define to 1 if the system has the `malloc' function attribute */ +#undef HAVE_FUNC_ATTRIBUTE_MALLOC + +/* Define to 1 if the system has the `noreturn' function attribute */ +#undef HAVE_FUNC_ATTRIBUTE_NORETURN + +/* Define to 1 if the system has the `returns_nonnull' function attribute */ +#undef HAVE_FUNC_ATTRIBUTE_RETURNS_NONNULL + +/* Define to 1 if the system has the `warn_unused_result' function attribute + */ +#undef HAVE_FUNC_ATTRIBUTE_WARN_UNUSED_RESULT + /* Define to 1 if you have the <inttypes.h> header file. */ #undef HAVE_INTTYPES_H @@ -34,12 +59,21 @@ /* Define to 1 if you have the <memory.h> header file. */ #undef HAVE_MEMORY_H +/* Define to 1 if you have the <netdb.h> header file. */ +#undef HAVE_NETDB_H + +/* Define to 1 if you have the <netinet/in.h> header file. */ +#undef HAVE_NETINET_IN_H + /* Define if you have POSIX threads libraries and header files. */ #undef HAVE_PTHREAD /* Have PTHREAD_PRIO_INHERIT. */ #undef HAVE_PTHREAD_PRIO_INHERIT +/* Define to 1 if you have the <resolv.h> header file. */ +#undef HAVE_RESOLV_H + /* Define to 1 if you have the <stdint.h> header file. */ #undef HAVE_STDINT_H @@ -55,6 +89,9 @@ /* Define to 1 if you have the <string.h> header file. */ #undef HAVE_STRING_H +/* Define to 1 if the system has the type `struct timespec'. */ +#undef HAVE_STRUCT_TIMESPEC + /* Define to 1 if you have the <sys/stat.h> header file. */ #undef HAVE_SYS_STAT_H @@ -67,6 +104,14 @@ /* nfacct plugin settings */ #undef INTERNAL_PLUGIN_NFACCT +/* Define to 1 if `major', `minor', and `makedev' are declared in <mkdev.h>. + */ +#undef MAJOR_IN_MKDEV + +/* Define to 1 if `major', `minor', and `makedev' are declared in + <sysmacros.h>. */ +#undef MAJOR_IN_SYSMACROS + /* use this user to drop privileged */ #undef NETDATA_USER diff --git a/config.sub b/config.sub index 9633db704..b01fff0b6 100755 --- a/config.sub +++ b/config.sub @@ -1,8 +1,8 @@ #! /bin/sh # Configuration validation subroutine script. -# Copyright 1992-2013 Free Software Foundation, Inc. +# Copyright 1992-2016 Free Software Foundation, Inc. -timestamp='2013-08-10' +timestamp='2016-03-30' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by @@ -25,7 +25,7 @@ timestamp='2013-08-10' # of the GNU General Public License, version 3 ("GPLv3"). -# Please send patches with a ChangeLog entry to config-patches@gnu.org. +# Please send patches to <config-patches@gnu.org>. # # Configuration subroutine to validate and canonicalize a configuration type. # Supply the specified configuration type as an argument. @@ -33,7 +33,7 @@ timestamp='2013-08-10' # Otherwise, we print the canonical config type on stdout and succeed. # You can get the latest version of this script from: -# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD +# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub # This file is supposed to be the same for all GNU packages # and recognize all the CPU types, system types and aliases @@ -53,8 +53,7 @@ timestamp='2013-08-10' me=`echo "$0" | sed -e 's,.*/,,'` usage="\ -Usage: $0 [OPTION] CPU-MFR-OPSYS - $0 [OPTION] ALIAS +Usage: $0 [OPTION] CPU-MFR-OPSYS or ALIAS Canonicalize a configuration name. @@ -68,7 +67,7 @@ Report bugs and patches to <config-patches@gnu.org>." version="\ GNU config.sub ($timestamp) -Copyright 1992-2013 Free Software Foundation, Inc. +Copyright 1992-2016 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." @@ -117,7 +116,7 @@ maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'` case $maybe_os in nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc | linux-newlib* | \ linux-musl* | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \ - knetbsd*-gnu* | netbsd*-gnu* | \ + knetbsd*-gnu* | netbsd*-gnu* | netbsd*-eabi* | \ kopensolaris*-gnu* | \ storm-chaos* | os2-emx* | rtmk-nova*) os=-$maybe_os @@ -255,16 +254,18 @@ case $basic_machine in | arc | arceb \ | arm | arm[bl]e | arme[lb] | armv[2-8] | armv[3-8][lb] | armv7[arm] \ | avr | avr32 \ + | ba \ | be32 | be64 \ | bfin \ | c4x | c8051 | clipper \ - | d10v | d30v | dlx | dsp16xx \ - | epiphany \ - | fido | fr30 | frv \ + | d10v | d30v | dlx | dsp16xx | dvp \ + | e2k | epiphany \ + | fido | fr30 | frv | ft32 \ | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ | hexagon \ | i370 | i860 | i960 | ia64 \ | ip2k | iq2000 \ + | k1om \ | le32 | le64 \ | lm32 \ | m32c | m32r | m32rle | m68000 | m68k | m88k \ @@ -282,8 +283,10 @@ case $basic_machine in | mips64vr5900 | mips64vr5900el \ | mipsisa32 | mipsisa32el \ | mipsisa32r2 | mipsisa32r2el \ + | mipsisa32r6 | mipsisa32r6el \ | mipsisa64 | mipsisa64el \ | mipsisa64r2 | mipsisa64r2el \ + | mipsisa64r6 | mipsisa64r6el \ | mipsisa64sb1 | mipsisa64sb1el \ | mipsisa64sr71k | mipsisa64sr71kel \ | mipsr5900 | mipsr5900el \ @@ -295,14 +298,14 @@ case $basic_machine in | nds32 | nds32le | nds32be \ | nios | nios2 | nios2eb | nios2el \ | ns16k | ns32k \ - | open8 \ - | or1k | or32 \ + | open8 | or1k | or1knd | or32 \ | pdp10 | pdp11 | pj | pjl \ | powerpc | powerpc64 | powerpc64le | powerpcle \ | pyramid \ + | riscv32 | riscv64 \ | rl78 | rx \ | score \ - | sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \ + | sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[234]eb | sheb | shbe | shle | sh[1234]le | sh3ele \ | sh64 | sh64le \ | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \ | sparcv8 | sparcv9 | sparcv9b | sparcv9v \ @@ -310,6 +313,7 @@ case $basic_machine in | tahoe | tic4x | tic54x | tic55x | tic6x | tic80 | tron \ | ubicom32 \ | v850 | v850e | v850e1 | v850e2 | v850es | v850e2v3 \ + | visium \ | we32k \ | x86 | xc16x | xstormy16 | xtensa \ | z8k | z80) @@ -324,7 +328,10 @@ case $basic_machine in c6x) basic_machine=tic6x-unknown ;; - m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | picochip) + leon|leon[3-9]) + basic_machine=sparc-$basic_machine + ;; + m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | nvptx | picochip) basic_machine=$basic_machine-unknown os=-none ;; @@ -369,18 +376,20 @@ case $basic_machine in | alphapca5[67]-* | alpha64pca5[67]-* | arc-* | arceb-* \ | arm-* | armbe-* | armle-* | armeb-* | armv*-* \ | avr-* | avr32-* \ + | ba-* \ | be32-* | be64-* \ | bfin-* | bs2000-* \ | c[123]* | c30-* | [cjt]90-* | c4x-* \ | c8051-* | clipper-* | craynv-* | cydra-* \ | d10v-* | d30v-* | dlx-* \ - | elxsi-* \ + | e2k-* | elxsi-* \ | f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \ | h8300-* | h8500-* \ | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \ | hexagon-* \ | i*86-* | i860-* | i960-* | ia64-* \ | ip2k-* | iq2000-* \ + | k1om-* \ | le32-* | le64-* \ | lm32-* \ | m32c-* | m32r-* | m32rle-* \ @@ -400,8 +409,10 @@ case $basic_machine in | mips64vr5900-* | mips64vr5900el-* \ | mipsisa32-* | mipsisa32el-* \ | mipsisa32r2-* | mipsisa32r2el-* \ + | mipsisa32r6-* | mipsisa32r6el-* \ | mipsisa64-* | mipsisa64el-* \ | mipsisa64r2-* | mipsisa64r2el-* \ + | mipsisa64r6-* | mipsisa64r6el-* \ | mipsisa64sb1-* | mipsisa64sb1el-* \ | mipsisa64sr71k-* | mipsisa64sr71kel-* \ | mipsr5900-* | mipsr5900el-* \ @@ -413,16 +424,18 @@ case $basic_machine in | nios-* | nios2-* | nios2eb-* | nios2el-* \ | none-* | np1-* | ns16k-* | ns32k-* \ | open8-* \ + | or1k*-* \ | orion-* \ | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \ | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \ | pyramid-* \ + | riscv32-* | riscv64-* \ | rl78-* | romp-* | rs6000-* | rx-* \ | sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \ | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \ | sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \ | sparclite-* \ - | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx?-* \ + | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx*-* \ | tahoe-* \ | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \ | tile*-* \ @@ -430,6 +443,7 @@ case $basic_machine in | ubicom32-* \ | v850-* | v850e-* | v850e1-* | v850es-* | v850e2-* | v850e2v3-* \ | vax-* \ + | visium-* \ | we32k-* \ | x86-* | x86_64-* | xc16x-* | xps100-* \ | xstormy16-* | xtensa*-* \ @@ -506,6 +520,9 @@ case $basic_machine in basic_machine=i386-pc os=-aros ;; + asmjs) + basic_machine=asmjs-unknown + ;; aux) basic_machine=m68k-apple os=-aux @@ -767,6 +784,9 @@ case $basic_machine in basic_machine=m68k-isi os=-sysv ;; + leon-*|leon[3-9]-*) + basic_machine=sparc-`echo $basic_machine | sed 's/-.*//'` + ;; m68knommu) basic_machine=m68k-unknown os=-linux @@ -808,6 +828,24 @@ case $basic_machine in basic_machine=m68k-atari os=-mint ;; + mipsEE* | ee | ps2) + basic_machine=mips64r5900el-scei + case $os in + -linux*) + ;; + *) + os=-elf + ;; + esac + ;; + iop) + basic_machine=mipsel-scei + os=-irx + ;; + dvp) + basic_machine=dvp-scei + os=-elf + ;; mips3*-*) basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'` ;; @@ -822,6 +860,10 @@ case $basic_machine in basic_machine=powerpc-unknown os=-morphos ;; + moxiebox) + basic_machine=moxie-unknown + os=-moxiebox + ;; msdos) basic_machine=i386-pc os=-msdos @@ -1354,11 +1396,11 @@ case $os in | -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \ | -sym* | -kopensolaris* | -plan9* \ | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \ - | -aos* | -aros* \ + | -aos* | -aros* | -cloudabi* | -sortix* \ | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \ | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \ - | -bitrig* | -openbsd* | -solidbsd* \ + | -bitrig* | -openbsd* | -solidbsd* | -libertybsd* \ | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \ | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \ | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \ @@ -1367,14 +1409,15 @@ case $os in | -cygwin* | -msys* | -pe* | -psos* | -moss* | -proelf* | -rtems* \ | -mingw32* | -mingw64* | -linux-gnu* | -linux-android* \ | -linux-newlib* | -linux-musl* | -linux-uclibc* \ - | -uxpv* | -beos* | -mpeix* | -udk* \ + | -uxpv* | -beos* | -mpeix* | -udk* | -moxiebox* \ | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \ | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \ - | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \ + | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* | -irx* \ | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \ | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \ | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \ - | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es*) + | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es* \ + | -onefs* | -tirtos*) # Remember, each alternative MUST END IN *, to match a version number. ;; -qnx*) @@ -1506,6 +1549,8 @@ case $os in ;; -nacl*) ;; + -ios) + ;; -none) ;; *) @@ -1592,9 +1637,6 @@ case $basic_machine in mips*-*) os=-elf ;; - or1k-*) - os=-elf - ;; or32-*) os=-coff ;; diff --git a/configs.signatures b/configs.signatures index 24603ae9f..fbae919c4 100644 --- a/configs.signatures +++ b/configs.signatures @@ -1,248 +1,376 @@ declare -A configs_signatures=( ['0056936ce99788ed9ae1c611c87aa6d8']='apps_groups.conf' + ['0102351817595a85d01ebd54a5f2f36b']='python.d/ovpn_status_log.conf' + ['02fa10fa85ab88e9723998de48d1aca0']='health.d/disks.conf' + ['036dc300bd7b0e0ef229b9822686d63e']='python.d/isc_dhcpd.conf' + ['0388b873d0d7e47c19005b7241db77d8']='python.d/tomcat.conf' + ['04138a3d8e907c75329fe60ce2e27c1c']='health.d/tcp_resets.conf' + ['0433d98a19d3b08e6f13884e46d39b47']='health.d/disks.conf' + ['043f0a35dde85837fabeb85b990a41c1']='health.d/swap.conf' + ['0529b679d3c0e7e6332753c7f6484731']='health.d/net.conf' + ['057d12aaff0467e64529e839a258806b']='health.d/entropy.conf' + ['061c45b0e34170d357e47883166ecf40']='python.d/nginx.conf' + ['074df527cc70b5f38c0714f08f20e57c']='health.d/apache.conf' + ['08042325ab27256b938575deafee8ecf']='python.d/nginx.conf' + ['0847d54a7a0c7e0381c52e9d4d3fa7db']='health.d/mdstat.conf' + ['084ee72d64760f2641b0720e79c922f3']='health.d/cpu.conf' + ['0856124b1eecf01681b4fdf4e21efb3f']='health.d/net.conf' + ['08ff5218f938fc48e09e718821169d14']='health.d/redis.conf' + ['091572888425bc3b8b559c3c53367ec7']='apps_groups.conf' + ['09264cec953ae1c4c2985e6446abb386']='health.d/mysql.conf' + ['093540fdc2a228e976ce5d48a3adf9fc']='health.d/disks.conf' + ['0c5e0fa364d7bdf7c16e8459a0544572']='health.d/netfilter.conf' + ['0cd4e1fb57497e4d4c2451a9e58f724d']='python.d/redis.conf' + ['0d29fe9919a2975107db1f2583344e7a']='health.d/mdstat.conf' + ['0e59bc11d0a869ea0247c04c08c8d72e']='python.d/ipfs.conf' + ['0ef8af1f358741afa7fd5d0ffabefaac']='charts.d/mysql.conf' + ['10c3b525850a1cb9de760a8ee96fbc6e']='charts.d/opensips.conf' + ['1112c848ef91ebb9c622020d09712d67']='health.d/net.conf' + ['13141998a5d71308d9c119834c27bfd3']='python.d.conf' + ['142a5b693d34b0308bb0b8aec71fad79']='python.d/postfix.conf' + ['14783e051650442ec9e2ed38d81d667e']='charts.d/exim.conf' + ['15d8401b56a74120f9f832873ec9c578']='health.d/postgres.conf' + ['15e32114994b92be7853b88091e7c6fb']='python.d/exim.conf' + ['174c21a6ce5de97bda83d502aa47a9f8']='health.d/apache.conf' + ['178281aa2241d4a3e6b798bb9c4ae577']='python.d/haproxy.conf' + ['18710ef6523cef8630d644ab270bfe02']='health.d/varnish.conf' ['18ee1c6197a4381b1c1631ef6129824f']='apps_groups.conf' + ['1972e48345e6c3f0d65f94a03317622b']='health_alarm_notify.conf' + ['1c12b678ab65f271a96da1bbd0a1ab1c']='health.d/softnet.conf' + ['1ea8e8ef1fa8a3a0fcdfba236f4cb195']='python.d/mysql.conf' + ['1ef0fd38e7969c023bc3fa6d89eaf6d6']='python.d/mdstat.conf' + ['1f5545b3ff52b3eb75ee05401f67a9bc']='fping.conf' + ['1fa47f32ab52a22f8e7087cae85dd25e']='health.d/net.conf' + ['203678a5be09d65993dcb3a9d475d187']='health.d/ipfs.conf' + ['20be73f473e59bc7de1fe61d53466aba']='health.d/ram.conf' + ['21924a6ab8008d16ffac340f226ebad9']='python.d/nginx.conf' + ['219c5bb81965fa17d4940d4aa343c282']='health.d/mysql.conf' + ['22952dbf42647c583b005054b23b545f']='health.d/disks.conf' + ['22ceb822983134a7ca67343241f30341']='health.d/disks.conf' + ['2385e5d35b440619621c4af62492d91b']='health.d/disks.conf' + ['23ae815aefa221b1929f96752a1f7556']='health.d/squid.conf' + ['2472e49550326f7142e2c425ccbca005']='health.d/softnet.conf' + ['254de8ec49602bea2da3631676d7cfec']='health.d/cpu.conf' + ['256a7f06f7e579a61752fc64418cffe5']='charts.d/nut.conf' + ['262f98b3d88b98978cb08d566ce85a9d']='charts.d/squid.conf' + ['2827de41cf34a91b7a8e4d8724f59668']='health.d/net.conf' + ['28df44a90e8ea4c6156314c03e88bf44']='health.d/softnet.conf' + ['297160ae7ee01a547ed14f857b4f2c8d']='health.d/memcached.conf' + ['29f97e10b92333790fbe0d2a3617b736']='health_alarm_notify.conf' + ['2a0794fd43eadf30a51805bc9ba2c64d']='python.d/hddtemp.conf' + ['2ad55a5d1e885cf142849a78d4b00401']='health.d/net.conf' + ['2bbbebf52f84fd27fbefecd2a8a8076f']='health.d/memcached.conf' + ['2d1d7498c72f4245cf32902c2b7e71e0']='health.d/entropy.conf' + ['2f3a8e33df83f14e0af8ca2465697215']='python.d/exim.conf' ['2f4a85fedecce1bf425fa1039f6b021e']='apps_groups.conf' + ['2fa8fb929fd597f2ab97b6efc540a043']='health_alarm_notify.conf' + ['312b4b8e2805e19cf9be554b319567d6']='health.d/softnet.conf' + ['318bb45755726a25120bb33413d4b582']='health.d/net.conf' + ['325617412a628e3bc776e3fbb777a2a6']='health.d/redis.conf' + ['32fde0057c790964f2c743cb3c9aad29']='health.d/nginx.conf' + ['33b135e28aeaef2b8224ba69a0fde245']='health.d/cpu.conf' + ['3634d5eddc46fb0d50cf47f370670c2c']='health.d/redis.conf' + ['364b6e0081b116c9ec073b4d329a6dcc']='health_alarm_notify.conf' + ['367d1463e520eb9dc89223bab161c6d1']='python.d/postgres.conf' + ['36fdd55665cf10b0db164c2a0cca5e57']='health.d/qos.conf' + ['373160658e7d5f1a129de397b9347365']='health.d/entropy.conf' + ['373c1276dc9e65884ff2b26e1f08afe7']='health.d/named.conf' + ['3848172053221b95279ba9bf789cd4e0']='health.d/apache.conf' + ['3866efafd38e161136428d0f818cac43']='health.d/net.conf' + ['38d1bf04fe9901481dd6febcc0404a86']='python.d.conf' + ['39304b2570611c3acb35b72762b46778']='charts.d/sensors.conf' + ['39571e9fad9b759200c5d5b2ee13feb4']='python.d/redis.conf' + ['39f9422b0f0c3eec11a31aff79d89514']='health.d/retroshare.conf' + ['3a04a3bc66c49d0c24f65a44fd9caa80']='python.d/postgres.conf' ['3af522d65b50a5e447607ffb28c81ff5']='apps_groups.conf' ['3b1bfa40a4ff6a200bb2fc00bc51a664']='apps_groups.conf' + ['3bc2776623889744a98178bad6fb3b79']='health.d/disks.conf' + ['3bc65e997ab59b9de390fdf63d77f5e1']='python.d/postgres.conf' + ['3c9c47163e9d4dbcb0079b6232398f2f']='apps_groups.conf' + ['3ca696189911fb38a0319ddd71e9a395']='python.d/phpfpm.conf' + ['3cc6255457d4cba881ae0554ae5d9190']='health.d/squid.conf' + ['3f170e3343cd784983b019163393f5af']='health.d/nginx.conf' + ['3fc45cc18e884c22482524dff6d27833']='python.d/hddtemp.conf' + ['3fcc3c449ce8e0388f9c23ca07cab608']='health.d/backend.conf' + ['4063a01bffb43b0423425d1ba3004967']='health.d/tcp_resets.conf' + ['421d5dc6c2fce22d0816b6e6363bea57']='python.d/hddtemp.conf' + ['42ad0e70b1365b6e7244cc305dbaa529']='health_alarm_notify.conf' + ['42bf1c7c64ed77038a0aa094d792a9e2']='python.d/mysql.conf' + ['4332dee96e4f38fc73c962df3494ab7c']='health_alarm_notify.conf' + ['43ebb7f224c3b232d8ad044d7e9508b6']='health.d/net.conf' + ['444e20cf75e2cd019e8d412d5d1f4a7f']='charts.d/cpu_apps.conf' + ['4461bfacf9a3da47770fb3ca31f4c91f']='health.d/net.conf' + ['450667c552ab7a7d8d4a2c214fdacca5']='health.d/entropy.conf' + ['45a77ac36ba9f1898144b902de17204b']='health.d/memcached.conf' + ['46798cda21e1a5faa769abf4e5d27c48']='health.d/disks.conf' + ['46ef6c1b638e40a7dfd62defdc5f99a3']='health.d/retroshare.conf' + ['47180421d580baeaedf8c0ef3d647fb5']='python.d/hddtemp.conf' + ['48195c5c8c0476a49b714b4c76bdb570']='python.d/squid.conf' + ['48eef63bcf744bae114b502b6dacb4a1']='charts.d/phpfpm.conf' ['4a448831776de8acf2e0bdc4cc994cb4']='apps_groups.conf' - ['5bf51bb24fb41db9b1e448bd060d3f8c']='apps_groups.conf' - ['636d032928ea0f4741eab264fb49c099']='apps_groups.conf' - ['647361e99b5f4e0d73470c569bb9461c']='apps_groups.conf' - ['6a47af861ad3dd112124c37fbf09672b']='apps_groups.conf' - ['79a37756869d9b4629285922572d6b9b']='apps_groups.conf' - ['99c1617448abbdc493976ab9bda5ce02']='apps_groups.conf' - ['9c0185ceff15415bc59b2ce2c1f04367']='apps_groups.conf' - ['a0ee8f351f213c0e8af9eb7a4a09cb95']='apps_groups.conf' - ['a7cceeafb1e6ef1ead503ab65f687902']='apps_groups.conf' - ['a837986be634fd7648bcdf939019424a']='apps_groups.conf' - ['a9cd91675467c5426f5b51c47602c889']='apps_groups.conf' - ['acaa6731a272f6d251afb357e99b518f']='apps_groups.conf' - ['bb51112d01ff20053196a57632df8962']='apps_groups.conf' - ['d9036091e2232fc2b8bfa8c7484dea28']='apps_groups.conf' - ['d9258e671d0d0b6498af1ce16ef030d2']='apps_groups.conf' - ['ebd0612ccc5807524ebb2b647e3e56c9']='apps_groups.conf' - ['f2f1b8656f5011e965ac45b818cf668d']='apps_groups.conf' - ['fdea185e0e52b459b48852aa37f20e0f']='apps_groups.conf' + ['4b775fb31342f1478b3773d041a72911']='python.d.conf' ['4ccb06fff1ce06dc5bc80e0a9f568f6e']='charts.d.conf' + ['4d13684cadfa90e73ab465409bf7263b']='health.d/mysql.conf' ['4e995acb0d6fd77403a2a9dca984b55b']='charts.d.conf' + ['4f6a5b47a13f5912cc89e9286701dd08']='health.d/redis.conf' + ['4f6f4d39c19d7d954f769d3f9d3b4da5']='health.d/memcached.conf' + ['4fdf72784296326e0b46cb526a5d77a1']='python.d.conf' + ['4fef19afccd9a591165b72f0b1a2ac2e']='python.d/freeradius.conf' + ['501eb2484b459b410b3f792c2dbaa955']='health.d/swap.conf' + ['508771d8e4611a058991a1bc11039dea']='health.d/disks.conf' + ['5120492fa26be3749192607f62dc05f8']='health.d/mdstat.conf' + ['5271cf9fc0fd10915a9759add70f7d78']='health.d/swap.conf' + ['5278ebbae19c60db600f0a119cb3664e']='python.d/apache.conf' + ['52d4131cf9df84e2550b1a5d899ec61d']='health.d/swap.conf' + ['53160707fdc6ce46c195b1b55bb0bcb1']='health.d/swap.conf' ['535e5113b07b0fc6f3abd59546c276f6']='charts.d.conf' - ['7cf6402b51e5070f2be3ad6fe059ff89']='charts.d.conf' - ['a02d14124b19c635c1426cee2e98bac5']='charts.d.conf' - ['ca026d7c779f0a7cb7787713c5be5c47']='charts.d.conf' - ['074df527cc70b5f38c0714f08f20e57c']='health.d/apache.conf' - ['174c21a6ce5de97bda83d502aa47a9f8']='health.d/apache.conf' - ['3848172053221b95279ba9bf789cd4e0']='health.d/apache.conf' - ['80266bddd3df374923c750a6de91d120']='health.d/apache.conf' - ['842b1ad5b89bfa5f421d9c5b72e001a4']='health.d/apache.conf' - ['a6d5ce2572bf7a1dce9e545fcd29273e']='health.d/apache.conf' - ['084ee72d64760f2641b0720e79c922f3']='health.d/cpu.conf' - ['254de8ec49602bea2da3631676d7cfec']='health.d/cpu.conf' - ['5eb670b6fe39da5fec2523d910b0dd1e']='health.d/cpu.conf' - ['623771eecb3c277fc728b5304793f93b']='health.d/cpu.conf' - ['7596ae54d46ce199ac599429ef753caf']='health.d/cpu.conf' - ['8d0552371a7c9725a04196fa560813d1']='health.d/cpu.conf' - ['c3296c08260bcd556e74711c820817be']='health.d/cpu.conf' - ['fdd11640ba626cc2064c2fe3ea3eee4c']='health.d/cpu.conf' - ['02fa10fa85ab88e9723998de48d1aca0']='health.d/disks.conf' - ['0433d98a19d3b08e6f13884e46d39b47']='health.d/disks.conf' - ['22ceb822983134a7ca67343241f30341']='health.d/disks.conf' - ['2385e5d35b440619621c4af62492d91b']='health.d/disks.conf' - ['3bc2776623889744a98178bad6fb3b79']='health.d/disks.conf' - ['46798cda21e1a5faa769abf4e5d27c48']='health.d/disks.conf' + ['5379cdc26d7725e2b0d688d785816cef']='python.d/mysql.conf' + ['54614490a14e1a4b7b3d9fecb6b4cfa5']='python.d/exim.conf' + ['547779cdc460a926980de1590294b96b']='health.d/softnet.conf' + ['55608bdd908a3806df1468f6ee318b2b']='health.d/qos.conf' + ['5598b83e915e31f68027afe324a427cd']='apps_groups.conf' + ['565f11c38ae6bd5cc9d3c2adb542bc1b']='health.d/softnet.conf' + ['5664a814f9351b55da76edd472169a73']='health_alarm_notify.conf' + ['56b689031cdcf138064825f31474b37d']='apps_groups.conf' ['573398335c0c71c075fa57f702bce287']='health.d/disks.conf' + ['5829812db29598db5857c9f433e96fef']='python.d/apache.conf' + ['58e835b7176865ec5a6f59f7aba832bf']='health.d/named.conf' + ['5b917d894bb6a755d59264e9d48e9d56']='fping.conf' + ['5bf51bb24fb41db9b1e448bd060d3f8c']='apps_groups.conf' ['5da15d6e17a15213a720749045e5d419']='health.d/disks.conf' - ['7aa209fa287c95b3ca04c23681b40770']='health.d/disks.conf' - ['80d242d619eb7e91cebfdbf58d79b0f8']='health.d/disks.conf' - ['8989b5e2f4ef9cd278ef58be0fae4074']='health.d/disks.conf' - ['8a1b95d375992d7b11330a0ac46f369c']='health.d/disks.conf' - ['8dc0bd0a70b5117454bd5f5b98f91c2c']='health.d/disks.conf' - ['a4407787e4beb23a701a8a614dca461d']='health.d/disks.conf' - ['a5114d5b0d3816dba75024b9444f4b40']='health.d/disks.conf' - ['a8167dafeac0b66696a1d9b08e815cda']='health.d/disks.conf' - ['afdae4646c755ff2d117527fbf761c8e']='health.d/disks.conf' - ['cb60badf376d246ad8ec9d3f524db430']='health.d/disks.conf' - ['dd8254ef74509a3e38cb2838e30f7e63']='health.d/disks.conf' - ['e8ec8046c7007af6ca3e8c51e62c99f8']='health.d/disks.conf' - ['057d12aaff0467e64529e839a258806b']='health.d/entropy.conf' - ['2d1d7498c72f4245cf32902c2b7e71e0']='health.d/entropy.conf' - ['373160658e7d5f1a129de397b9347365']='health.d/entropy.conf' - ['450667c552ab7a7d8d4a2c214fdacca5']='health.d/entropy.conf' + ['5e6fd588ef6934cf04ddb5e662aa02ea']='health.d/postgres.conf' + ['5eb670b6fe39da5fec2523d910b0dd1e']='health.d/cpu.conf' + ['5f05d4b248ab2637ada319b4e8c4e4c3']='python.d/varnish.conf' ['5ff1bcaa58695754e2f6980bfe19f579']='health.d/entropy.conf' - ['a8feb36776005bf419c90278787a1be8']='health.d/entropy.conf' - ['b636e5e603f9d93e52c7577ac8c6bf0c']='health.d/entropy.conf' - ['d8dc489e32f7114c6298fce94e86a8ef']='health.d/entropy.conf' - ['def883f35986c9d25de63b1a8e7d0f46']='health.d/entropy.conf' - ['e449e5582279742496550df14b6fca95']='health.d/entropy.conf' - ['297160ae7ee01a547ed14f857b4f2c8d']='health.d/memcached.conf' - ['2bbbebf52f84fd27fbefecd2a8a8076f']='health.d/memcached.conf' - ['45a77ac36ba9f1898144b902de17204b']='health.d/memcached.conf' + ['61b7ed36f35e7bd930f5f7f91694a112']='charts.d/postfix.conf' ['621f10b257a11add5ff5aff41e9662e3']='health.d/memcached.conf' - ['7e5fc1644aa7a54f9dbb1bd102521b09']='health.d/memcached.conf' - ['9c981c75bdf4b1637f7113e7e45eb2bf']='health.d/memcached.conf' - ['a731b7b164f42717c1c9a778ee637ff3']='health.d/memcached.conf' - ['b81b8f331161b0d48e03f6fbf6b6d062']='health.d/memcached.conf' - ['c1a7e634b5b8aad523a0d115a93379cd']='health.d/memcached.conf' - ['dd7764507804a2296bfd091a58ad4ad7']='health.d/memcached.conf' - ['f8c30f22df92765e2c0fab3c8174e2fc']='health.d/memcached.conf' - ['09264cec953ae1c4c2985e6446abb386']='health.d/mysql.conf' - ['4d13684cadfa90e73ab465409bf7263b']='health.d/mysql.conf' - ['97eee7a30e6419df4537242e9d4a719d']='health.d/mysql.conf' - ['373c1276dc9e65884ff2b26e1f08afe7']='health.d/named.conf' - ['58e835b7176865ec5a6f59f7aba832bf']='health.d/named.conf' + ['623771eecb3c277fc728b5304793f93b']='health.d/cpu.conf' + ['636d032928ea0f4741eab264fb49c099']='apps_groups.conf' + ['6398ef37a15cb6a0bc921f58948d2b39']='health.d/softnet.conf' + ['64070d856ab1b47a18ec871e49bbc13b']='python.d/squid.conf' + ['647361e99b5f4e0d73470c569bb9461c']='apps_groups.conf' + ['64ac37868097a462e5ee6905c350267e']='python.d/postgres.conf' + ['64c48f9726ab987baec9c617a9fef7a6']='health.d/nginx.conf' + ['64ffc1b6878c81b87564b0f48642c790']='health.d/elasticsearch.conf' + ['650b5fc9da23b25ee7ee1481e4aa2851']='health_alarm_notify.conf' + ['6546909d10cc5efcef9dd873bea85956']='python.d/mysql.conf' + ['65c6933a17fb6b7f8e6baeab73431c17']='charts.d/apcupsd.conf' + ['6608c6546b3c6bde084fc1d34b1163c1']='health.d/retroshare.conf' ['669ebef43ee341f6889d382e86d0e200']='health.d/named.conf' + ['66dfe138058ca26a31a118007eb31f35']='health.d/nginx.conf' + ['6814b9bc84483db428f6a479ba221855']='python.d/mysql.conf' + ['6a18f61a595c0d48c3363bcc0dbfa6b9']='health_alarm_notify.conf' + ['6a47af861ad3dd112124c37fbf09672b']='apps_groups.conf' ['6b39de5d85db45115db236347a6896d4']='health.d/named.conf' - ['846ce94bfeeb90c0dc6a89e8d25f1a68']='health.d/named.conf' - ['ddda2bb1c88be03b637d3285406f7910']='health.d/named.conf' - ['0529b679d3c0e7e6332753c7f6484731']='health.d/net.conf' - ['0856124b1eecf01681b4fdf4e21efb3f']='health.d/net.conf' - ['2827de41cf34a91b7a8e4d8724f59668']='health.d/net.conf' - ['2ad55a5d1e885cf142849a78d4b00401']='health.d/net.conf' - ['318bb45755726a25120bb33413d4b582']='health.d/net.conf' - ['43ebb7f224c3b232d8ad044d7e9508b6']='health.d/net.conf' - ['cb178b15427274d7def5b14bc4c09441']='health.d/net.conf' - ['d11711b3647bc2bdd0292dd7deebbeb1']='health.d/net.conf' - ['dc9c2a66778623a759706c14c3d91983']='health.d/net.conf' - ['de02f899a61f21b86adb646940f0bcae']='health.d/net.conf' - ['feb8bcf828aa2529a7ee4a140feeb12d']='health.d/net.conf' - ['32fde0057c790964f2c743cb3c9aad29']='health.d/nginx.conf' - ['3f170e3343cd784983b019163393f5af']='health.d/nginx.conf' - ['64c48f9726ab987baec9c617a9fef7a6']='health.d/nginx.conf' + ['6b917300747e7e8314844237e2462261']='python.d/apache_cache.conf' + ['6bf0de6e3b251b765b10a71d8c5c319d']='python.d/apache.conf' + ['6ca08ea2a238cad26578b8b85edae160']='health.d/udp_errors.conf' + ['6cba40e32a7e98a98c31a209913839cc']='python.d/nginx_log.conf' + ['6d02c2dd0863e09ad9dbba53e3b58116']='health.d/mysql.conf' + ['6ea958ca521e0514af57c08b518d8c5c']='health.d/backend.conf' + ['70105b1744a8e13f49083d7f1981aea2']='python.d/ipfs.conf' + ['707a63f53f4b32e01d134ae90ba94aad']='health_alarm_notify.conf' + ['707a63f53f4b32e01d134ae90ba94aad']='health_email_recipients.conf' + ['70d82dabecb09a1da4684f293abef0c9']='health_alarm_notify.conf' + ['73125ae64d5c6e9361944cd9bd14844e']='python.d/exim.conf' + ['731a1fcfe9b2da1b9d685056a59541b8']='python.d/hddtemp.conf' + ['73a8e10dfe4183aca751e9e2a80dabe3']='node.d.conf' + ['7454ed74511d7b9819dfe173f9020786']='python.d/redis.conf' + ['749fe31362969d75f1ea66d15231d98d']='python.d/retroshare.conf' + ['7596ae54d46ce199ac599429ef753caf']='health.d/cpu.conf' + ['75a9c4b0b1c73956df55585eb0619f6c']='charts.d/ap.conf' + ['769aa4cdcdc3d78d0328d1f9e4edcdf9']='python.d/mysql.conf' + ['777f4da70f461ef675bde07fb3644312']='python.d/redis.conf' + ['777f55a95c5c25cf6176fece1ebbf4b8']='apps_groups.conf' + ['7808ba2ca26bd0642270740cf6a8ee59']='charts.d/mem_apps.conf' + ['7830066c46a7e5f9682b8d3f4566b4e5']='python.d/cpufreq.conf' + ['78bb08809dffcb62e9bc493840f9c039']='python.d/squid.conf' + ['78e0065738394f5bf15023f41d66ed4b']='python.d/squid.conf' + ['79a37756869d9b4629285922572d6b9b']='apps_groups.conf' + ['7a21ccc76be2968ce5d0b52ec1166788']='python.d.conf' ['7a985528cc9176564640001aa73e3492']='health.d/nginx.conf' - ['81255035f6d53534938085df72cdef23']='health.d/nginx.conf' - ['eb5168f0b516bc982aac45e59da6e52e']='health.d/nginx.conf' - ['36fdd55665cf10b0db164c2a0cca5e57']='health.d/qos.conf' - ['55608bdd908a3806df1468f6ee318b2b']='health.d/qos.conf' + ['7aa209fa287c95b3ca04c23681b40770']='health.d/disks.conf' + ['7bac18d8d5ff8f117be8d489a21c0c65']='python.d/mysql.conf' + ['7cf6402b51e5070f2be3ad6fe059ff89']='charts.d.conf' + ['7d8bd884ec26cb35d16c4fc05f969799']='python.d/squid.conf' + ['7deb236ec68a512b9bdd18e6a51d76f7']='python.d/mysql.conf' + ['7e5fc1644aa7a54f9dbb1bd102521b09']='health.d/memcached.conf' + ['80266bddd3df374923c750a6de91d120']='health.d/apache.conf' + ['80d242d619eb7e91cebfdbf58d79b0f8']='health.d/disks.conf' + ['80df37b89e852d585209b8c02bb94312']='python.d/bind_rndc.conf' ['80f109ff293ac94222bf3959432751bd']='health.d/qos.conf' - ['b735732fbe993d8191d6b3317082efa2']='health.d/qos.conf' - ['eb748d6fb69d11b0d29c5794657e206c']='health.d/qos.conf' - ['20be73f473e59bc7de1fe61d53466aba']='health.d/ram.conf' + ['81255035f6d53534938085df72cdef23']='health.d/nginx.conf' + ['8213d921b6a8382e27052fb42d81db3d']='python.d/freeradius.conf' + ['8214bb8f4b005aa4691fcd38f7331e8f']='health.d/swap.conf' + ['837480f77ba1a85677a36747fbc2cd2e']='python.d/sensors.conf' + ['8425a60ea3d28ed40bb0bac4c3f182e8']='python.d/sensors.conf' + ['842b1ad5b89bfa5f421d9c5b72e001a4']='health.d/apache.conf' + ['845023f9b4a526aa0e6493756dbe6034']='health.d/squid.conf' + ['846ce94bfeeb90c0dc6a89e8d25f1a68']='health.d/named.conf' + ['8490f690d97adacc4e2096df82e7e8a5']='charts.d/cpufreq.conf' + ['8810140ce9c09af1d18b9602c4003904']='health_alarm_notify.conf' + ['88f77865f75c9fb61c97d700bd4561ee']='python.d/mysql.conf' + ['8989b5e2f4ef9cd278ef58be0fae4074']='health.d/disks.conf' + ['899bcb0b3f4375b0a1280296be930201']='health.d/named.conf' + ['89fb3cbb223be4fa0cb676cfa3b07055']='health.d/backend.conf' + ['8a1b95d375992d7b11330a0ac46f369c']='health.d/disks.conf' + ['8a66a3085ad8892a002ff39b18b2cb07']='python.d/fail2ban.conf' + ['8c1d41e2c88aeca78bc319ed74c8748c']='python.d/phpfpm.conf' + ['8d0552371a7c9725a04196fa560813d1']='health.d/cpu.conf' + ['8dc0bd0a70b5117454bd5f5b98f91c2c']='health.d/disks.conf' + ['8fd472a854b0996327e8ed3562161182']='health_alarm_notify.conf' ['919911d13901d60a7580f5dfd7fc87bb']='health.d/ram.conf' - ['a09714b5942cf25a89ec3da1dbc18063']='health.d/ram.conf' - ['b7d769ce86a7aebba01315da5c0799e6']='health.d/ram.conf' - ['cd08e5534c94bf1f2cd28396c76b8bbc']='health.d/ram.conf' - ['d55bdb83b9ff606852f6a97c1430258c']='health.d/ram.conf' - ['325617412a628e3bc776e3fbb777a2a6']='health.d/redis.conf' - ['3634d5eddc46fb0d50cf47f370670c2c']='health.d/redis.conf' - ['4f6a5b47a13f5912cc89e9286701dd08']='health.d/redis.conf' + ['91c757ef6be3abdb86906d9dbb9c217a']='fping.conf' + ['91cf3b3d42cac969b8b3fd4f531ecfb3']='python.d/squid.conf' + ['91e1a9703debbdc64edf124419fdc14b']='python.d/elasticsearch.conf' + ['9542f80def48ba105190f6cdaa18248e']='health.d/mysql.conf' + ['97eee7a30e6419df4537242e9d4a719d']='health.d/mysql.conf' + ['97f337eb96213f3ede05e522e3743a6c']='python.d/memcached.conf' + ['99a3de85d1e7826ed64a5f8576712e5d']='python.d.conf' ['99b6030ce25c8fee4598179c0f95fb0b']='health.d/redis.conf' - ['a4be524cc5b7192878c292a17c767c28']='health.d/redis.conf' - ['39f9422b0f0c3eec11a31aff79d89514']='health.d/retroshare.conf' - ['46ef6c1b638e40a7dfd62defdc5f99a3']='health.d/retroshare.conf' - ['6608c6546b3c6bde084fc1d34b1163c1']='health.d/retroshare.conf' - ['1c12b678ab65f271a96da1bbd0a1ab1c']='health.d/softnet.conf' - ['2472e49550326f7142e2c425ccbca005']='health.d/softnet.conf' - ['312b4b8e2805e19cf9be554b319567d6']='health.d/softnet.conf' - ['565f11c38ae6bd5cc9d3c2adb542bc1b']='health.d/softnet.conf' - ['6398ef37a15cb6a0bc921f58948d2b39']='health.d/softnet.conf' + ['99c1617448abbdc493976ab9bda5ce02']='apps_groups.conf' + ['9a8a459a3841b78d4c6ef07428ad2fe1']='health.d/entropy.conf' + ['9c0185ceff15415bc59b2ce2c1f04367']='apps_groups.conf' + ['9c981c75bdf4b1637f7113e7e45eb2bf']='health.d/memcached.conf' + ['9e0553ebdc21b64295873fc104cfa79d']='python.d.conf' + ['9eb3326ae2ee9badeaad31d8dd2eaa2b']='python.d/isc_dhcpd.conf' + ['a02d14124b19c635c1426cee2e98bac5']='charts.d.conf' + ['a09714b5942cf25a89ec3da1dbc18063']='health.d/ram.conf' + ['a0b3a12389c9c56dfe35964b20b59836']='health.d/bind_rndc.conf' + ['a0ee8f351f213c0e8af9eb7a4a09cb95']='apps_groups.conf' + ['a2944a309f8ce1a3195451856478d6ae']='python.d.conf' ['a305b400378d6492efd15f9940c2779b']='health.d/softnet.conf' - ['e734c5951a8764d4d9de046dd7cf7407']='health.d/softnet.conf' - ['23ae815aefa221b1929f96752a1f7556']='health.d/squid.conf' - ['3cc6255457d4cba881ae0554ae5d9190']='health.d/squid.conf' - ['845023f9b4a526aa0e6493756dbe6034']='health.d/squid.conf' - ['a4a8660728c6afcb528cc6b378897d6b']='health.d/squid.conf' - ['d162b7465a56151312e60151c1d74fba']='health.d/squid.conf' - ['ef9916ea144878a9f37cbb6b1b29da10']='health.d/squid.conf' - ['043f0a35dde85837fabeb85b990a41c1']='health.d/swap.conf' - ['52d4131cf9df84e2550b1a5d899ec61d']='health.d/swap.conf' - ['53160707fdc6ce46c195b1b55bb0bcb1']='health.d/swap.conf' - ['8214bb8f4b005aa4691fcd38f7331e8f']='health.d/swap.conf' + ['a4407787e4beb23a701a8a614dca461d']='health.d/disks.conf' ['a44899a5795bed2863c1d11aa3e85586']='health.d/swap.conf' - ['a7320c6f26191b9599ec3bc4be007a93']='health.d/swap.conf' - ['c9b792755de59d842ba95f8c315d94c8']='health.d/swap.conf' - ['ca08a9b18d38ae0a0f5081a7cdc96863']='health.d/swap.conf' - ['da29d2ab1ab7b8fda189960c840e5144']='health.d/swap.conf' - ['b3fc4749b132e55ac0d3a0f92859237e']='health.d/tcp_resets.conf' - ['2fa8fb929fd597f2ab97b6efc540a043']='health_alarm_notify.conf' - ['42ad0e70b1365b6e7244cc305dbaa529']='health_alarm_notify.conf' - ['707a63f53f4b32e01d134ae90ba94aad']='health_alarm_notify.conf' - ['8fd472a854b0996327e8ed3562161182']='health_alarm_notify.conf' + ['a4a8660728c6afcb528cc6b378897d6b']='health.d/squid.conf' + ['a4be524cc5b7192878c292a17c767c28']='health.d/redis.conf' + ['a5114d5b0d3816dba75024b9444f4b40']='health.d/disks.conf' ['a55133f1b0be0a4255057849dd451b09']='health_alarm_notify.conf' - ['a89c516a1144435a88decf25509318ac']='health_alarm_notify.conf' - ['b68706bb8101ef85192db92f865a5d80']='health_alarm_notify.conf' - ['ce2e8768964a936f58c4c2144aee8a01']='health_alarm_notify.conf' - ['e3023092e3b2bbb5351e0fe6682f4fe9']='health_alarm_notify.conf' - ['f8dade4484f1b6a48655388502df7d5a']='health_alarm_notify.conf' - ['fd3164e6e8cb6726706267eae49aa082']='health_alarm_notify.conf' - ['ff1b3d8ae8b2149c711d8da9b7a9c4bd']='health_alarm_notify.conf' - ['707a63f53f4b32e01d134ae90ba94aad']='health_email_recipients.conf' + ['a6d5ce2572bf7a1dce9e545fcd29273e']='health.d/apache.conf' + ['a71d9082410200bf92e823675d78121c']='python.d/retroshare.conf' + ['a731b7b164f42717c1c9a778ee637ff3']='health.d/memcached.conf' + ['a7320c6f26191b9599ec3bc4be007a93']='health.d/swap.conf' ['a752e51d923e15add4a11fa8f3be935a']='health_email_recipients.conf' - ['13141998a5d71308d9c119834c27bfd3']='python.d.conf' - ['38d1bf04fe9901481dd6febcc0404a86']='python.d.conf' - ['4b775fb31342f1478b3773d041a72911']='python.d.conf' - ['4fdf72784296326e0b46cb526a5d77a1']='python.d.conf' - ['7a21ccc76be2968ce5d0b52ec1166788']='python.d.conf' - ['99a3de85d1e7826ed64a5f8576712e5d']='python.d.conf' - ['9e0553ebdc21b64295873fc104cfa79d']='python.d.conf' - ['a2944a309f8ce1a3195451856478d6ae']='python.d.conf' + ['a7cceeafb1e6ef1ead503ab65f687902']='apps_groups.conf' + ['a8167dafeac0b66696a1d9b08e815cda']='health.d/disks.conf' + ['a837986be634fd7648bcdf939019424a']='apps_groups.conf' + ['a89c516a1144435a88decf25509318ac']='health_alarm_notify.conf' + ['a8bb4e1d0525f59692778ad8f675a77a']='python.d/example.conf' + ['a8feb36776005bf419c90278787a1be8']='health.d/entropy.conf' + ['a94af1c808aafdf00537d85ff2197ec8']='python.d/exim.conf' + ['a9ab68845db2fb695b7060273a6ac68e']='health_alarm_notify.conf' + ['a9cd91675467c5426f5b51c47602c889']='apps_groups.conf' + ['aa4bee249bfc0c4a88ac8c2ffb97aa0d']='health.d/squid.conf' + ['aa8b57a733c2035917acf81a8ebdfbe7']='health.d/haproxy.conf' + ['abaf2e021f9f6ee5d1c4e4726f47348e']='health.d/ipc.conf' + ['acaa6731a272f6d251afb357e99b518f']='apps_groups.conf' + ['ade389c1b6efe0cff47c33e662731f0a']='python.d/squid.conf' + ['ae5ac0a3521e50aa6f6eda2a330b4075']='python.d/example.conf' ['af44cc53aa2bc5cc8935667119567522']='python.d.conf' + ['afdae4646c755ff2d117527fbf761c8e']='health.d/disks.conf' + ['b07eebc6f58d19721ac069171b911d2a']='health_alarm_notify.conf' + ['b0c59b2bd7a10f6a3f2be6b4b27857db']='health.d/haproxy.conf' + ['b0f0a0ac415e4b1a82187b80d211e83b']='python.d/mysql.conf' + ['b185914d4f795e1732273dc4c7a35845']='health.d/memory.conf' ['b27f10a38a95edbbec20f44a4728b7c4']='python.d.conf' ['b32164929eda7449a9677044e11151bf']='python.d.conf' + ['b3fc4749b132e55ac0d3a0f92859237e']='health.d/tcp_resets.conf' + ['b5b5a8d6d991fb1cef8d80afa23ba114']='python.d/cpufreq.conf' + ['b636e5e603f9d93e52c7577ac8c6bf0c']='health.d/entropy.conf' + ['b68706bb8101ef85192db92f865a5d80']='health_alarm_notify.conf' + ['b735732fbe993d8191d6b3317082efa2']='health.d/qos.conf' + ['b7d769ce86a7aebba01315da5c0799e6']='health.d/ram.conf' + ['b81b8f331161b0d48e03f6fbf6b6d062']='health.d/memcached.conf' + ['b846ca1f99fa6a65303b58186f47d7a4']='python.d/squid.conf' ['b8969be5b3ceb4a99477937119bd4323']='python.d.conf' + ['b8b87574fd496a66ede884c5336493bd']='python.d/phpfpm.conf' + ['b915126262d08aa9da81de539a58a3fb']='python.d/redis.conf' + ['bb51112d01ff20053196a57632df8962']='apps_groups.conf' ['bba2f3886587f137ea08a6e63dd3d376']='python.d.conf' - ['d55be5bb5e108da1e7645da007c53cd4']='python.d.conf' - ['f82924563e41d99cdae5431f0af69155']='python.d.conf' - ['5278ebbae19c60db600f0a119cb3664e']='python.d/apache.conf' - ['5829812db29598db5857c9f433e96fef']='python.d/apache.conf' - ['6bf0de6e3b251b765b10a71d8c5c319d']='python.d/apache.conf' - ['6b917300747e7e8314844237e2462261']='python.d/apache_cache.conf' - ['e0e96cc47ed61d6492416be5236cd4d3']='python.d/apache_cache.conf' - ['7830066c46a7e5f9682b8d3f4566b4e5']='python.d/cpufreq.conf' - ['b5b5a8d6d991fb1cef8d80afa23ba114']='python.d/cpufreq.conf' - ['dc0d2b96378f290eec3fcf98b89ad824']='python.d/cpufreq.conf' - ['e40947d22f7ed5359f12fc89e3512963']='python.d/dovecot.conf' - ['a8bb4e1d0525f59692778ad8f675a77a']='python.d/example.conf' - ['ae5ac0a3521e50aa6f6eda2a330b4075']='python.d/example.conf' - ['e5f32f54d6d6728f21f9ac26f37d6573']='python.d/example.conf' - ['15e32114994b92be7853b88091e7c6fb']='python.d/exim.conf' - ['54614490a14e1a4b7b3d9fecb6b4cfa5']='python.d/exim.conf' - ['73125ae64d5c6e9361944cd9bd14844e']='python.d/exim.conf' - ['a94af1c808aafdf00537d85ff2197ec8']='python.d/exim.conf' - ['2a0794fd43eadf30a51805bc9ba2c64d']='python.d/hddtemp.conf' - ['3fc45cc18e884c22482524dff6d27833']='python.d/hddtemp.conf' - ['421d5dc6c2fce22d0816b6e6363bea57']='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' - ['42bf1c7c64ed77038a0aa094d792a9e2']='python.d/mysql.conf' - ['5379cdc26d7725e2b0d688d785816cef']='python.d/mysql.conf' - ['7deb236ec68a512b9bdd18e6a51d76f7']='python.d/mysql.conf' - ['88f77865f75c9fb61c97d700bd4561ee']='python.d/mysql.conf' - ['b0f0a0ac415e4b1a82187b80d211e83b']='python.d/mysql.conf' - ['df381f3a7ca9fb2b4b43ae7cb7a4c492']='python.d/mysql.conf' - ['061c45b0e34170d357e47883166ecf40']='python.d/nginx.conf' - ['08042325ab27256b938575deafee8ecf']='python.d/nginx.conf' - ['21924a6ab8008d16ffac340f226ebad9']='python.d/nginx.conf' + ['bda5517ea01640cfdfa0a27549619d6a']='health.d/memcached.conf' + ['c004430f55310ae9ed489c4905ed02cb']='charts.d/apache.conf' + ['c080e006f544c949baca33cc24a9c126']='health_alarm_notify.conf' + ['c1a7e634b5b8aad523a0d115a93379cd']='health.d/memcached.conf' + ['c3296c08260bcd556e74711c820817be']='health.d/cpu.conf' ['c61948101e0e6846679682794ee48c5b']='python.d/nginx.conf' - ['ce3b65eac6c472b21905f7f72104f4c9']='python.d/nginx.conf' - ['6cba40e32a7e98a98c31a209913839cc']='python.d/nginx_log.conf' - ['3ca696189911fb38a0319ddd71e9a395']='python.d/phpfpm.conf' - ['8c1d41e2c88aeca78bc319ed74c8748c']='python.d/phpfpm.conf' - ['b8b87574fd496a66ede884c5336493bd']='python.d/phpfpm.conf' ['c88fb430f35b7d8f08775d84debffbd2']='python.d/phpfpm.conf' - ['d2b2ad30e277a69d8713e620dabc18bc']='python.d/phpfpm.conf' - ['d7e0bd12d4a60a761dcab3531a841711']='python.d/phpfpm.conf' - ['142a5b693d34b0308bb0b8aec71fad79']='python.d/postfix.conf' + ['c9b792755de59d842ba95f8c315d94c8']='health.d/swap.conf' + ['ca026d7c779f0a7cb7787713c5be5c47']='charts.d.conf' + ['ca08a9b18d38ae0a0f5081a7cdc96863']='health.d/swap.conf' + ['ca0eb92bdd3de67582ea6db37462895f']='health.d/tcp_resets.conf' ['ca249db7a0637d55abb938d969f9b486']='python.d/postfix.conf' - ['39571e9fad9b759200c5d5b2ee13feb4']='python.d/redis.conf' - ['777f4da70f461ef675bde07fb3644312']='python.d/redis.conf' - ['b915126262d08aa9da81de539a58a3fb']='python.d/redis.conf' - ['749fe31362969d75f1ea66d15231d98d']='python.d/retroshare.conf' - ['a71d9082410200bf92e823675d78121c']='python.d/retroshare.conf' - ['837480f77ba1a85677a36747fbc2cd2e']='python.d/sensors.conf' + ['cb178b15427274d7def5b14bc4c09441']='health.d/net.conf' + ['cb60badf376d246ad8ec9d3f524db430']='health.d/disks.conf' + ['cb7f80cd2768c649d7448e01f8aa6579']='python.d.conf' + ['cc4d31a0d1ff9c339892c1f8a0c5fcd3']='charts.d/load_average.conf' + ['ccde91d209aeb02c4a6be0e43a8d92b3']='health.d/apache.conf' + ['cd08e5534c94bf1f2cd28396c76b8bbc']='health.d/ram.conf' + ['cdd504812ff93073c57d02209d4d0f69']='health.d/cpu.conf' + ['ce2e8768964a936f58c4c2144aee8a01']='health_alarm_notify.conf' + ['ce3b65eac6c472b21905f7f72104f4c9']='python.d/nginx.conf' + ['cf48dfd828af70bea04db7a809f94358']='health.d/haproxy.conf' ['cfecf298bdafaa7e0a3a263548e82132']='python.d/sensors.conf' - ['48195c5c8c0476a49b714b4c76bdb570']='python.d/squid.conf' - ['64070d856ab1b47a18ec871e49bbc13b']='python.d/squid.conf' - ['78bb08809dffcb62e9bc493840f9c039']='python.d/squid.conf' - ['78e0065738394f5bf15023f41d66ed4b']='python.d/squid.conf' - ['7d8bd884ec26cb35d16c4fc05f969799']='python.d/squid.conf' - ['91cf3b3d42cac969b8b3fd4f531ecfb3']='python.d/squid.conf' - ['ade389c1b6efe0cff47c33e662731f0a']='python.d/squid.conf' - ['b846ca1f99fa6a65303b58186f47d7a4']='python.d/squid.conf' + ['d11711b3647bc2bdd0292dd7deebbeb1']='health.d/net.conf' + ['d1596fe068c8674efade49a4a8e22b5d']='health.d/isc_dhcpd.conf' + ['d162b7465a56151312e60151c1d74fba']='health.d/squid.conf' + ['d1e79707cd9b51a14288e8dd40694fcc']='fping.conf' + ['d2b2ad30e277a69d8713e620dabc18bc']='python.d/phpfpm.conf' + ['d55bdb83b9ff606852f6a97c1430258c']='health.d/ram.conf' + ['d55be5bb5e108da1e7645da007c53cd4']='python.d.conf' + ['d5dab509d8792f795bece27de39dd476']='health.d/mysql.conf' + ['d74dc63fbe631dab9a2ff1b0f5d71719']='python.d/hddtemp.conf' + ['d7e0bd12d4a60a761dcab3531a841711']='python.d/phpfpm.conf' + ['d8dc489e32f7114c6298fce94e86a8ef']='health.d/entropy.conf' + ['d9036091e2232fc2b8bfa8c7484dea28']='apps_groups.conf' + ['d9258e671d0d0b6498af1ce16ef030d2']='apps_groups.conf' + ['da29d2ab1ab7b8fda189960c840e5144']='health.d/swap.conf' + ['dad303c5cca7a69345811a01a74f5892']='health.d/net.conf' + ['dc0d2b96378f290eec3fcf98b89ad824']='python.d/cpufreq.conf' + ['dc9c2a66778623a759706c14c3d91983']='health.d/net.conf' + ['dd7764507804a2296bfd091a58ad4ad7']='health.d/memcached.conf' + ['dd8254ef74509a3e38cb2838e30f7e63']='health.d/disks.conf' + ['ddda2bb1c88be03b637d3285406f7910']='health.d/named.conf' + ['dddc4f93e6187fe4220eb6bf5e20f095']='health.d/ram.conf' + ['de02f899a61f21b86adb646940f0bcae']='health.d/net.conf' + ['def883f35986c9d25de63b1a8e7d0f46']='health.d/entropy.conf' + ['df381f3a7ca9fb2b4b43ae7cb7a4c492']='python.d/mysql.conf' + ['dfd5431b11cf2f3852a40d390c1d5a92']='python.d/varnish.conf' + ['e0242003fd2e3f9ac1b9314e802ada79']='python.d/hddtemp.conf' + ['e0e96cc47ed61d6492416be5236cd4d3']='python.d/apache_cache.conf' + ['e2f3388c06726154c10ec22bad5bc7ec']='fping.conf' + ['e3023092e3b2bbb5351e0fe6682f4fe9']='health_alarm_notify.conf' ['e3e5bc57335c489f01b8559f5c70e112']='python.d/squid.conf' - ['0388b873d0d7e47c19005b7241db77d8']='python.d/tomcat.conf' + ['e40947d22f7ed5359f12fc89e3512963']='python.d/dovecot.conf' + ['e449e5582279742496550df14b6fca95']='health.d/entropy.conf' + ['e5f32f54d6d6728f21f9ac26f37d6573']='python.d/example.conf' + ['e734c5951a8764d4d9de046dd7cf7407']='health.d/softnet.conf' + ['e7bc22a1942cffbd2b1b0cfd119ee328']='health.d/ipfs.conf' + ['e8ec8046c7007af6ca3e8c51e62c99f8']='health.d/disks.conf' + ['eb5168f0b516bc982aac45e59da6e52e']='health.d/nginx.conf' + ['eb748d6fb69d11b0d29c5794657e206c']='health.d/qos.conf' + ['ebd0612ccc5807524ebb2b647e3e56c9']='apps_groups.conf' + ['ec490e037c1e53daa44a55a941381d6d']='python.d/gunicorn_log.conf' + ['ecd3aa97e2581f88eb466d6612690ef2']='charts.d/nginx.conf' + ['ee5343881744e6a97e6ee5cdd329cfb8']='health.d/retroshare.conf' + ['ef1861bf5725d91e773cbdba05687597']='python.d.conf' + ['ef9916ea144878a9f37cbb6b1b29da10']='health.d/squid.conf' + ['f2f1b8656f5011e965ac45b818cf668d']='apps_groups.conf' + ['f42df9f13abfae2426519c6728b34882']='charts.d/example.conf' + ['f4c5d88c34d3fb853498124177cc77f1']='python.d.conf' + ['f5736e0b2945182cb659cb0713eff923']='apps_groups.conf' + ['f66e5236ba1245bb2e5fd99191f114c6']='charts.d/hddtemp.conf' + ['f6c6656f900ff52d159dca12d624016a']='python.d/postgres.conf' ['f7a99e94231beda85c6254912d8d31c1']='python.d/tomcat.conf' + ['f82924563e41d99cdae5431f0af69155']='python.d.conf' + ['f8c30f22df92765e2c0fab3c8174e2fc']='health.d/memcached.conf' + ['f8dade4484f1b6a48655388502df7d5a']='health_alarm_notify.conf' + ['f9be549a849d023595d19d5d74263e0f']='health.d/tcp_resets.conf' + ['fa4396513b358d6ec6a7f5bfb08439b8']='health.d/net.conf' + ['fd3164e6e8cb6726706267eae49aa082']='health_alarm_notify.conf' + ['fdd11640ba626cc2064c2fe3ea3eee4c']='health.d/cpu.conf' + ['fde44f62c8d7e52f09705cd273fae6b1']='charts.d/tomcat.conf' + ['fdea185e0e52b459b48852aa37f20e0f']='apps_groups.conf' + ['fe478efe2e721724edb1fe2ef1addf93']='health_alarm_notify.conf' + ['feb8bcf828aa2529a7ee4a140feeb12d']='health.d/net.conf' + ['ff1b3d8ae8b2149c711d8da9b7a9c4bd']='health_alarm_notify.conf' ) @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for netdata 1.4.0. +# Generated by GNU Autoconf 2.69 for netdata 1.5.0. # # # Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. @@ -577,8 +577,8 @@ MAKEFLAGS= # Identity of this package. PACKAGE_NAME='netdata' PACKAGE_TARNAME='netdata' -PACKAGE_VERSION='1.4.0' -PACKAGE_STRING='netdata 1.4.0' +PACKAGE_VERSION='1.5.0' +PACKAGE_STRING='netdata 1.5.0' PACKAGE_BUGREPORT='' PACKAGE_URL='' @@ -659,29 +659,16 @@ PTHREAD_CFLAGS PTHREAD_LIBS PTHREAD_CC ax_pthread_config +MACOS_FALSE +MACOS_TRUE +FREEBSD_FALSE +FREEBSD_TRUE EGREP GREP CPP PKG_CONFIG_LIBDIR PKG_CONFIG_PATH PKG_CONFIG -am__fastdepCC_FALSE -am__fastdepCC_TRUE -CCDEPMODE -am__nodep -AMDEPBACKSLASH -AMDEP_FALSE -AMDEP_TRUE -am__quote -am__include -DEPDIR -OBJEXT -EXEEXT -ac_ct_CC -CPPFLAGS -LDFLAGS -CFLAGS -CC host_os host_vendor host_cpu @@ -694,6 +681,16 @@ AM_BACKSLASH AM_DEFAULT_VERBOSITY AM_DEFAULT_V AM_V +am__fastdepCC_FALSE +am__fastdepCC_TRUE +CCDEPMODE +am__nodep +AMDEPBACKSLASH +AMDEP_FALSE +AMDEP_TRUE +am__quote +am__include +DEPDIR am__untar am__tar AMTAR @@ -717,6 +714,13 @@ am__isrc INSTALL_DATA INSTALL_SCRIPT INSTALL_PROGRAM +OBJEXT +EXEEXT +ac_ct_CC +CPPFLAGS +LDFLAGS +CFLAGS +CC PACKAGE_RPM_RELEASE PACKAGE_RPM_VERSION MAINT @@ -764,8 +768,8 @@ ac_subst_files='' ac_user_opts=' enable_option_checking enable_maintainer_mode -enable_silent_rules enable_dependency_tracking +enable_silent_rules enable_plugin_nfacct enable_pedantic with_webdir @@ -1341,7 +1345,7 @@ if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures netdata 1.4.0 to adapt to many kinds of systems. +\`configure' configures netdata 1.5.0 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1411,7 +1415,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of netdata 1.4.0:";; + short | recursive ) echo "Configuration of netdata 1.5.0:";; esac cat <<\_ACEOF @@ -1422,12 +1426,12 @@ Optional Features: --enable-maintainer-mode enable make rules and dependencies not useful (and sometimes confusing) to the casual installer - --enable-silent-rules less verbose build output (undo: "make V=1") - --disable-silent-rules verbose build output (undo: "make V=0") --enable-dependency-tracking do not reject slow dependency extractors --disable-dependency-tracking speeds up one-time build + --enable-silent-rules less verbose build output (undo: "make V=1") + --disable-silent-rules verbose build output (undo: "make V=0") --enable-plugin-nfacct enable nfacct plugin, requires root --enable-pedantic enable pedantic compiler warnings --disable-x86-sse SSE/SS2 optimizations on x86 [default enabled] @@ -1541,7 +1545,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -netdata configure 1.4.0 +netdata configure 1.5.0 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. @@ -1593,6 +1597,52 @@ fi } # ac_fn_c_try_compile +# ac_fn_c_try_link LINENO +# ----------------------- +# Try to link conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_link () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext conftest$ac_exeext + if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + test -x conftest$ac_exeext + }; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information + # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would + # interfere with the next link command; also delete a directory that is + # left behind by Apple's compiler. We do this before executing the actions. + rm -rf conftest.dSYM conftest_ipa8_conftest.oo + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_link + # ac_fn_c_try_cpp LINENO # ---------------------- # Try to preprocess conftest.$ac_ext, and return whether this succeeded. @@ -1790,52 +1840,6 @@ $as_echo "$ac_res" >&6; } } # ac_fn_c_check_header_compile -# ac_fn_c_try_link LINENO -# ----------------------- -# Try to link conftest.$ac_ext, and return whether this succeeded. -ac_fn_c_try_link () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - rm -f conftest.$ac_objext conftest$ac_exeext - if { { ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_link") 2>conftest.err - ac_status=$? - if test -s conftest.err; then - grep -v '^ *+' conftest.err >conftest.er1 - cat conftest.er1 >&5 - mv -f conftest.er1 conftest.err - fi - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest$ac_exeext && { - test "$cross_compiling" = yes || - test -x conftest$ac_exeext - }; then : - ac_retval=0 -else - $as_echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - ac_retval=1 -fi - # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information - # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would - # interfere with the next link command; also delete a directory that is - # left behind by Apple's compiler. We do this before executing the actions. - rm -rf conftest.dSYM conftest_ipa8_conftest.oo - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - as_fn_set_status $ac_retval - -} # ac_fn_c_try_link - # ac_fn_c_check_func LINENO FUNC VAR # ---------------------------------- # Tests whether FUNC exists, setting the cache variable VAR accordingly @@ -1903,6 +1907,60 @@ $as_echo "$ac_res" >&6; } } # ac_fn_c_check_func +# ac_fn_c_check_type LINENO TYPE VAR INCLUDES +# ------------------------------------------- +# Tests whether TYPE exists after having included INCLUDES, setting cache +# variable VAR accordingly. +ac_fn_c_check_type () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + eval "$3=no" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +if (sizeof ($2)) + return 0; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +if (sizeof (($2))) + return 0; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + +else + eval "$3=yes" +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_type + # ac_fn_c_find_uintX_t LINENO BITS VAR # ------------------------------------ # Finds an unsigned integer type with width BITS, setting cache variable VAR @@ -2189,7 +2247,7 @@ cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by netdata $as_me 1.4.0, which was +It was created by netdata $as_me 1.5.0, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -2568,7 +2626,1142 @@ $as_echo "$as_me: ***************** MAINTAINER MODE *****************" >&6;} PACKAGE_BUILT_DATE=$(date '+%d %b %Y') fi -PACKAGE_RPM_VERSION="1.4.0" +PACKAGE_RPM_VERSION="1.5.0" + + + +# fails on centos6 +#AX_CHECK_ENABLE_DEBUG() + +ac_aux_dir= +for ac_dir in "$srcdir" "$srcdir/.." "$srcdir/../.."; do + if test -f "$ac_dir/install-sh"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install-sh -c" + break + elif test -f "$ac_dir/install.sh"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install.sh -c" + break + elif test -f "$ac_dir/shtool"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/shtool install -c" + break + fi +done +if test -z "$ac_aux_dir"; then + as_fn_error $? "cannot find install-sh, install.sh, or shtool in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" "$LINENO" 5 +fi + +# These three variables are undocumented and unsupported, +# and are intended to be withdrawn in a future Autoconf release. +# They can cause serious problems if a builder's source tree is in a directory +# whose full name contains unusual characters. +ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var. +ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var. +ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var. + + +# Expand $ac_aux_dir to an absolute path. +am_aux_dir=`cd "$ac_aux_dir" && pwd` + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. +set dummy ${ac_tool_prefix}gcc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}gcc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="gcc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +else + CC="$ac_cv_prog_CC" +fi + +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. +set dummy ${ac_tool_prefix}cc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}cc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + fi +fi +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + ac_prog_rejected=no +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# != 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" + fi +fi +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + for ac_prog in cl.exe + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="$ac_tool_prefix$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$CC" && break + done +fi +if test -z "$CC"; then + ac_ct_CC=$CC + for ac_prog in cl.exe +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$ac_ct_CC" && break +done + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +fi + +fi + + +test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "no acceptable C compiler found in \$PATH +See \`config.log' for more details" "$LINENO" 5; } + +# Provide some information about the compiler. +$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 +set X $ac_compile +ac_compiler=$2 +for ac_option in --version -v -V -qversion; do + { { ac_try="$ac_compiler $ac_option >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compiler $ac_option >&5") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + sed '10a\ +... rest of stderr output deleted ... + 10q' conftest.err >conftest.er1 + cat conftest.er1 >&5 + fi + rm -f conftest.er1 conftest.err + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } +done + +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out" +# Try to create an executable without -o first, disregard a.out. +# It will help us diagnose broken compilers, and finding out an intuition +# of exeext. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5 +$as_echo_n "checking whether the C compiler works... " >&6; } +ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` + +# The possible output files: +ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" + +ac_rmfiles= +for ac_file in $ac_files +do + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; + * ) ac_rmfiles="$ac_rmfiles $ac_file";; + esac +done +rm -f $ac_rmfiles + +if { { ac_try="$ac_link_default" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link_default") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then : + # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. +# So ignore a value of `no', otherwise this would lead to `EXEEXT = no' +# in a Makefile. We should not override ac_cv_exeext if it was cached, +# so that the user can short-circuit this test for compilers unknown to +# Autoconf. +for ac_file in $ac_files '' +do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) + ;; + [ab].out ) + # We found the default executable, but exeext='' is most + # certainly right. + break;; + *.* ) + if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no; + then :; else + ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + fi + # We set ac_cv_exeext here because the later test for it is not + # safe: cross compilers may not add the suffix if given an `-o' + # argument, so we may need to know it at that point already. + # Even if this section looks crufty: it has the advantage of + # actually working. + break;; + * ) + break;; + esac +done +test "$ac_cv_exeext" = no && ac_cv_exeext= + +else + ac_file='' +fi +if test -z "$ac_file"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +$as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error 77 "C compiler cannot create executables +See \`config.log' for more details" "$LINENO" 5; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5 +$as_echo_n "checking for C compiler default output file name... " >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 +$as_echo "$ac_file" >&6; } +ac_exeext=$ac_cv_exeext + +rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out +ac_clean_files=$ac_clean_files_save +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 +$as_echo_n "checking for suffix of executables... " >&6; } +if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then : + # If both `conftest.exe' and `conftest' are `present' (well, observable) +# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will +# work properly (i.e., refer to `conftest.exe'), while it won't with +# `rm'. +for ac_file in conftest.exe conftest conftest.*; do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; + *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + break;; + * ) break;; + esac +done +else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details" "$LINENO" 5; } +fi +rm -f conftest conftest$ac_cv_exeext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 +$as_echo "$ac_cv_exeext" >&6; } + +rm -f conftest.$ac_ext +EXEEXT=$ac_cv_exeext +ac_exeext=$EXEEXT +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <stdio.h> +int +main () +{ +FILE *f = fopen ("conftest.out", "w"); + return ferror (f) || fclose (f) != 0; + + ; + return 0; +} +_ACEOF +ac_clean_files="$ac_clean_files conftest.out" +# Check that the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 +$as_echo_n "checking whether we are cross compiling... " >&6; } +if test "$cross_compiling" != yes; then + { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } + if { ac_try='./conftest$ac_cv_exeext' + { { case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; }; then + cross_compiling=no + else + if test "$cross_compiling" = maybe; then + cross_compiling=yes + else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot run C compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details" "$LINENO" 5; } + fi + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 +$as_echo "$cross_compiling" >&6; } + +rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out +ac_clean_files=$ac_clean_files_save +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 +$as_echo_n "checking for suffix of object files... " >&6; } +if ${ac_cv_objext+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.o conftest.obj +if { { ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compile") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then : + for ac_file in conftest.o conftest.obj conftest.*; do + test -f "$ac_file" || continue; + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; + *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` + break;; + esac +done +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot compute suffix of object files: cannot compile +See \`config.log' for more details" "$LINENO" 5; } +fi +rm -f conftest.$ac_cv_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 +$as_echo "$ac_cv_objext" >&6; } +OBJEXT=$ac_cv_objext +ac_objext=$OBJEXT +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 +$as_echo_n "checking whether we are using the GNU C compiler... " >&6; } +if ${ac_cv_c_compiler_gnu+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_compiler_gnu=yes +else + ac_compiler_gnu=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_c_compiler_gnu=$ac_compiler_gnu + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 +$as_echo "$ac_cv_c_compiler_gnu" >&6; } +if test $ac_compiler_gnu = yes; then + GCC=yes +else + GCC= +fi +ac_test_CFLAGS=${CFLAGS+set} +ac_save_CFLAGS=$CFLAGS +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 +$as_echo_n "checking whether $CC accepts -g... " >&6; } +if ${ac_cv_prog_cc_g+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_save_c_werror_flag=$ac_c_werror_flag + ac_c_werror_flag=yes + ac_cv_prog_cc_g=no + CFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_g=yes +else + CFLAGS="" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + +else + ac_c_werror_flag=$ac_save_c_werror_flag + CFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_g=yes +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_c_werror_flag=$ac_save_c_werror_flag +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 +$as_echo "$ac_cv_prog_cc_g" >&6; } +if test "$ac_test_CFLAGS" = set; then + CFLAGS=$ac_save_CFLAGS +elif test $ac_cv_prog_cc_g = yes; then + if test "$GCC" = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-g" + fi +else + if test "$GCC" = yes; then + CFLAGS="-O2" + else + CFLAGS= + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 +$as_echo_n "checking for $CC option to accept ISO C89... " >&6; } +if ${ac_cv_prog_cc_c89+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_cv_prog_cc_c89=no +ac_save_CC=$CC +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <stdarg.h> +#include <stdio.h> +struct stat; +/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ +struct buf { int x; }; +FILE * (*rcsopen) (struct buf *, struct stat *, int); +static char *e (p, i) + char **p; + int i; +{ + return p[i]; +} +static char *f (char * (*g) (char **, int), char **p, ...) +{ + char *s; + va_list v; + va_start (v,p); + s = g (p, va_arg (v,int)); + va_end (v); + return s; +} + +/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has + function prototypes and stuff, but not '\xHH' hex character constants. + These don't provoke an error unfortunately, instead are silently treated + as 'x'. The following induces an error, until -std is added to get + proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an + array size at least. It's necessary to write '\x00'==0 to get something + that's true only with -std. */ +int osf4_cc_array ['\x00' == 0 ? 1 : -1]; + +/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters + inside strings and character constants. */ +#define FOO(x) 'x' +int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; + +int test (int i, double x); +struct s1 {int (*f) (int a);}; +struct s2 {int (*f) (double a);}; +int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); +int argc; +char **argv; +int +main () +{ +return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; + ; + return 0; +} +_ACEOF +for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ + -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" +do + CC="$ac_save_CC $ac_arg" + if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_c89=$ac_arg +fi +rm -f core conftest.err conftest.$ac_objext + test "x$ac_cv_prog_cc_c89" != "xno" && break +done +rm -f conftest.$ac_ext +CC=$ac_save_CC + +fi +# AC_CACHE_VAL +case "x$ac_cv_prog_cc_c89" in + x) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 +$as_echo "none needed" >&6; } ;; + xno) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 +$as_echo "unsupported" >&6; } ;; + *) + CC="$CC $ac_cv_prog_cc_c89" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 +$as_echo "$ac_cv_prog_cc_c89" >&6; } ;; +esac +if test "x$ac_cv_prog_cc_c89" != xno; then : + +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC understands -c and -o together" >&5 +$as_echo_n "checking whether $CC understands -c and -o together... " >&6; } +if ${am_cv_prog_cc_c_o+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF + # Make sure it works both with $CC and with simple cc. + # Following AC_PROG_CC_C_O, we do the test twice because some + # compilers refuse to overwrite an existing .o file with -o, + # though they will create one. + am_cv_prog_cc_c_o=yes + for am_i in 1 2; do + if { echo "$as_me:$LINENO: $CC -c conftest.$ac_ext -o conftest2.$ac_objext" >&5 + ($CC -c conftest.$ac_ext -o conftest2.$ac_objext) >&5 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } \ + && test -f conftest2.$ac_objext; then + : OK + else + am_cv_prog_cc_c_o=no + break + fi + done + rm -f core conftest* + unset am_i +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_prog_cc_c_o" >&5 +$as_echo "$am_cv_prog_cc_c_o" >&6; } +if test "$am_cv_prog_cc_c_o" != yes; then + # Losing compiler, so override with the script. + # FIXME: It is wrong to rewrite CC. + # But if we don't then we get into trouble of one sort or another. + # A longer-term fix would be to have automake use am__CC in this case, + # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)" + CC="$am_aux_dir/compile $CC" +fi +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __attribute__((returns_nonnull))" >&5 +$as_echo_n "checking for __attribute__((returns_nonnull))... " >&6; } +if ${ax_cv_have_func_attribute_returns_nonnull+:} false; then : + $as_echo_n "(cached) " >&6 +else + + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + + void *foo( void ) __attribute__((returns_nonnull)); + +int +main () +{ + + ; + return 0; +} + +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + if test -s conftest.err; then : + ax_cv_have_func_attribute_returns_nonnull=no +else + ax_cv_have_func_attribute_returns_nonnull=yes +fi +else + ax_cv_have_func_attribute_returns_nonnull=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_have_func_attribute_returns_nonnull" >&5 +$as_echo "$ax_cv_have_func_attribute_returns_nonnull" >&6; } + + if test yes = $ax_cv_have_func_attribute_returns_nonnull; then : + +cat >>confdefs.h <<_ACEOF +#define HAVE_FUNC_ATTRIBUTE_RETURNS_NONNULL 1 +_ACEOF + +fi + + + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __attribute__((malloc))" >&5 +$as_echo_n "checking for __attribute__((malloc))... " >&6; } +if ${ax_cv_have_func_attribute_malloc+:} false; then : + $as_echo_n "(cached) " >&6 +else + + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + + void *foo( void ) __attribute__((malloc)); + +int +main () +{ + + ; + return 0; +} + +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + if test -s conftest.err; then : + ax_cv_have_func_attribute_malloc=no +else + ax_cv_have_func_attribute_malloc=yes +fi +else + ax_cv_have_func_attribute_malloc=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_have_func_attribute_malloc" >&5 +$as_echo "$ax_cv_have_func_attribute_malloc" >&6; } + + if test yes = $ax_cv_have_func_attribute_malloc; then : + +cat >>confdefs.h <<_ACEOF +#define HAVE_FUNC_ATTRIBUTE_MALLOC 1 +_ACEOF + +fi + + + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __attribute__((noreturn))" >&5 +$as_echo_n "checking for __attribute__((noreturn))... " >&6; } +if ${ax_cv_have_func_attribute_noreturn+:} false; then : + $as_echo_n "(cached) " >&6 +else + + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + + void foo( void ) __attribute__((noreturn)); + +int +main () +{ + + ; + return 0; +} + +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + if test -s conftest.err; then : + ax_cv_have_func_attribute_noreturn=no +else + ax_cv_have_func_attribute_noreturn=yes +fi +else + ax_cv_have_func_attribute_noreturn=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_have_func_attribute_noreturn" >&5 +$as_echo "$ax_cv_have_func_attribute_noreturn" >&6; } + + if test yes = $ax_cv_have_func_attribute_noreturn; then : + +cat >>confdefs.h <<_ACEOF +#define HAVE_FUNC_ATTRIBUTE_NORETURN 1 +_ACEOF + +fi + + + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __attribute__((format))" >&5 +$as_echo_n "checking for __attribute__((format))... " >&6; } +if ${ax_cv_have_func_attribute_format+:} false; then : + $as_echo_n "(cached) " >&6 +else + + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + + int foo(const char *p, ...) __attribute__((format(printf, 1, 2))); + +int +main () +{ + + ; + return 0; +} + +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + if test -s conftest.err; then : + ax_cv_have_func_attribute_format=no +else + ax_cv_have_func_attribute_format=yes +fi +else + ax_cv_have_func_attribute_format=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_have_func_attribute_format" >&5 +$as_echo "$ax_cv_have_func_attribute_format" >&6; } + + if test yes = $ax_cv_have_func_attribute_format; then : + +cat >>confdefs.h <<_ACEOF +#define HAVE_FUNC_ATTRIBUTE_FORMAT 1 +_ACEOF + +fi + + + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __attribute__((warn_unused_result))" >&5 +$as_echo_n "checking for __attribute__((warn_unused_result))... " >&6; } +if ${ax_cv_have_func_attribute_warn_unused_result+:} false; then : + $as_echo_n "(cached) " >&6 +else + + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + + int foo( void ) __attribute__((warn_unused_result)); + +int +main () +{ + + ; + return 0; +} + +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + if test -s conftest.err; then : + ax_cv_have_func_attribute_warn_unused_result=no +else + ax_cv_have_func_attribute_warn_unused_result=yes +fi +else + ax_cv_have_func_attribute_warn_unused_result=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_have_func_attribute_warn_unused_result" >&5 +$as_echo "$ax_cv_have_func_attribute_warn_unused_result" >&6; } + + if test yes = $ax_cv_have_func_attribute_warn_unused_result; then : + +cat >>confdefs.h <<_ACEOF +#define HAVE_FUNC_ATTRIBUTE_WARN_UNUSED_RESULT 1 +_ACEOF + +fi + @@ -2605,7 +3798,7 @@ ac_config_headers="$ac_config_headers config.h" -am__api_version='1.14' +am__api_version='1.15' # Find a good install program. We prefer a C program (faster), # so one script is as good as another. But avoid the broken or @@ -2777,9 +3970,6 @@ test "$program_suffix" != NONE && ac_script='s/[\\$]/&&/g;s/;s,x,x,$//' program_transform_name=`$as_echo "$program_transform_name" | sed "$ac_script"` -# expand $ac_aux_dir to an absolute path -am_aux_dir=`cd $ac_aux_dir && pwd` - if test x"${MISSING+set}" != xset; then case $am_aux_dir in *\ * | *\ *) @@ -2797,7 +3987,7 @@ else $as_echo "$as_me: WARNING: 'missing' script is too old or missing" >&2;} fi -if test x"${install_sh}" != xset; then +if test x"${install_sh+set}" != xset; then case $am_aux_dir in *\ * | *\ *) install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;; @@ -3030,6 +4220,69 @@ else fi rmdir .tst 2>/dev/null +DEPDIR="${am__leading_dot}deps" + +ac_config_commands="$ac_config_commands depfiles" + + +am_make=${MAKE-make} +cat > confinc << 'END' +am__doit: + @echo this is the am__doit target +.PHONY: am__doit +END +# If we don't find an include directive, just comment out the code. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for style of include used by $am_make" >&5 +$as_echo_n "checking for style of include used by $am_make... " >&6; } +am__include="#" +am__quote= +_am_result=none +# First try GNU make style include. +echo "include confinc" > confmf +# Ignore all kinds of additional output from 'make'. +case `$am_make -s -f confmf 2> /dev/null` in #( +*the\ am__doit\ target*) + am__include=include + am__quote= + _am_result=GNU + ;; +esac +# Now try BSD make style include. +if test "$am__include" = "#"; then + echo '.include "confinc"' > confmf + case `$am_make -s -f confmf 2> /dev/null` in #( + *the\ am__doit\ target*) + am__include=.include + am__quote="\"" + _am_result=BSD + ;; + esac +fi + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $_am_result" >&5 +$as_echo "$_am_result" >&6; } +rm -f confinc confmf + +# Check whether --enable-dependency-tracking was given. +if test "${enable_dependency_tracking+set}" = set; then : + enableval=$enable_dependency_tracking; +fi + +if test "x$enable_dependency_tracking" != xno; then + am_depcomp="$ac_aux_dir/depcomp" + AMDEPBACKSLASH='\' + am__nodep='_no' +fi + if test "x$enable_dependency_tracking" != xno; then + AMDEP_TRUE= + AMDEP_FALSE='#' +else + AMDEP_TRUE='#' + AMDEP_FALSE= +fi + + # Check whether --enable-silent-rules was given. if test "${enable_silent_rules+set}" = set; then : enableval=$enable_silent_rules; @@ -3091,7 +4344,7 @@ fi # Define the identity of the package. PACKAGE='netdata' - VERSION='1.4.0' + VERSION='1.5.0' cat >>confdefs.h <<_ACEOF @@ -3125,8 +4378,8 @@ MAKEINFO=${MAKEINFO-"${am_missing_run}makeinfo"} # <http://lists.gnu.org/archive/html/automake/2012-07/msg00014.html> mkdir_p='$(MKDIR_P)' -# We need awk for the "check" target. The system "awk" is bad on -# some platforms. +# We need awk for the "check" target (and possibly the TAP driver). The +# system "awk" is bad on some platforms. # Always define AMTAR for backward compatibility. Yes, it's still used # in the wild :-( We should find a proper way to deprecate it ... AMTAR='$${TAR-tar}' @@ -3141,6 +4394,134 @@ am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -' +depcc="$CC" am_compiler_list= + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5 +$as_echo_n "checking dependency style of $depcc... " >&6; } +if ${am_cv_CC_dependencies_compiler_type+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then + # We make a subdir and do the tests there. Otherwise we can end up + # making bogus files that we don't know about and never remove. For + # instance it was reported that on HP-UX the gcc test will end up + # making a dummy file named 'D' -- because '-MD' means "put the output + # in D". + rm -rf conftest.dir + mkdir conftest.dir + # Copy depcomp to subdir because otherwise we won't find it if we're + # using a relative directory. + cp "$am_depcomp" conftest.dir + cd conftest.dir + # We will build objects and dependencies in a subdirectory because + # it helps to detect inapplicable dependency modes. For instance + # both Tru64's cc and ICC support -MD to output dependencies as a + # side effect of compilation, but ICC will put the dependencies in + # the current directory while Tru64 will put them in the object + # directory. + mkdir sub + + am_cv_CC_dependencies_compiler_type=none + if test "$am_compiler_list" = ""; then + am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp` + fi + am__universal=false + case " $depcc " in #( + *\ -arch\ *\ -arch\ *) am__universal=true ;; + esac + + for depmode in $am_compiler_list; do + # Setup a source with many dependencies, because some compilers + # like to wrap large dependency lists on column 80 (with \), and + # we should not choose a depcomp mode which is confused by this. + # + # We need to recreate these files for each test, as the compiler may + # overwrite some of them when testing with obscure command lines. + # This happens at least with the AIX C compiler. + : > sub/conftest.c + for i in 1 2 3 4 5 6; do + echo '#include "conftst'$i'.h"' >> sub/conftest.c + # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with + # Solaris 10 /bin/sh. + echo '/* dummy */' > sub/conftst$i.h + done + echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf + + # We check with '-c' and '-o' for the sake of the "dashmstdout" + # mode. It turns out that the SunPro C++ compiler does not properly + # handle '-M -o', and we need to detect this. Also, some Intel + # versions had trouble with output in subdirs. + am__obj=sub/conftest.${OBJEXT-o} + am__minus_obj="-o $am__obj" + case $depmode in + gcc) + # This depmode causes a compiler race in universal mode. + test "$am__universal" = false || continue + ;; + nosideeffect) + # After this tag, mechanisms are not by side-effect, so they'll + # only be used when explicitly requested. + if test "x$enable_dependency_tracking" = xyes; then + continue + else + break + fi + ;; + msvc7 | msvc7msys | msvisualcpp | msvcmsys) + # This compiler won't grok '-c -o', but also, the minuso test has + # not run yet. These depmodes are late enough in the game, and + # so weak that their functioning should not be impacted. + am__obj=conftest.${OBJEXT-o} + am__minus_obj= + ;; + none) break ;; + esac + if depmode=$depmode \ + source=sub/conftest.c object=$am__obj \ + depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ + $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ + >/dev/null 2>conftest.err && + grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && + grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && + grep $am__obj sub/conftest.Po > /dev/null 2>&1 && + ${MAKE-make} -s -f confmf > /dev/null 2>&1; then + # icc doesn't choke on unknown options, it will just issue warnings + # or remarks (even with -Werror). So we grep stderr for any message + # that says an option was ignored or not supported. + # When given -MP, icc 7.0 and 7.1 complain thusly: + # icc: Command line warning: ignoring option '-M'; no argument required + # The diagnosis changed in icc 8.0: + # icc: Command line remark: option '-MP' not supported + if (grep 'ignoring option' conftest.err || + grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else + am_cv_CC_dependencies_compiler_type=$depmode + break + fi + fi + done + + cd .. + rm -rf conftest.dir +else + am_cv_CC_dependencies_compiler_type=none +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_CC_dependencies_compiler_type" >&5 +$as_echo "$am_cv_CC_dependencies_compiler_type" >&6; } +CCDEPMODE=depmode=$am_cv_CC_dependencies_compiler_type + + if + test "x$enable_dependency_tracking" != xno \ + && test "$am_cv_CC_dependencies_compiler_type" = gcc3; then + am__fastdepCC_TRUE= + am__fastdepCC_FALSE='#' +else + am__fastdepCC_TRUE='#' + am__fastdepCC_FALSE= +fi + + # POSIX will say in a future version that running "rm -f" with no argument # is OK; and we want to be able to make that assumption in our Makefile @@ -3183,6 +4564,7 @@ END as_fn_error $? "Your 'rm' program is bad, sorry." "$LINENO" 5 fi fi + # Make sure we can run config.sub. $SHELL "$ac_aux_dir/config.sub" sun4 >/dev/null 2>&1 || as_fn_error $? "cannot run $SHELL $ac_aux_dir/config.sub" "$LINENO" 5 @@ -3582,256 +4964,6 @@ $as_echo "$ac_try_echo"; } >&5 test $ac_status = 0; } done -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -ac_clean_files_save=$ac_clean_files -ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out" -# Try to create an executable without -o first, disregard a.out. -# It will help us diagnose broken compilers, and finding out an intuition -# of exeext. -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5 -$as_echo_n "checking whether the C compiler works... " >&6; } -ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` - -# The possible output files: -ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" - -ac_rmfiles= -for ac_file in $ac_files -do - case $ac_file in - *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; - * ) ac_rmfiles="$ac_rmfiles $ac_file";; - esac -done -rm -f $ac_rmfiles - -if { { ac_try="$ac_link_default" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_link_default") 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; then : - # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. -# So ignore a value of `no', otherwise this would lead to `EXEEXT = no' -# in a Makefile. We should not override ac_cv_exeext if it was cached, -# so that the user can short-circuit this test for compilers unknown to -# Autoconf. -for ac_file in $ac_files '' -do - test -f "$ac_file" || continue - case $ac_file in - *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) - ;; - [ab].out ) - # We found the default executable, but exeext='' is most - # certainly right. - break;; - *.* ) - if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no; - then :; else - ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` - fi - # We set ac_cv_exeext here because the later test for it is not - # safe: cross compilers may not add the suffix if given an `-o' - # argument, so we may need to know it at that point already. - # Even if this section looks crufty: it has the advantage of - # actually working. - break;; - * ) - break;; - esac -done -test "$ac_cv_exeext" = no && ac_cv_exeext= - -else - ac_file='' -fi -if test -z "$ac_file"; then : - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -$as_echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error 77 "C compiler cannot create executables -See \`config.log' for more details" "$LINENO" 5; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5 -$as_echo_n "checking for C compiler default output file name... " >&6; } -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 -$as_echo "$ac_file" >&6; } -ac_exeext=$ac_cv_exeext - -rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out -ac_clean_files=$ac_clean_files_save -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 -$as_echo_n "checking for suffix of executables... " >&6; } -if { { ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_link") 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; then : - # If both `conftest.exe' and `conftest' are `present' (well, observable) -# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will -# work properly (i.e., refer to `conftest.exe'), while it won't with -# `rm'. -for ac_file in conftest.exe conftest conftest.*; do - test -f "$ac_file" || continue - case $ac_file in - *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; - *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` - break;; - * ) break;; - esac -done -else - { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "cannot compute suffix of executables: cannot compile and link -See \`config.log' for more details" "$LINENO" 5; } -fi -rm -f conftest conftest$ac_cv_exeext -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 -$as_echo "$ac_cv_exeext" >&6; } - -rm -f conftest.$ac_ext -EXEEXT=$ac_cv_exeext -ac_exeext=$EXEEXT -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include <stdio.h> -int -main () -{ -FILE *f = fopen ("conftest.out", "w"); - return ferror (f) || fclose (f) != 0; - - ; - return 0; -} -_ACEOF -ac_clean_files="$ac_clean_files conftest.out" -# Check that the compiler produces executables we can run. If not, either -# the compiler is broken, or we cross compile. -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 -$as_echo_n "checking whether we are cross compiling... " >&6; } -if test "$cross_compiling" != yes; then - { { ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_link") 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } - if { ac_try='./conftest$ac_cv_exeext' - { { case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_try") 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; }; then - cross_compiling=no - else - if test "$cross_compiling" = maybe; then - cross_compiling=yes - else - { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "cannot run C compiled programs. -If you meant to cross compile, use \`--host'. -See \`config.log' for more details" "$LINENO" 5; } - fi - fi -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 -$as_echo "$cross_compiling" >&6; } - -rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out -ac_clean_files=$ac_clean_files_save -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 -$as_echo_n "checking for suffix of object files... " >&6; } -if ${ac_cv_objext+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -rm -f conftest.o conftest.obj -if { { ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_compile") 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; then : - for ac_file in conftest.o conftest.obj conftest.*; do - test -f "$ac_file" || continue; - case $ac_file in - *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; - *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` - break;; - esac -done -else - $as_echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "cannot compute suffix of object files: cannot compile -See \`config.log' for more details" "$LINENO" 5; } -fi -rm -f conftest.$ac_cv_objext conftest.$ac_ext -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 -$as_echo "$ac_cv_objext" >&6; } -OBJEXT=$ac_cv_objext -ac_objext=$OBJEXT { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 $as_echo_n "checking whether we are using the GNU C compiler... " >&6; } if ${ac_cv_c_compiler_gnu+:} false; then : @@ -4101,197 +5233,6 @@ ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu -DEPDIR="${am__leading_dot}deps" - -ac_config_commands="$ac_config_commands depfiles" - - -am_make=${MAKE-make} -cat > confinc << 'END' -am__doit: - @echo this is the am__doit target -.PHONY: am__doit -END -# If we don't find an include directive, just comment out the code. -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for style of include used by $am_make" >&5 -$as_echo_n "checking for style of include used by $am_make... " >&6; } -am__include="#" -am__quote= -_am_result=none -# First try GNU make style include. -echo "include confinc" > confmf -# Ignore all kinds of additional output from 'make'. -case `$am_make -s -f confmf 2> /dev/null` in #( -*the\ am__doit\ target*) - am__include=include - am__quote= - _am_result=GNU - ;; -esac -# Now try BSD make style include. -if test "$am__include" = "#"; then - echo '.include "confinc"' > confmf - case `$am_make -s -f confmf 2> /dev/null` in #( - *the\ am__doit\ target*) - am__include=.include - am__quote="\"" - _am_result=BSD - ;; - esac -fi - - -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $_am_result" >&5 -$as_echo "$_am_result" >&6; } -rm -f confinc confmf - -# Check whether --enable-dependency-tracking was given. -if test "${enable_dependency_tracking+set}" = set; then : - enableval=$enable_dependency_tracking; -fi - -if test "x$enable_dependency_tracking" != xno; then - am_depcomp="$ac_aux_dir/depcomp" - AMDEPBACKSLASH='\' - am__nodep='_no' -fi - if test "x$enable_dependency_tracking" != xno; then - AMDEP_TRUE= - AMDEP_FALSE='#' -else - AMDEP_TRUE='#' - AMDEP_FALSE= -fi - - - -depcc="$CC" am_compiler_list= - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5 -$as_echo_n "checking dependency style of $depcc... " >&6; } -if ${am_cv_CC_dependencies_compiler_type+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then - # We make a subdir and do the tests there. Otherwise we can end up - # making bogus files that we don't know about and never remove. For - # instance it was reported that on HP-UX the gcc test will end up - # making a dummy file named 'D' -- because '-MD' means "put the output - # in D". - rm -rf conftest.dir - mkdir conftest.dir - # Copy depcomp to subdir because otherwise we won't find it if we're - # using a relative directory. - cp "$am_depcomp" conftest.dir - cd conftest.dir - # We will build objects and dependencies in a subdirectory because - # it helps to detect inapplicable dependency modes. For instance - # both Tru64's cc and ICC support -MD to output dependencies as a - # side effect of compilation, but ICC will put the dependencies in - # the current directory while Tru64 will put them in the object - # directory. - mkdir sub - - am_cv_CC_dependencies_compiler_type=none - if test "$am_compiler_list" = ""; then - am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp` - fi - am__universal=false - case " $depcc " in #( - *\ -arch\ *\ -arch\ *) am__universal=true ;; - esac - - for depmode in $am_compiler_list; do - # Setup a source with many dependencies, because some compilers - # like to wrap large dependency lists on column 80 (with \), and - # we should not choose a depcomp mode which is confused by this. - # - # We need to recreate these files for each test, as the compiler may - # overwrite some of them when testing with obscure command lines. - # This happens at least with the AIX C compiler. - : > sub/conftest.c - for i in 1 2 3 4 5 6; do - echo '#include "conftst'$i'.h"' >> sub/conftest.c - # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with - # Solaris 10 /bin/sh. - echo '/* dummy */' > sub/conftst$i.h - done - echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf - - # We check with '-c' and '-o' for the sake of the "dashmstdout" - # mode. It turns out that the SunPro C++ compiler does not properly - # handle '-M -o', and we need to detect this. Also, some Intel - # versions had trouble with output in subdirs. - am__obj=sub/conftest.${OBJEXT-o} - am__minus_obj="-o $am__obj" - case $depmode in - gcc) - # This depmode causes a compiler race in universal mode. - test "$am__universal" = false || continue - ;; - nosideeffect) - # After this tag, mechanisms are not by side-effect, so they'll - # only be used when explicitly requested. - if test "x$enable_dependency_tracking" = xyes; then - continue - else - break - fi - ;; - msvc7 | msvc7msys | msvisualcpp | msvcmsys) - # This compiler won't grok '-c -o', but also, the minuso test has - # not run yet. These depmodes are late enough in the game, and - # so weak that their functioning should not be impacted. - am__obj=conftest.${OBJEXT-o} - am__minus_obj= - ;; - none) break ;; - esac - if depmode=$depmode \ - source=sub/conftest.c object=$am__obj \ - depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ - $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ - >/dev/null 2>conftest.err && - grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && - grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && - grep $am__obj sub/conftest.Po > /dev/null 2>&1 && - ${MAKE-make} -s -f confmf > /dev/null 2>&1; then - # icc doesn't choke on unknown options, it will just issue warnings - # or remarks (even with -Werror). So we grep stderr for any message - # that says an option was ignored or not supported. - # When given -MP, icc 7.0 and 7.1 complain thusly: - # icc: Command line warning: ignoring option '-M'; no argument required - # The diagnosis changed in icc 8.0: - # icc: Command line remark: option '-MP' not supported - if (grep 'ignoring option' conftest.err || - grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else - am_cv_CC_dependencies_compiler_type=$depmode - break - fi - fi - done - - cd .. - rm -rf conftest.dir -else - am_cv_CC_dependencies_compiler_type=none -fi - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_CC_dependencies_compiler_type" >&5 -$as_echo "$am_cv_CC_dependencies_compiler_type" >&6; } -CCDEPMODE=depmode=$am_cv_CC_dependencies_compiler_type - - if - test "x$enable_dependency_tracking" != xno \ - && test "$am_cv_CC_dependencies_compiler_type" = gcc3; then - am__fastdepCC_TRUE= - am__fastdepCC_FALSE='#' -else - am__fastdepCC_TRUE='#' - am__fastdepCC_FALSE= -fi - @@ -4414,7 +5355,6 @@ $as_echo "no" >&6; } PKG_CONFIG="" fi fi - ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' @@ -4891,6 +5831,124 @@ done +ac_fn_c_check_type "$LINENO" "struct timespec" "ac_cv_type_struct_timespec" "#include <time.h> +" +if test "x$ac_cv_type_struct_timespec" = xyes; then : + +cat >>confdefs.h <<_ACEOF +#define HAVE_STRUCT_TIMESPEC 1 +_ACEOF + + +fi +ac_fn_c_check_type "$LINENO" "clockid_t" "ac_cv_type_clockid_t" "#include <time.h> +" +if test "x$ac_cv_type_clockid_t" = xyes; then : + +cat >>confdefs.h <<_ACEOF +#define HAVE_CLOCKID_T 1 +_ACEOF + + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing clock_gettime" >&5 +$as_echo_n "checking for library containing clock_gettime... " >&6; } +if ${ac_cv_search_clock_gettime+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_func_search_save_LIBS=$LIBS +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char clock_gettime (); +int +main () +{ +return clock_gettime (); + ; + return 0; +} +_ACEOF +for ac_lib in '' rt posix4; do + if test -z "$ac_lib"; then + ac_res="none required" + else + ac_res=-l$ac_lib + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + fi + if ac_fn_c_try_link "$LINENO"; then : + ac_cv_search_clock_gettime=$ac_res +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext + if ${ac_cv_search_clock_gettime+:} false; then : + break +fi +done +if ${ac_cv_search_clock_gettime+:} false; then : + +else + ac_cv_search_clock_gettime=no +fi +rm conftest.$ac_ext +LIBS=$ac_func_search_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_clock_gettime" >&5 +$as_echo "$ac_cv_search_clock_gettime" >&6; } +ac_res=$ac_cv_search_clock_gettime +if test "$ac_res" != no; then : + test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" + +fi + +for ac_func in clock_gettime +do : + ac_fn_c_check_func "$LINENO" "clock_gettime" "ac_cv_func_clock_gettime" +if test "x$ac_cv_func_clock_gettime" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_CLOCK_GETTIME 1 +_ACEOF + +fi +done + + +# Check system type +case "$host_os" in +freebsd*) + build_target=freebsd + ;; +darwin*) + build_target=macos + LDFLAGS="${LDFLAGS} -framework CoreFoundation -framework IOKit" + ;; +*) + ;; +esac + + if test x$build_target = xfreebsd; then + FREEBSD_TRUE= + FREEBSD_FALSE='#' +else + FREEBSD_TRUE='#' + FREEBSD_FALSE= +fi + + if test x$build_target = xmacos; then + MACOS_TRUE= + MACOS_FALSE='#' +else + MACOS_TRUE='#' + MACOS_FALSE= +fi + # Check whether --enable-plugin-nfacct was given. if test "${enable_plugin_nfacct+set}" = set; then : @@ -5950,6 +7008,7 @@ $as_echo "#define HAVE_C___ATOMIC 1" >>confdefs.h fi +# AC_C_STMT_EXPR # The cast to long int works around a bug in the HP C Compiler # version HP92453-01 B.11.11.23709.GP, which incorrectly rejects # declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. @@ -5984,6 +7043,82 @@ _ACEOF +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether sys/types.h defines makedev" >&5 +$as_echo_n "checking whether sys/types.h defines makedev... " >&6; } +if ${ac_cv_header_sys_types_h_makedev+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <sys/types.h> +int +main () +{ +return makedev(0, 0); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_header_sys_types_h_makedev=yes +else + ac_cv_header_sys_types_h_makedev=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_sys_types_h_makedev" >&5 +$as_echo "$ac_cv_header_sys_types_h_makedev" >&6; } + +if test $ac_cv_header_sys_types_h_makedev = no; then +ac_fn_c_check_header_mongrel "$LINENO" "sys/mkdev.h" "ac_cv_header_sys_mkdev_h" "$ac_includes_default" +if test "x$ac_cv_header_sys_mkdev_h" = xyes; then : + +$as_echo "#define MAJOR_IN_MKDEV 1" >>confdefs.h + +fi + + + + if test $ac_cv_header_sys_mkdev_h = no; then + ac_fn_c_check_header_mongrel "$LINENO" "sys/sysmacros.h" "ac_cv_header_sys_sysmacros_h" "$ac_includes_default" +if test "x$ac_cv_header_sys_sysmacros_h" = xyes; then : + +$as_echo "#define MAJOR_IN_SYSMACROS 1" >>confdefs.h + +fi + + + fi +fi + +for ac_header in sys/types.h netinet/in.h arpa/nameser.h netdb.h resolv.h +do : + as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` +ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "#ifdef HAVE_SYS_TYPES_H +# include <sys/types.h> +#endif +#ifdef HAVE_NETINET_IN_H +# include <netinet/in.h> /* inet_ functions / structs */ +#endif +#ifdef HAVE_ARPA_NAMESER_H +# include <arpa/nameser.h> /* DNS HEADER struct */ +#endif +#ifdef HAVE_NETDB_H +# include <netdb.h> +#endif +" +if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + case $host_cpu in #( @@ -6665,6 +7800,14 @@ $as_echo_n "checking that generated files are newer than configure... " >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: done" >&5 $as_echo "done" >&6; } +if test -z "${AMDEP_TRUE}" && test -z "${AMDEP_FALSE}"; then + as_fn_error $? "conditional \"AMDEP\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then + as_fn_error $? "conditional \"am__fastdepCC\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi if test -n "$EXEEXT"; then am__EXEEXT_TRUE= am__EXEEXT_FALSE='#' @@ -6673,12 +7816,12 @@ else am__EXEEXT_FALSE= fi -if test -z "${AMDEP_TRUE}" && test -z "${AMDEP_FALSE}"; then - as_fn_error $? "conditional \"AMDEP\" was never defined. +if test -z "${FREEBSD_TRUE}" && test -z "${FREEBSD_FALSE}"; then + as_fn_error $? "conditional \"FREEBSD\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi -if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then - as_fn_error $? "conditional \"am__fastdepCC\" was never defined. +if test -z "${MACOS_TRUE}" && test -z "${MACOS_FALSE}"; then + as_fn_error $? "conditional \"MACOS\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi @@ -7078,7 +8221,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by netdata $as_me 1.4.0, which was +This file was extended by netdata $as_me 1.5.0, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -7144,7 +8287,7 @@ _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -netdata config.status 1.4.0 +netdata config.status 1.5.0 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" diff --git a/configure.ac b/configure.ac index 415b09c75..d0a6ebd6f 100644 --- a/configure.ac +++ b/configure.ac @@ -4,7 +4,7 @@ AC_PREREQ(2.60) define([VERSION_MAJOR], [1]) -define([VERSION_MINOR], [4]) +define([VERSION_MINOR], [5]) define([VERSION_FIX], [0]) define([VERSION_NUMBER], VERSION_MAJOR[.]VERSION_MINOR[.]VERSION_FIX) define([VERSION_SUFFIX], []) @@ -24,6 +24,15 @@ PACKAGE_RPM_VERSION="VERSION_NUMBER" AC_SUBST([PACKAGE_RPM_VERSION]) AC_SUBST([PACKAGE_RPM_RELEASE]) +# fails on centos6 +#AX_CHECK_ENABLE_DEBUG() + +AX_GCC_FUNC_ATTRIBUTE(returns_nonnull) +AX_GCC_FUNC_ATTRIBUTE(malloc) +AX_GCC_FUNC_ATTRIBUTE(noreturn) +AX_GCC_FUNC_ATTRIBUTE(format) +AX_GCC_FUNC_ATTRIBUTE(warn_unused_result) + AC_CONFIG_AUX_DIR([.]) AC_CONFIG_HEADERS([config.h]) AC_CONFIG_MACRO_DIR([m4]) @@ -35,6 +44,25 @@ AC_PROG_INSTALL PKG_PROG_PKG_CONFIG AC_USE_SYSTEM_EXTENSIONS AC_CHECK_FUNCS_ONCE(accept4) +AC_CHECK_TYPES([struct timespec, clockid_t], [], [], [[#include <time.h>]]) +AC_SEARCH_LIBS([clock_gettime], [rt posix4]) +AC_CHECK_FUNCS([clock_gettime]) + +# Check system type +case "$host_os" in +freebsd*) + build_target=freebsd + ;; +darwin*) + build_target=macos + LDFLAGS="${LDFLAGS} -framework CoreFoundation -framework IOKit" + ;; +*) + ;; +esac + +AM_CONDITIONAL([FREEBSD], [test x$build_target = xfreebsd]) +AM_CONDITIONAL([MACOS], [test x$build_target = xmacos]) AC_ARG_ENABLE( [plugin-nfacct], @@ -104,8 +132,11 @@ AC_C_INLINE AC_FUNC_STRERROR_R AC_C__GENERIC AC_C___ATOMIC +# AC_C_STMT_EXPR AC_CHECK_SIZEOF([void *]) AC_CANONICAL_HOST +AC_HEADER_MAJOR +AC_HEADER_RESOLV AC_ARG_VAR([SSE_CANDIDATE], [C compiler flags for SSE]) AS_CASE([$host_cpu], diff --git a/contrib/Makefile.in b/contrib/Makefile.in index 242ed8ebf..eed37f4d9 100644 --- a/contrib/Makefile.in +++ b/contrib/Makefile.in @@ -1,7 +1,7 @@ -# Makefile.in generated by automake 1.14.1 from Makefile.am. +# Makefile.in generated by automake 1.15 from Makefile.am. # @configure_input@ -# Copyright (C) 1994-2013 Free Software Foundation, Inc. +# Copyright (C) 1994-2014 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -16,7 +16,17 @@ VPATH = @srcdir@ -am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ @@ -80,18 +90,19 @@ POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = contrib -DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ - $(dist_noinst_SCRIPTS) $(dist_noinst_DATA) ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_c___atomic.m4 \ $(top_srcdir)/m4/ax_c__generic.m4 \ $(top_srcdir)/m4/ax_c_mallinfo.m4 \ $(top_srcdir)/m4/ax_c_mallopt.m4 \ $(top_srcdir)/m4/ax_check_compile_flag.m4 \ + $(top_srcdir)/m4/ax_gcc_func_attribute.m4 \ $(top_srcdir)/m4/ax_pthread.m4 $(top_srcdir)/m4/jemalloc.m4 \ $(top_srcdir)/m4/tcmalloc.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(dist_noinst_SCRIPTS) \ + $(dist_noinst_DATA) $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = @@ -118,6 +129,7 @@ am__can_run_installinfo = \ esac DATA = $(dist_noinst_DATA) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +am__DIST_COMMON = $(srcdir)/Makefile.in DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ @@ -294,7 +306,6 @@ $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__confi echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu contrib/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu contrib/Makefile -.PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ @@ -463,6 +474,8 @@ uninstall-am: maintainer-clean-generic mostlyclean mostlyclean-generic pdf \ pdf-am ps ps-am tags-am uninstall uninstall-am +.PRECIOUS: Makefile + debian/changelog: echo "netdata ($(PACKAGE_VERSION)) UNRELEASED; urgency=medium" | \ diff --git a/contrib/README.md b/contrib/README.md index d3643d753..60bcf3f28 100644 --- a/contrib/README.md +++ b/contrib/README.md @@ -37,14 +37,16 @@ updates first. * rename `contrib/debian/control.wheezy` to `contrib/debian/control`. +* change `control.wheezy from contrib/Makefile* to control`. + * uncomment `EXTRA_OPTS="-P /var/run/netdata.pid"` in `contrib/debian/netdata.default` * edit `contrib/debian/netdata.init` and change `PIDFILE` to `/var/run/netdata.pid` -* uncomment `postrotate` in `system/netdata.logrotate.in` and change - `try-restart` to `restart` +* remove `dpkg-statoverride --update --add --force root netdata 0775 /var/lib/netdata/registry` from + `contrib/debian/netdata.postinst.in`. If you are going to handle the unique id file differently. Then proceed as the main instructions above. diff --git a/contrib/debian/changelog b/contrib/debian/changelog new file mode 100644 index 000000000..bc8f48cd5 --- /dev/null +++ b/contrib/debian/changelog @@ -0,0 +1,3 @@ +netdata (1.5.0) UNRELEASED; urgency=medium + * Latest release + -- Netdata Team <> Mon, 23 Jan 2017 21:57:01 +0200 diff --git a/contrib/debian/compat b/contrib/debian/compat new file mode 100644 index 000000000..ec635144f --- /dev/null +++ b/contrib/debian/compat @@ -0,0 +1 @@ +9 diff --git a/contrib/debian/control b/contrib/debian/control new file mode 100644 index 000000000..24c5f4c4b --- /dev/null +++ b/contrib/debian/control @@ -0,0 +1,25 @@ +Source: netdata +Build-Depends: debhelper (>= 9), + dh-autoreconf, + dh-systemd (>= 1.5), + dpkg-dev (>= 1.13.19), + zlib1g-dev, + uuid-dev +Section: net +Priority: optional +Maintainer: Costa Tsaousis <costa@tsaousis.gr> +Standards-Version: 3.9.6 +Homepage: https://github.com/firehol/netdata/wiki + +Package: netdata +Architecture: any +Depends: adduser, + libcap2-bin (>= 1:2.0), + lsb-base (>= 3.1-23.2), + ${misc:Depends}, + ${shlibs:Depends} +Description: real-time charts for system monitoring + Netdata is a daemon that collects data in realtime (per second) + and presents a web site to view and analyze them. The presentation + is also real-time and full of interactive charts that precisely + render all collected values. diff --git a/contrib/debian/control.wheezy b/contrib/debian/control.wheezy new file mode 100644 index 000000000..4103908a2 --- /dev/null +++ b/contrib/debian/control.wheezy @@ -0,0 +1,25 @@ +Source: netdata +Build-Depends: debhelper (>= 9), + dh-autoreconf, + pkg-config, + dpkg-dev (>= 1.13.19), + zlib1g-dev, + uuid-dev +Section: net +Priority: optional +Maintainer: Costa Tsaousis <costa@tsaousis.gr> +Standards-Version: 3.9.6 +Homepage: https://github.com/firehol/netdata/wiki + +Package: netdata +Architecture: any +Depends: adduser, + libcap2-bin (>= 1:2.0), + lsb-base (>= 3.1-23.2), + ${misc:Depends}, + ${shlibs:Depends} +Description: real-time charts for system monitoring + Netdata is a daemon that collects data in realtime (per second) + and presents a web site to view and analyze them. The presentation + is also real-time and full of interactive charts that precisely + render all collected values. diff --git a/contrib/debian/copyright b/contrib/debian/copyright new file mode 100644 index 000000000..11a3d6390 --- /dev/null +++ b/contrib/debian/copyright @@ -0,0 +1,10 @@ +Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: Netdata +Upstream-Contact: Costa Tsaousis <costa@tsaousis.gr> +Source: https://github.com/firehol/netdata + +Files: * +Copyright: 2014-2016, Costa Tsaousis +License: GPL-3+ + On Debian systems, the complete text of the GNU General Public + License version 3 can be found in /usr/share/common-licenses/GPL-3. diff --git a/contrib/debian/netdata.conf b/contrib/debian/netdata.conf new file mode 100644 index 000000000..a963d80b7 --- /dev/null +++ b/contrib/debian/netdata.conf @@ -0,0 +1,16 @@ +# NetData Configuration + +# The current full configuration can be retrieved from the running +# server at the URL +# +# http://localhost:19999/netdata.conf +# +# for example: +# +# wget -O /etc/netdata/netdata.conf http://localhost:19999/netdata.conf +# + +[global] + run as user = netdata + web files owner = root + web files group = netdata diff --git a/contrib/debian/netdata.default b/contrib/debian/netdata.default new file mode 100644 index 000000000..9e7f8ae6e --- /dev/null +++ b/contrib/debian/netdata.default @@ -0,0 +1,5 @@ +# Extra arguments to pass to netdata +# +#EXTRA_OPTS="" +#uncomment following line if you are building a wheezy-package +#EXTRA_OPTS="-P /var/run/netdata.pid" diff --git a/contrib/debian/netdata.docs b/contrib/debian/netdata.docs new file mode 100644 index 000000000..56631abf1 --- /dev/null +++ b/contrib/debian/netdata.docs @@ -0,0 +1 @@ +ChangeLog diff --git a/contrib/debian/netdata.init b/contrib/debian/netdata.init new file mode 100755 index 000000000..c1b2b74de --- /dev/null +++ b/contrib/debian/netdata.init @@ -0,0 +1,56 @@ +#!/bin/sh +# Start/stop the netdata daemon. +# +### BEGIN INIT INFO +# Provides: netdata +# Required-Start: $remote_fs +# Required-Stop: $remote_fs +# Should-Start: $network +# Should-Stop: $network +# Default-Start: 2 3 4 5 +# Default-Stop: +# Short-Description: Real-time charts for system monitoring +# Description: Netdata is a daemon that collects data in realtime (per second) +# and presents a web site to view and analyze them. The presentation +# is also real-time and full of interactive charts that precisely +# render all collected values. +### END INIT INFO + +PATH=/bin:/usr/bin:/sbin:/usr/sbin +DESC="netdata daemon" +NAME=netdata +DAEMON=/usr/sbin/netdata +PIDFILE=/var/run/netdata/netdata.pid +SCRIPTNAME=/etc/init.d/"$NAME" + +test -f $DAEMON || exit 0 + +. /lib/lsb/init-functions + +[ -r /etc/default/netdata ] && . /etc/default/netdata + +case "$1" in +start) log_daemon_msg "Starting real-time system monitoring" "netdata" + start_daemon -p $PIDFILE $DAEMON $EXTRA_OPTS + log_end_msg $? + ;; +stop) log_daemon_msg "Stopping real-time system monitoring" "netdata" + killproc -p $PIDFILE $DAEMON + RETVAL=$? + [ $RETVAL -eq 0 ] && [ -e "$PIDFILE" ] && rm -f $PIDFILE + log_end_msg $RETVAL + # wait for plugins to exit + sleep 1 + ;; +restart|force-reload) log_daemon_msg "Restarting real-time system monitoring" "netdata" + $0 stop + $0 start + ;; +status) + status_of_proc -p $PIDFILE $DAEMON $NAME && exit 0 || exit $? + ;; +*) log_action_msg "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" + exit 2 + ;; +esac +exit 0 diff --git a/contrib/debian/netdata.install b/contrib/debian/netdata.install new file mode 100644 index 000000000..45d42b635 --- /dev/null +++ b/contrib/debian/netdata.install @@ -0,0 +1 @@ +debian/netdata.conf /etc/netdata/ diff --git a/contrib/debian/netdata.lintian-overrides b/contrib/debian/netdata.lintian-overrides new file mode 100644 index 000000000..45b2d868f --- /dev/null +++ b/contrib/debian/netdata.lintian-overrides @@ -0,0 +1,16 @@ + +# See Debian policy 10.9. apps.plugin has extra capabilities, so don't let +# normal users run it. +netdata: non-standard-executable-perm usr/lib/*/netdata/plugins.d/apps.plugin 0754 != 0755 + + +# FontAwesome is at least in the fonts-font-awesome package, but this is +# not available in wheezy. glyphicons-halflings-regular isn't currently in +# a Debian package. Therefore don't complain about shipping them with netdata +# for the time being. +netdata: duplicate-font-file usr/share/netdata/fonts/* +netdata: font-in-non-font-package usr/share/netdata/fonts/* + +# Files here are marked as conffiles so that local updates to the html files +# isn't clobbered on upgrade. +netdata: non-etc-file-marked-as-conffile var/lib/netdata/www/* diff --git a/contrib/debian/netdata.postinst.in b/contrib/debian/netdata.postinst.in new file mode 100644 index 000000000..29615f541 --- /dev/null +++ b/contrib/debian/netdata.postinst.in @@ -0,0 +1,41 @@ +#! /bin/sh + +set -e + +case "$1" in + configure) + if [ -z "$2" ]; then + if ! getent group netdata >/dev/null; then + addgroup --quiet --system netdata + fi + + if ! getent passwd netdata >/dev/null; then + adduser --quiet --system --ingroup netdata --home /var/lib/netdata --no-create-home netdata + fi + + if ! dpkg-statoverride --list /var/lib/netdata >/dev/null 2>&1; then + dpkg-statoverride --update --add netdata netdata 0755 /var/lib/netdata + fi + + if ! dpkg-statoverride --list /var/lib/netdata/www >/dev/null 2>&1; then + dpkg-statoverride --update --add root netdata 0755 /var/lib/netdata/www + fi + + if ! dpkg-statoverride --list /var/cache/netdata >/dev/null 2>&1; then + dpkg-statoverride --update --add netdata netdata 0755 /var/cache/netdata + fi + + fi + + dpkg-statoverride --update --add --force root netdata 0775 /var/lib/netdata/registry + chown -R root:netdata /usr/share/netdata/* + chown -R root:netdata /usr/lib/@DEB_HOST_MULTIARCH@/netdata/plugins.d + setcap cap_dac_read_search,cap_sys_ptrace+ep /usr/lib/@DEB_HOST_MULTIARCH@/netdata/plugins.d/apps.plugin + +#PERMS# + ;; +esac + +#DEBHELPER# + +exit 0 diff --git a/contrib/debian/netdata.postrm b/contrib/debian/netdata.postrm new file mode 100644 index 000000000..4ab4eeadd --- /dev/null +++ b/contrib/debian/netdata.postrm @@ -0,0 +1,43 @@ +#! /bin/sh + +set -e + +case "$1" in + remove) + ;; + + purge) + if dpkg-statoverride --list | grep -qw /var/cache/netdata; then + dpkg-statoverride --remove /var/cache/netdata + fi + + if dpkg-statoverride --list | grep -qw /var/lib/netdata/www; then + dpkg-statoverride --remove /var/lib/netdata/www + fi + + if dpkg-statoverride --list | grep -qw /var/lib/netdata; then + dpkg-statoverride --remove /var/lib/netdata + fi + + if getent passwd netdata >/dev/null; then + if [ -x /usr/sbin/deluser ]; then + deluser --quiet --system netdata || echo "Unable to remove netdata user" + fi + fi + + if getent group netdata >/dev/null; then + if [ -x /usr/sbin/delgroup ]; then + delgroup --quiet --system netdata || echo "Unable to remove netdata group" + fi + fi + + ;; + + *) + ;; +esac + +#DEBHELPER# + +exit 0 + diff --git a/contrib/debian/netdata.service b/contrib/debian/netdata.service new file mode 100644 index 000000000..e5d3a3863 --- /dev/null +++ b/contrib/debian/netdata.service @@ -0,0 +1,14 @@ +[Unit] +Description=netdata real-time system monitoring +After=network.target + +[Service] +Type=simple +EnvironmentFile=-/etc/default/netdata +ExecStart=/usr/sbin/netdata -D $EXTRA_OPTS +TimeoutStopSec=30 +Restart=always +RestartSec=5 + +[Install] +WantedBy=multi-user.target diff --git a/contrib/debian/rules b/contrib/debian/rules new file mode 100755 index 000000000..ec4ec4182 --- /dev/null +++ b/contrib/debian/rules @@ -0,0 +1,87 @@ +#!/usr/bin/make -f + +# Find the arch we are building for, as this determines +# the location of plugins in /usr/lib +DEB_HOST_MULTIARCH ?= $(shell dpkg-architecture -qDEB_HOST_MULTIARCH) +TOP = $(CURDIR)/debian/netdata + +%: + # For jessie and beyond + # + dh $@ --with autoreconf,systemd + + # For wheezy or other non-systemd distributions use the following. You + # should also see contrib/README.md which gives details of updates to + # make to debian/control. + # + #dh $@ --with autoreconf + +override_dh_auto_configure: + dh_auto_configure -- --with-math --with-webdir=/var/lib/netdata/www + +debian/%.postinst: debian/%.postinst.in + sed 's/@DEB_HOST_MULTIARCH@/$(DEB_HOST_MULTIARCH)/g' $< > $@ + +override_dh_install: debian/netdata.postinst + dh_install + + # Remove unneeded .keep files + # + find "$(TOP)" -name .keep -exec rm '{}' ';' + + # Move files that local user shouldn't be editing to /usr/share/netdata + # + mkdir -p "$(TOP)/usr/share/netdata" + for D in $$(find "$(TOP)/var/lib/netdata/www/" -maxdepth 1 -type d -printf '%f '); do \ + echo Relocating $$D; \ + mv "$(TOP)/var/lib/netdata/www/$$D" "$(TOP)/usr/share/netdata/$$D"; \ + ln -s "/usr/share/netdata/$$D" "$(TOP)/var/lib/netdata/www/$$D"; \ + done + + # Update postinst to set correct group for www files on installation. + # Should probably be dpkg-statoverride really, but that gets *really* + # messy. We also set all web files in /var as conffiles so an upgrade + # doesn't splat them. + # + for D in $$(find "$(TOP)/var/lib/netdata/www/" -maxdepth 1 -type f -printf '%f '); do \ + echo Updating postinst for $$D; \ + sed -i "s/^#PERMS#/chgrp netdata \/var\/lib\/netdata\/www\/$$D\n#PERMS#/g" \ + $(CURDIR)/debian/netdata.postinst; \ + echo "/var/lib/netdata/www/$$D" >> $(CURDIR)/debian/netdata.conffiles; \ + done + sed -i "/^#PERMS#/d" $(CURDIR)/debian/netdata.postinst + +override_dh_installdocs: + dh_installdocs + + # Docs should not be under /usr/lib + # + mv $(TOP)/usr/lib/$(DEB_HOST_MULTIARCH)/netdata/plugins.d/README.md \ + $(TOP)/usr/share/doc/netdata/README.plugins.md + mv $(TOP)/usr/lib/$(DEB_HOST_MULTIARCH)/netdata/charts.d/README.md \ + $(TOP)/usr/share/doc/netdata/README.charts.md + + # This doc is currently empty, so no point installing it. + # + rm $(TOP)/usr/lib/$(DEB_HOST_MULTIARCH)/netdata/node.d/README.md + +override_dh_fixperms: + dh_fixperms + + # apps.plugin should only be runnable by the netdata user. It will be + # given extra capabilities in the postinst script. + # + chmod 0754 $(TOP)/usr/lib/$(DEB_HOST_MULTIARCH)/netdata/plugins.d/apps.plugin + +override_dh_installlogrotate: + cp system/netdata.logrotate debian/netdata.logrotate + dh_installlogrotate + +override_dh_clean: + dh_clean + + # Tidy up copied/generated files + # + -[ -r $(CURDIR)/debian/netdata.logrotate ] && rm $(CURDIR)/debian/netdata.logrotate + -[ -r $(CURDIR)/debian/netdata.postinst ] && rm $(CURDIR)/debian/netdata.postinst + -[ -r $(CURDIR)/debian/netdata.conffiles ] && rm $(CURDIR)/debian/netdata.conffiles diff --git a/contrib/debian/source/format b/contrib/debian/source/format new file mode 100644 index 000000000..89ae9db8f --- /dev/null +++ b/contrib/debian/source/format @@ -0,0 +1 @@ +3.0 (native) diff --git a/coverity-scan.sh b/coverity-scan.sh index 6bb18e411..a690fead7 100755 --- a/coverity-scan.sh +++ b/coverity-scan.sh @@ -34,6 +34,6 @@ tar czvf netdata-coverity-analysis.tgz cov-int || exit 1 curl --form token="${token}" \ --form email=costa@tsaousis.gr \ --form file=@netdata-coverity-analysis.tgz \ - --form version="1.3.0rc1" \ + --form version="1.5.0rc1" \ --form description="Description" \ https://scan.coverity.com/builds?project=firehol%2Fnetdata @@ -3,7 +3,7 @@ scriptversion=2013-05-30.07; # UTC -# Copyright (C) 1999-2013 Free Software Foundation, Inc. +# Copyright (C) 1999-2014 Free Software Foundation, Inc. # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/diagrams/build.sh b/diagrams/build.sh new file mode 100755 index 000000000..53f0ea7ab --- /dev/null +++ b/diagrams/build.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash + +path=$(dirname "$0") +cd "${path}" || exit 1 + +if [ ! -f "plantuml.jar" ] +then + echo >&2 "Please download 'plantuml.jar' from http://plantuml.com/ and put it the same folder with me." + exit 1 +fi + +for x in *.puml +do + [ "${x}" = "config.puml" ] && continue + + echo >&2 "Working on ${x}..." + java -jar plantuml.jar -tpng "${x}" + java -jar plantuml.jar -tsvg "${x}" + # java -jar plantuml.jar -ttxt "${x}" +done diff --git a/diagrams/config.puml b/diagrams/config.puml new file mode 100644 index 000000000..0ce0932f5 --- /dev/null +++ b/diagrams/config.puml @@ -0,0 +1,46 @@ + +skinparam handwritten true +skinparam monochrome true +skinparam roundcorner 15 + +skinparam sequence { + ArrowThickness 3 + + DividerFontColor Black + DividerFontName Comic Sans MS + DividerFontSize 15 + DividerFontStyle Italic + + DelayFontColor Black + DelayFontName Comic Sans MS + DelayFontSize 15 + DelayFontStyle Italic + + TitleFontColor Black + TitleFontName Comic Sans MS + TitleFontStyle Italic + TitleFontSize 25 + + ArrowColor DeepSkyBlue + ArrowFontColor Black + ArrowFontName Comic Sans MS + ArrowFontStyle Regular + ArrowFontSize 19 + + ActorBorderColor DeepSkyBlue + + LifeLineBorderColor blue + LifeLineBackgroundColor #A9DCDF + + ParticipantBorderColor DeepSkyBlue + ParticipantBackgroundColor LightBlue + ParticipantFontName Comic Sans MS + ParticipantFontSize 20 + ParticipantFontColor Black + + ActorBackgroundColor aqua + ActorFontColor Black + ActorFontSize 20 + ActorFontName Comic Sans MS +} + diff --git a/diagrams/registry.puml b/diagrams/registry.puml new file mode 100644 index 000000000..51a337fab --- /dev/null +++ b/diagrams/registry.puml @@ -0,0 +1,40 @@ +@startuml +!include config.puml + +title netdata registry operation +actor "web browser" as user +participant "netdata 1" as n1 +participant "registry 1" as r1 +autonumber "<b>0." + +== standard dashboard communication == + +user ->n1 : \ + hi, give me the dashboard + +n1 --> user : \ + welcome, here it is... + +... a few seconds later ... + +== registry related communication == + +user -> n1 : \ + now give me registry information + +n1 --> user: \ + here it is, talk to <b>registry 1</b> + +note left of r1 #eee: \ + only your web browser \n\ + talks to the registry + +user -> r1 : \ + Hey <b>registry 1</b>, \ +I am accessing <b>netdata 1</b>... + +r1 --> user : \ + nice!, here are other netdata servers \ +you have accessed in the past + +@enduml diff --git a/install-sh b/install-sh index 377bb8687..0b0fdcbba 100755 --- a/install-sh +++ b/install-sh @@ -1,7 +1,7 @@ #!/bin/sh # install - install a program, script, or datafile -scriptversion=2011-11-20.07; # UTC +scriptversion=2013-12-25.23; # UTC # This originates from X11R5 (mit/util/scripts/install.sh), which was # later released in X11R6 (xc/config/util/install.sh) with the @@ -41,19 +41,15 @@ scriptversion=2011-11-20.07; # UTC # This script is compatible with the BSD install script, but was written # from scratch. +tab=' ' nl=' ' -IFS=" "" $nl" +IFS=" $tab$nl" -# set DOITPROG to echo to test this script +# Set DOITPROG to "echo" to test this script. -# Don't use :- since 4.3BSD and earlier shells don't like it. doit=${DOITPROG-} -if test -z "$doit"; then - doit_exec=exec -else - doit_exec=$doit -fi +doit_exec=${doit:-exec} # Put in absolute file names if you don't have them in your path; # or use environment vars. @@ -68,17 +64,6 @@ mvprog=${MVPROG-mv} rmprog=${RMPROG-rm} stripprog=${STRIPPROG-strip} -posix_glob='?' -initialize_posix_glob=' - test "$posix_glob" != "?" || { - if (set -f) 2>/dev/null; then - posix_glob= - else - posix_glob=: - fi - } -' - posix_mkdir= # Desired mode of installed file. @@ -97,7 +82,7 @@ dir_arg= dst_arg= copy_on_change=false -no_target_directory= +is_target_a_directory=possibly usage="\ Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE @@ -137,46 +122,57 @@ while test $# -ne 0; do -d) dir_arg=true;; -g) chgrpcmd="$chgrpprog $2" - shift;; + shift;; --help) echo "$usage"; exit $?;; -m) mode=$2 - case $mode in - *' '* | *' '* | *' -'* | *'*'* | *'?'* | *'['*) - echo "$0: invalid mode: $mode" >&2 - exit 1;; - esac - shift;; + case $mode in + *' '* | *"$tab"* | *"$nl"* | *'*'* | *'?'* | *'['*) + echo "$0: invalid mode: $mode" >&2 + exit 1;; + esac + shift;; -o) chowncmd="$chownprog $2" - shift;; + shift;; -s) stripcmd=$stripprog;; - -t) dst_arg=$2 - # Protect names problematic for 'test' and other utilities. - case $dst_arg in - -* | [=\(\)!]) dst_arg=./$dst_arg;; - esac - shift;; + -t) + is_target_a_directory=always + dst_arg=$2 + # Protect names problematic for 'test' and other utilities. + case $dst_arg in + -* | [=\(\)!]) dst_arg=./$dst_arg;; + esac + shift;; - -T) no_target_directory=true;; + -T) is_target_a_directory=never;; --version) echo "$0 $scriptversion"; exit $?;; - --) shift - break;; + --) shift + break;; - -*) echo "$0: invalid option: $1" >&2 - exit 1;; + -*) echo "$0: invalid option: $1" >&2 + exit 1;; *) break;; esac shift done +# We allow the use of options -d and -T together, by making -d +# take the precedence; this is for compatibility with GNU install. + +if test -n "$dir_arg"; then + if test -n "$dst_arg"; then + echo "$0: target directory not allowed when installing a directory." >&2 + exit 1 + fi +fi + if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then # When -d is used, all remaining arguments are directories to create. # When -t is used, the destination is already specified. @@ -208,6 +204,15 @@ if test $# -eq 0; then fi if test -z "$dir_arg"; then + if test $# -gt 1 || test "$is_target_a_directory" = always; then + if test ! -d "$dst_arg"; then + echo "$0: $dst_arg: Is not a directory." >&2 + exit 1 + fi + fi +fi + +if test -z "$dir_arg"; then do_exit='(exit $ret); exit $ret' trap "ret=129; $do_exit" 1 trap "ret=130; $do_exit" 2 @@ -223,16 +228,16 @@ if test -z "$dir_arg"; then *[0-7]) if test -z "$stripcmd"; then - u_plus_rw= + u_plus_rw= else - u_plus_rw='% 200' + u_plus_rw='% 200' fi cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;; *) if test -z "$stripcmd"; then - u_plus_rw= + u_plus_rw= else - u_plus_rw=,u+rw + u_plus_rw=,u+rw fi cp_umask=$mode$u_plus_rw;; esac @@ -269,41 +274,15 @@ do # If destination is a directory, append the input filename; won't work # if double slashes aren't ignored. if test -d "$dst"; then - if test -n "$no_target_directory"; then - echo "$0: $dst_arg: Is a directory" >&2 - exit 1 + if test "$is_target_a_directory" = never; then + echo "$0: $dst_arg: Is a directory" >&2 + exit 1 fi dstdir=$dst dst=$dstdir/`basename "$src"` dstdir_status=0 else - # Prefer dirname, but fall back on a substitute if dirname fails. - dstdir=` - (dirname "$dst") 2>/dev/null || - expr X"$dst" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ - X"$dst" : 'X\(//\)[^/]' \| \ - X"$dst" : 'X\(//\)$' \| \ - X"$dst" : 'X\(/\)' \| . 2>/dev/null || - echo X"$dst" | - sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ - s//\1/ - q - } - /^X\(\/\/\)[^/].*/{ - s//\1/ - q - } - /^X\(\/\/\)$/{ - s//\1/ - q - } - /^X\(\/\).*/{ - s//\1/ - q - } - s/.*/./; q' - ` - + dstdir=`dirname "$dst"` test -d "$dstdir" dstdir_status=$? fi @@ -314,74 +293,74 @@ do if test $dstdir_status != 0; then case $posix_mkdir in '') - # Create intermediate dirs using mode 755 as modified by the umask. - # This is like FreeBSD 'install' as of 1997-10-28. - umask=`umask` - case $stripcmd.$umask in - # Optimize common cases. - *[2367][2367]) mkdir_umask=$umask;; - .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;; - - *[0-7]) - mkdir_umask=`expr $umask + 22 \ - - $umask % 100 % 40 + $umask % 20 \ - - $umask % 10 % 4 + $umask % 2 - `;; - *) mkdir_umask=$umask,go-w;; - esac - - # With -d, create the new directory with the user-specified mode. - # Otherwise, rely on $mkdir_umask. - if test -n "$dir_arg"; then - mkdir_mode=-m$mode - else - mkdir_mode= - fi - - posix_mkdir=false - case $umask in - *[123567][0-7][0-7]) - # POSIX mkdir -p sets u+wx bits regardless of umask, which - # is incompatible with FreeBSD 'install' when (umask & 300) != 0. - ;; - *) - tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$ - trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0 - - if (umask $mkdir_umask && - exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1 - then - if test -z "$dir_arg" || { - # Check for POSIX incompatibilities with -m. - # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or - # other-writable bit of parent directory when it shouldn't. - # FreeBSD 6.1 mkdir -m -p sets mode of existing directory. - ls_ld_tmpdir=`ls -ld "$tmpdir"` - case $ls_ld_tmpdir in - d????-?r-*) different_mode=700;; - d????-?--*) different_mode=755;; - *) false;; - esac && - $mkdirprog -m$different_mode -p -- "$tmpdir" && { - ls_ld_tmpdir_1=`ls -ld "$tmpdir"` - test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1" - } - } - then posix_mkdir=: - fi - rmdir "$tmpdir/d" "$tmpdir" - else - # Remove any dirs left behind by ancient mkdir implementations. - rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null - fi - trap '' 0;; - esac;; + # Create intermediate dirs using mode 755 as modified by the umask. + # This is like FreeBSD 'install' as of 1997-10-28. + umask=`umask` + case $stripcmd.$umask in + # Optimize common cases. + *[2367][2367]) mkdir_umask=$umask;; + .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;; + + *[0-7]) + mkdir_umask=`expr $umask + 22 \ + - $umask % 100 % 40 + $umask % 20 \ + - $umask % 10 % 4 + $umask % 2 + `;; + *) mkdir_umask=$umask,go-w;; + esac + + # With -d, create the new directory with the user-specified mode. + # Otherwise, rely on $mkdir_umask. + if test -n "$dir_arg"; then + mkdir_mode=-m$mode + else + mkdir_mode= + fi + + posix_mkdir=false + case $umask in + *[123567][0-7][0-7]) + # POSIX mkdir -p sets u+wx bits regardless of umask, which + # is incompatible with FreeBSD 'install' when (umask & 300) != 0. + ;; + *) + tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$ + trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0 + + if (umask $mkdir_umask && + exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1 + then + if test -z "$dir_arg" || { + # Check for POSIX incompatibilities with -m. + # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or + # other-writable bit of parent directory when it shouldn't. + # FreeBSD 6.1 mkdir -m -p sets mode of existing directory. + ls_ld_tmpdir=`ls -ld "$tmpdir"` + case $ls_ld_tmpdir in + d????-?r-*) different_mode=700;; + d????-?--*) different_mode=755;; + *) false;; + esac && + $mkdirprog -m$different_mode -p -- "$tmpdir" && { + ls_ld_tmpdir_1=`ls -ld "$tmpdir"` + test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1" + } + } + then posix_mkdir=: + fi + rmdir "$tmpdir/d" "$tmpdir" + else + # Remove any dirs left behind by ancient mkdir implementations. + rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null + fi + trap '' 0;; + esac;; esac if $posix_mkdir && ( - umask $mkdir_umask && - $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir" + umask $mkdir_umask && + $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir" ) then : else @@ -391,53 +370,51 @@ do # directory the slow way, step by step, checking for races as we go. case $dstdir in - /*) prefix='/';; - [-=\(\)!]*) prefix='./';; - *) prefix='';; + /*) prefix='/';; + [-=\(\)!]*) prefix='./';; + *) prefix='';; esac - eval "$initialize_posix_glob" - oIFS=$IFS IFS=/ - $posix_glob set -f + set -f set fnord $dstdir shift - $posix_glob set +f + set +f IFS=$oIFS prefixes= for d do - test X"$d" = X && continue - - prefix=$prefix$d - if test -d "$prefix"; then - prefixes= - else - if $posix_mkdir; then - (umask=$mkdir_umask && - $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break - # Don't fail if two instances are running concurrently. - test -d "$prefix" || exit 1 - else - case $prefix in - *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;; - *) qprefix=$prefix;; - esac - prefixes="$prefixes '$qprefix'" - fi - fi - prefix=$prefix/ + test X"$d" = X && continue + + prefix=$prefix$d + if test -d "$prefix"; then + prefixes= + else + if $posix_mkdir; then + (umask=$mkdir_umask && + $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break + # Don't fail if two instances are running concurrently. + test -d "$prefix" || exit 1 + else + case $prefix in + *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;; + *) qprefix=$prefix;; + esac + prefixes="$prefixes '$qprefix'" + fi + fi + prefix=$prefix/ done if test -n "$prefixes"; then - # Don't fail if two instances are running concurrently. - (umask $mkdir_umask && - eval "\$doit_exec \$mkdirprog $prefixes") || - test -d "$dstdir" || exit 1 - obsolete_mkdir_used=true + # Don't fail if two instances are running concurrently. + (umask $mkdir_umask && + eval "\$doit_exec \$mkdirprog $prefixes") || + test -d "$dstdir" || exit 1 + obsolete_mkdir_used=true fi fi fi @@ -472,15 +449,12 @@ do # If -C, don't bother to copy if it wouldn't change the file. if $copy_on_change && - old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` && - new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` && - - eval "$initialize_posix_glob" && - $posix_glob set -f && + old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` && + new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` && + set -f && set X $old && old=:$2:$4:$5:$6 && set X $new && new=:$2:$4:$5:$6 && - $posix_glob set +f && - + set +f && test "$old" = "$new" && $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1 then @@ -493,24 +467,24 @@ do # to itself, or perhaps because mv is so ancient that it does not # support -f. { - # Now remove or move aside any old file at destination location. - # We try this two ways since rm can't unlink itself on some - # systems and the destination file might be busy for other - # reasons. In this case, the final cleanup might fail but the new - # file should still install successfully. - { - test ! -f "$dst" || - $doit $rmcmd -f "$dst" 2>/dev/null || - { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null && - { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; } - } || - { echo "$0: cannot unlink or rename $dst" >&2 - (exit 1); exit 1 - } - } && - - # Now rename the file to the real destination. - $doit $mvcmd "$dsttmp" "$dst" + # Now remove or move aside any old file at destination location. + # We try this two ways since rm can't unlink itself on some + # systems and the destination file might be busy for other + # reasons. In this case, the final cleanup might fail but the new + # file should still install successfully. + { + test ! -f "$dst" || + $doit $rmcmd -f "$dst" 2>/dev/null || + { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null && + { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; } + } || + { echo "$0: cannot unlink or rename $dst" >&2 + (exit 1); exit 1 + } + } && + + # Now rename the file to the real destination. + $doit $mvcmd "$dsttmp" "$dst" } fi || exit 1 diff --git a/m4/ax_c_mallinfo.m4 b/m4/ax_c_mallinfo.m4 index af8d0481e..af8d0481e 100755..100644 --- a/m4/ax_c_mallinfo.m4 +++ b/m4/ax_c_mallinfo.m4 diff --git a/m4/ax_c_mallopt.m4 b/m4/ax_c_mallopt.m4 index 31c4fdc36..31c4fdc36 100755..100644 --- a/m4/ax_c_mallopt.m4 +++ b/m4/ax_c_mallopt.m4 diff --git a/m4/ax_c_statement_expressions.m4 b/m4/ax_c_statement_expressions.m4 new file mode 100644 index 000000000..fb259e727 --- /dev/null +++ b/m4/ax_c_statement_expressions.m4 @@ -0,0 +1,23 @@ +# AC_C_STMT_EXPR +# ------------- +# Define HAVE_STMT_EXPR if compiler has statement expressions. +AN_IDENTIFIER([_Generic], [AC_C_STMT_EXPR]) +AC_DEFUN([AC_C_STMT_EXPR], +[AC_CACHE_CHECK([for statement expressions], ac_cv_c_stmt_expr, +[AC_COMPILE_IFELSE( + [AC_LANG_SOURCE( + [[int + main (int argc, char **argv) + { + int x = ({ int y = 1; y; }); + return x; + } + ]])], + [ac_cv_c_stmt_expr=yes], + [ac_cv_c_stmt_expr=no])]) +if test $ac_cv_c_stmt_expr = yes; then + AC_DEFINE([HAVE_STMT_EXPR], 1, + [Define to 1 if compiler supports statement expressions.]) +fi +])# AC_C_STMT_EXPR + diff --git a/m4/ax_check_enable_debug.m4 b/m4/ax_check_enable_debug.m4 new file mode 100644 index 000000000..f99d75feb --- /dev/null +++ b/m4/ax_check_enable_debug.m4 @@ -0,0 +1,124 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_check_enable_debug.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_CHECK_ENABLE_DEBUG([enable by default=yes/info/profile/no], [ENABLE DEBUG VARIABLES ...], [DISABLE DEBUG VARIABLES NDEBUG ...], [IS-RELEASE]) +# +# DESCRIPTION +# +# Check for the presence of an --enable-debug option to configure, with +# the specified default value used when the option is not present. Return +# the value in the variable $ax_enable_debug. +# +# Specifying 'yes' adds '-g -O0' to the compilation flags for all +# languages. Specifying 'info' adds '-g' to the compilation flags. +# Specifying 'profile' adds '-g -pg' to the compilation flags and '-pg' to +# the linking flags. Otherwise, nothing is added. +# +# Define the variables listed in the second argument if debug is enabled, +# defaulting to no variables. Defines the variables listed in the third +# argument if debug is disabled, defaulting to NDEBUG. All lists of +# variables should be space-separated. +# +# If debug is not enabled, ensure AC_PROG_* will not add debugging flags. +# Should be invoked prior to any AC_PROG_* compiler checks. +# +# IS-RELEASE can be used to change the default to 'no' when making a +# release. Set IS-RELEASE to 'yes' or 'no' as appropriate. By default, it +# uses the value of $ax_is_release, so if you are using the AX_IS_RELEASE +# macro, there is no need to pass this parameter. +# +# AX_IS_RELEASE([git-directory]) +# AX_CHECK_ENABLE_DEBUG() +# +# LICENSE +# +# Copyright (c) 2011 Rhys Ulerich <rhys.ulerich@gmail.com> +# Copyright (c) 2014, 2015 Philip Withnall <philip@tecnocode.co.uk> +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. + +#serial 5 + +AC_DEFUN([AX_CHECK_ENABLE_DEBUG],[ + AC_BEFORE([$0],[AC_PROG_CC])dnl + AC_BEFORE([$0],[AC_PROG_CXX])dnl + AC_BEFORE([$0],[AC_PROG_F77])dnl + AC_BEFORE([$0],[AC_PROG_FC])dnl + + AC_MSG_CHECKING(whether to enable debugging) + + ax_enable_debug_default=m4_tolower(m4_normalize(ifelse([$1],,[no],[$1]))) + ax_enable_debug_is_release=m4_tolower(m4_normalize(ifelse([$4],, + [$ax_is_release], + [$4]))) + + # If this is a release, override the default. + AS_IF([test "$ax_enable_debug_is_release" = "yes"], + [ax_enable_debug_default="no"]) + + m4_define(ax_enable_debug_vars,[m4_normalize(ifelse([$2],,,[$2]))]) + m4_define(ax_disable_debug_vars,[m4_normalize(ifelse([$3],,[NDEBUG],[$3]))]) + + AC_ARG_ENABLE(debug, + [AS_HELP_STRING([--enable-debug=]@<:@yes/info/profile/no@:>@,[compile with debugging])], + [],enable_debug=$ax_enable_debug_default) + + # empty mean debug yes + AS_IF([test "x$enable_debug" = "x"], + [enable_debug="yes"]) + + # case of debug + AS_CASE([$enable_debug], + [yes],[ + AC_MSG_RESULT(yes) + CFLAGS="${CFLAGS} -g -O0" + CXXFLAGS="${CXXFLAGS} -g -O0" + FFLAGS="${FFLAGS} -g -O0" + FCFLAGS="${FCFLAGS} -g -O0" + OBJCFLAGS="${OBJCFLAGS} -g -O0" + ], + [info],[ + AC_MSG_RESULT(info) + CFLAGS="${CFLAGS} -g" + CXXFLAGS="${CXXFLAGS} -g" + FFLAGS="${FFLAGS} -g" + FCFLAGS="${FCFLAGS} -g" + OBJCFLAGS="${OBJCFLAGS} -g" + ], + [profile],[ + AC_MSG_RESULT(profile) + CFLAGS="${CFLAGS} -g -pg" + CXXFLAGS="${CXXFLAGS} -g -pg" + FFLAGS="${FFLAGS} -g -pg" + FCFLAGS="${FCFLAGS} -g -pg" + OBJCFLAGS="${OBJCFLAGS} -g -pg" + LDFLAGS="${LDFLAGS} -pg" + ], + [ + AC_MSG_RESULT(no) + dnl Ensure AC_PROG_CC/CXX/F77/FC/OBJC will not enable debug flags + dnl by setting any unset environment flag variables + AS_IF([test "x${CFLAGS+set}" != "xset"], + [CFLAGS=""]) + AS_IF([test "x${CXXFLAGS+set}" != "xset"], + [CXXFLAGS=""]) + AS_IF([test "x${FFLAGS+set}" != "xset"], + [FFLAGS=""]) + AS_IF([test "x${FCFLAGS+set}" != "xset"], + [FCFLAGS=""]) + AS_IF([test "x${OBJCFLAGS+set}" != "xset"], + [OBJCFLAGS=""]) + ]) + + dnl Define various variables if debugging is disabled. + dnl assert.h is a NOP if NDEBUG is defined, so define it by default. + AS_IF([test "x$enable_debug" = "xyes"], + [m4_map_args_w(ax_enable_debug_vars, [AC_DEFINE(], [,,[Define if debugging is enabled])])], + [m4_map_args_w(ax_disable_debug_vars, [AC_DEFINE(], [,,[Define if debugging is disabled])])]) + ax_enable_debug=$enable_debug +]) diff --git a/m4/ax_gcc_func_attribute.m4 b/m4/ax_gcc_func_attribute.m4 new file mode 100644 index 000000000..a6d9f6c5f --- /dev/null +++ b/m4/ax_gcc_func_attribute.m4 @@ -0,0 +1,226 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_gcc_func_attribute.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_GCC_FUNC_ATTRIBUTE(ATTRIBUTE) +# +# DESCRIPTION +# +# This macro checks if the compiler supports one of GCC's function +# attributes; many other compilers also provide function attributes with +# the same syntax. Compiler warnings are used to detect supported +# attributes as unsupported ones are ignored by default so quieting +# warnings when using this macro will yield false positives. +# +# The ATTRIBUTE parameter holds the name of the attribute to be checked. +# +# If ATTRIBUTE is supported define HAVE_FUNC_ATTRIBUTE_<ATTRIBUTE>. +# +# The macro caches its result in the ax_cv_have_func_attribute_<attribute> +# variable. +# +# The macro currently supports the following function attributes: +# +# alias +# aligned +# alloc_size +# always_inline +# artificial +# cold +# const +# constructor +# constructor_priority for constructor attribute with priority +# deprecated +# destructor +# dllexport +# dllimport +# error +# externally_visible +# flatten +# format +# format_arg +# gnu_inline +# hot +# ifunc +# leaf +# malloc +# noclone +# noinline +# nonnull +# noreturn +# nothrow +# optimize +# pure +# unused +# used +# visibility +# warning +# warn_unused_result +# weak +# weakref +# +# Unsuppored function attributes will be tested with a prototype returning +# an int and not accepting any arguments and the result of the check might +# be wrong or meaningless so use with care. +# +# LICENSE +# +# Copyright (c) 2013 Gabriele Svelto <gabriele.svelto@gmail.com> +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 4 + +AC_DEFUN([AX_GCC_FUNC_ATTRIBUTE], [ + AS_VAR_PUSHDEF([ac_var], [ax_cv_have_func_attribute_$1]) + + AC_CACHE_CHECK([for __attribute__(($1))], [ac_var], [ + AC_LINK_IFELSE([AC_LANG_PROGRAM([ + m4_case([$1], + [alias], [ + int foo( void ) { return 0; } + int bar( void ) __attribute__(($1("foo"))); + ], + [aligned], [ + int foo( void ) __attribute__(($1(32))); + ], + [alloc_size], [ + void *foo(int a) __attribute__(($1(1))); + ], + [always_inline], [ + inline __attribute__(($1)) int foo( void ) { return 0; } + ], + [artificial], [ + inline __attribute__(($1)) int foo( void ) { return 0; } + ], + [cold], [ + int foo( void ) __attribute__(($1)); + ], + [const], [ + int foo( void ) __attribute__(($1)); + ], + [constructor_priority], [ + int foo( void ) __attribute__((__constructor__(65535/2))); + ], + [constructor], [ + int foo( void ) __attribute__(($1)); + ], + [deprecated], [ + int foo( void ) __attribute__(($1(""))); + ], + [destructor], [ + int foo( void ) __attribute__(($1)); + ], + [dllexport], [ + __attribute__(($1)) int foo( void ) { return 0; } + ], + [dllimport], [ + int foo( void ) __attribute__(($1)); + ], + [error], [ + int foo( void ) __attribute__(($1(""))); + ], + [externally_visible], [ + int foo( void ) __attribute__(($1)); + ], + [flatten], [ + int foo( void ) __attribute__(($1)); + ], + [format], [ + int foo(const char *p, ...) __attribute__(($1(printf, 1, 2))); + ], + [format_arg], [ + char *foo(const char *p) __attribute__(($1(1))); + ], + [gnu_inline], [ + inline __attribute__(($1)) int foo( void ) { return 0; } + ], + [hot], [ + int foo( void ) __attribute__(($1)); + ], + [ifunc], [ + int my_foo( void ) { return 0; } + static int (*resolve_foo(void))(void) { return my_foo; } + int foo( void ) __attribute__(($1("resolve_foo"))); + ], + [leaf], [ + __attribute__(($1)) int foo( void ) { return 0; } + ], + [malloc], [ + void *foo( void ) __attribute__(($1)); + ], + [noclone], [ + int foo( void ) __attribute__(($1)); + ], + [noinline], [ + __attribute__(($1)) int foo( void ) { return 0; } + ], + [nonnull], [ + int foo(char *p) __attribute__(($1(1))); + ], + [noreturn], [ + void foo( void ) __attribute__(($1)); + ], + [nothrow], [ + int foo( void ) __attribute__(($1)); + ], + [optimize], [ + __attribute__(($1(3))) int foo( void ) { return 0; } + ], + [pure], [ + int foo( void ) __attribute__(($1)); + ], + [returns_nonnull], [ + void *foo( void ) __attribute__(($1)); + ], + [unused], [ + int foo( void ) __attribute__(($1)); + ], + [used], [ + int foo( void ) __attribute__(($1)); + ], + [visibility], [ + int foo_def( void ) __attribute__(($1("default"))); + int foo_hid( void ) __attribute__(($1("hidden"))); + int foo_int( void ) __attribute__(($1("internal"))); + int foo_pro( void ) __attribute__(($1("protected"))); + ], + [warning], [ + int foo( void ) __attribute__(($1(""))); + ], + [warn_unused_result], [ + int foo( void ) __attribute__(($1)); + ], + [weak], [ + int foo( void ) __attribute__(($1)); + ], + [weakref], [ + static int foo( void ) { return 0; } + static int bar( void ) __attribute__(($1("foo"))); + ], + [ + m4_warn([syntax], [Unsupported attribute $1, the test may fail]) + int foo( void ) __attribute__(($1)); + ] + )], []) + ], + dnl GCC doesn't exit with an error if an unknown attribute is + dnl provided but only outputs a warning, so accept the attribute + dnl only if no warning were issued. + [AS_IF([test -s conftest.err], + [AS_VAR_SET([ac_var], [no])], + [AS_VAR_SET([ac_var], [yes])])], + [AS_VAR_SET([ac_var], [no])]) + ]) + + AS_IF([test yes = AS_VAR_GET([ac_var])], + [AC_DEFINE_UNQUOTED(AS_TR_CPP(HAVE_FUNC_ATTRIBUTE_$1), 1, + [Define to 1 if the system has the `$1' function attribute])], []) + + AS_VAR_POPDEF([ac_var]) +]) @@ -3,7 +3,7 @@ scriptversion=2013-10-28.13; # UTC -# Copyright (C) 1996-2013 Free Software Foundation, Inc. +# Copyright (C) 1996-2014 Free Software Foundation, Inc. # Originally written by Fran,cois Pinard <pinard@iro.umontreal.ca>, 1996. # This program is free software; you can redistribute it and/or modify diff --git a/netdata-installer.sh b/netdata-installer.sh index c3b97243b..fa69de196 100755 --- a/netdata-installer.sh +++ b/netdata-installer.sh @@ -13,10 +13,17 @@ then fi LC_ALL=C -umask 022 +umask 002 + +# Be nice on production environments +renice 19 $$ >/dev/null 2>/dev/null + +processors=$(cat /proc/cpuinfo | grep ^processor | wc -l) +[ $(( processors )) -lt 1 ] && processors=1 # you can set CFLAGS before running installer -CFLAGS="${CFLAGS--O3}" +CFLAGS="${CFLAGS--O2}" +[ "z${CFLAGS}" = "z-O3" ] && CFLAGS="-O2" # keep a log of this command printf "\n# " >>netdata-installer.log @@ -106,7 +113,7 @@ Valid <installer options> are: Use this option to allow it continue without checking pkg-config. -Netdata will by default be compiled with gcc optimization -O3 +Netdata will by default be compiled with gcc optimization -O2 If you need to pass different CFLAGS, use something like this: CFLAGS="<gcc options>" ${ME} <installer options> @@ -141,7 +148,7 @@ get_git_config_signatures() { do git checkout ${c} "conf.d/${x}" || continue s="$(cat "conf.d/${x}" | md5sum | cut -d ' ' -f 1)" - echo >>configs.signatures.tmp "${x}:${s}" + echo >>configs.signatures.tmp "${s}:${x}" echo " ${s}" done git checkout HEAD "conf.d/${x}" || break @@ -153,7 +160,7 @@ get_git_config_signatures() { { echo "declare -A configs_signatures=(" IFS=":" - while read file md5 + while read md5 file do echo " ['${md5}']='${file}'" done @@ -427,7 +434,7 @@ if [ -f src/netdata ] fi echo >&2 "Compiling netdata ..." -run make || exit 1 +run make -j${processors} || exit 1 if [ "${BASH_VERSINFO[0]}" -ge "4" ] then @@ -541,46 +548,173 @@ do fi done -NETDATA_ADDED_TO_DOCKER=0 -if [ ${UID} -eq 0 ] +echo >&2 "Fixing permissions ..." + +check_cmd() { + which "${1}" >/dev/null 2>&1 && return 0 + command -v "${1}" >/dev/null 2>&1 && return 0 + return 1 +} + +portable_add_user() { + local username="${1}" + + getent passwd "${username}" > /dev/null 2>&1 + [ $? -eq 0 ] && return 0 + + echo >&2 "Adding ${username} user account ..." + + local nologin="$(which nologin 2>/dev/null || command -v nologin 2>/dev/null || echo '/bin/false')" + + # Linux + if check_cmd useradd then - getent group netdata > /dev/null - if [ $? -ne 0 ] + run useradd -r -g "${username}" -c "${username}" -s "${nologin}" -d / "${username}" && return 0 + fi + + # FreeBSD + if check_cmd pw + then + run pw useradd "${username}" -d / -g "${username}" -s "${nologin}" && return 0 + fi + + # BusyBox + if check_cmd adduser + then + run adduser -D -G "${username}" "${username}" && return 0 + fi + + echo >&2 "Failed to add ${username} user account !" + + return 1 +} + +portable_add_group() { + local groupname="${1}" + + getent group "${groupname}" > /dev/null 2>&1 + [ $? -eq 0 ] && return 0 + + echo >&2 "Adding ${groupname} user group ..." + + # Linux + if check_cmd groupadd + then + run groupadd -r "${groupname}" && return 0 + fi + + # FreeBSD + if check_cmd pw + then + run pw groupadd "${groupname}" && return 0 + fi + + # BusyBox + if check_cmd addgroup + then + run addgroup "${groupname}" && return 0 + fi + + echo >&2 "Failed to add ${groupname} user group !" + return 1 +} + +portable_add_user_to_group() { + local groupname="${1}" username="${2}" + + getent group "${groupname}" > /dev/null 2>&1 + [ $? -ne 0 ] && return 1 + + # find the user is already in the group + local users=$(getent group "${groupname}" | cut -d ':' -f 4) + if [[ ",${users}," =~ ,${username}, ]] + then + # username is already there + return 0 + else + # username is not in group + echo >&2 "Adding ${username} user to the ${groupname} group ..." + + # Linux + if check_cmd usermod + then + run usermod -a -G "${groupname}" "${username}" && return 0 + fi + + # FreeBSD + if check_cmd pw + then + run pw groupmod "${groupname}" -m "${username}" && return 0 + fi + + # BusyBox + if check_cmd addgroup then - echo >&2 "Adding netdata user group ..." - run groupadd -r netdata + run addgroup "${username}" "${groupname}" && return 0 + fi + + echo >&2 "Failed to add user ${username} to group ${groupname} !" + return 1 fi +} - getent passwd netdata > /dev/null - if [ $? -ne 0 ] +iscontainer() { + # man systemd-detect-virt + local cmd=$(which systemd-detect-virt 2>/dev/null || command -v systemd-detect-virt 2>/dev/null) + if [ ! -z "${cmd}" -a -x "${cmd}" ] then - echo >&2 "Adding netdata user account ..." - run useradd -r -g netdata -c netdata -s $(which nologin || echo '/bin/false') -d / netdata + "${cmd}" --container >/dev/null 2>&1 && return 0 fi - getent group docker > /dev/null - if [ $? -eq 0 ] + # /proc/1/sched exposes the host's pid of our init ! + # http://stackoverflow.com/a/37016302 + local pid=$( cat /proc/1/sched | head -n 1 | { IFS='(),#:' read name pid th threads; echo $pid; } ) + local p=$(( pid + 0 )) + [ ${pid} -ne 1 ] && return 0 + + # lxc sets environment variable 'container' + [ ! -z "${container}" ] && return 0 + + # docker creates /.dockerenv + # http://stackoverflow.com/a/25518345 + [ -f "/.dockerenv" ] && return 0 + + # ubuntu and debian supply /bin/running-in-container + # https://www.apt-browse.org/browse/ubuntu/trusty/main/i386/upstart/1.12.1-0ubuntu4/file/bin/running-in-container + if [ -x "/bin/running-in-container" ] then - # find the users in the docker group - docker=$(getent group docker | cut -d ':' -f 4) - if [[ ",${docker}," =~ ,netdata, ]] - then - # netdata is already there - : - else - # netdata is not in docker group - echo >&2 "Adding netdata user to the docker group (needed to get container names) ..." - run usermod -a -G docker netdata - fi - # let the uninstall script know - NETDATA_ADDED_TO_DOCKER=1 + "/bin/running-in-container" >/dev/null 2>&1 && return 0 fi + return 1 +} + +run find ./system/ -type f -a \! -name \*.in -a \! -name Makefile\* -a \! -name \*.conf -a \! -name \*.service -a \! -name \*.logrotate -exec chmod 755 {} \; + +NETDATA_ADDED_TO_DOCKER=0 +NETDATA_ADDED_TO_NGINX=0 +NETDATA_ADDED_TO_VARNISH=0 +NETDATA_ADDED_TO_HAPROXY=0 +if [ ${UID} -eq 0 ] + then + portable_add_group netdata + portable_add_user netdata + portable_add_user_to_group docker netdata && NETDATA_ADDED_TO_DOCKER=1 + portable_add_user_to_group nginx netdata && NETDATA_ADDED_TO_NGINX=1 + portable_add_user_to_group varnish netdata && NETDATA_ADDED_TO_VARNISH=1 + portable_add_user_to_group haproxy netdata && NETDATA_ADDED_TO_HAPROXY=1 + if [ -d /etc/logrotate.d -a ! -f /etc/logrotate.d/netdata ] then echo >&2 "Adding netdata logrotate configuration ..." run cp system/netdata.logrotate /etc/logrotate.d/netdata fi + + if [ -f /etc/logrotate.d/netdata ] + then + echo >&2 "Fixing netdata logrotate permissions ..." + run chmod 644 /etc/logrotate.d/netdata + fi fi @@ -603,17 +737,20 @@ config_option() { echo "${value}" } -# user -defuser="netdata" -[ ! "${UID}" = "0" ] && defuser="${USER}" -NETDATA_USER="$( config_option "run as user" "${defuser}" )" +# the user netdata will run as +if [ "${UID}" = "0" ] + then + NETDATA_USER="$( config_option "run as user" "netdata" )" +else + NETDATA_USER="${USER}" +fi -NETDATA_WEB_USER="$( config_option "web files owner" "${defuser}" )" +# the owners of the web files +NETDATA_WEB_USER="$( config_option "web files owner" "${NETDATA_USER}" )" NETDATA_WEB_GROUP="$( config_option "web files group" "${NETDATA_WEB_USER}" )" # debug flags -defdebug=0 -NETDATA_DEBUG="$( config_option "debug flags" ${defdebug} )" +NETDATA_DEBUG="$( config_option "debug flags" 0 )" # port defport=19999 @@ -648,7 +785,35 @@ fi echo >&2 echo >&2 "Fixing directories (user: ${NETDATA_USER})..." -for x in "${NETDATA_WEB_DIR}" "${NETDATA_CONF_DIR}" "${NETDATA_CACHE_DIR}" "${NETDATA_LOG_DIR}" "${NETDATA_LIB_DIR}" "${NETDATA_CONF_DIR}/python.d" "${NETDATA_CONF_DIR}/charts.d" "${NETDATA_CONF_DIR}/node.d" + +# --- conf dir ---- + +for x in "python.d" "charts.d" "node.d" +do + if [ ! -d "${NETDATA_CONF_DIR}/${x}" ] + then + echo >&2 "Creating directory '${NETDATA_CONF_DIR}/${x}'" + run mkdir -p "${NETDATA_CONF_DIR}/${x}" || exit 1 + fi +done +run chown -R "${NETDATA_USER}:${NETDATA_USER}" "${NETDATA_CONF_DIR}" +run find "${NETDATA_CONF_DIR}" -type f -exec chmod 0660 {} \; +run find "${NETDATA_CONF_DIR}" -type d -exec chmod 0775 {} \; + +# --- web dir ---- + +if [ ! -d "${NETDATA_WEB_DIR}" ] + then + echo >&2 "Creating directory '${NETDATA_WEB_DIR}'" + run mkdir -p "${NETDATA_WEB_DIR}" || exit 1 +fi +run chown -R "${NETDATA_WEB_USER}:${NETDATA_WEB_GROUP}" "${NETDATA_WEB_DIR}" +run find "${NETDATA_WEB_DIR}" -type f -exec chmod 0664 {} \; +run find "${NETDATA_WEB_DIR}" -type d -exec chmod 0775 {} \; + +# --- data dirs ---- + +for x in "${NETDATA_LIB_DIR}" "${NETDATA_CACHE_DIR}" "${NETDATA_LOG_DIR}" do if [ ! -d "${x}" ] then @@ -656,32 +821,59 @@ do run mkdir -p "${x}" || exit 1 fi - if [ ${UID} -eq 0 ] + run chown -R "${NETDATA_USER}:${NETDATA_USER}" "${x}" + #run find "${x}" -type f -exec chmod 0660 {} \; + #run find "${x}" -type d -exec chmod 0770 {} \; +done + +run chmod 755 "${NETDATA_LOG_DIR}" + +# --- plugins ---- + +if [ ${UID} -eq 0 ] + then + run chown "${NETDATA_USER}:root" "${NETDATA_LOG_DIR}" + run chown -R root "${NETDATA_PREFIX}/usr/libexec/netdata" + run find "${NETDATA_PREFIX}/usr/libexec/netdata" -type d -exec chmod 0755 {} \; + run find "${NETDATA_PREFIX}/usr/libexec/netdata" -type f -exec chmod 0644 {} \; + run find "${NETDATA_PREFIX}/usr/libexec/netdata" -type f -a -name \*.plugin -exec chmod 0755 {} \; + run find "${NETDATA_PREFIX}/usr/libexec/netdata" -type f -a -name \*.sh -exec chmod 0755 {} \; + + setcap_ret=1 + if ! iscontainer then - if [ "${x}" = "${NETDATA_WEB_DIR}" ] + run setcap cap_dac_read_search,cap_sys_ptrace+ep "${NETDATA_PREFIX}/usr/libexec/netdata/plugins.d/apps.plugin" + setcap_ret=$? + + if [ ${setcap_ret} -eq 0 ] then - run chown -R "${NETDATA_WEB_USER}:${NETDATA_WEB_GROUP}" "${x}" || echo >&2 "WARNING: Cannot change the ownership of the files in directory ${x} to ${NETDATA_WEB_USER}:${NETDATA_WEB_GROUP}..." - else - run chown -R "${NETDATA_USER}:${NETDATA_USER}" "${x}" || echo >&2 "WARNING: Cannot change the ownership of the files in directory ${x} to ${NETDATA_USER}..." + # if we managed to setcap + # but we fail to execute apps.plugin + # trigger setuid to root + "${NETDATA_PREFIX}/usr/libexec/netdata/plugins.d/apps.plugin" -v >/dev/null 2>&1 + setcap_ret=$? fi fi - run chmod 0755 "${x}" || echo >&2 "WARNING: Cannot change the permissions of the directory ${x} to 0755..." -done - -if [ ${UID} -eq 0 ] - then - run chown root "${NETDATA_PREFIX}/usr/libexec/netdata/plugins.d/apps.plugin" - run chmod 0755 "${NETDATA_PREFIX}/usr/libexec/netdata/plugins.d/apps.plugin" - run setcap cap_dac_read_search,cap_sys_ptrace+ep "${NETDATA_PREFIX}/usr/libexec/netdata/plugins.d/apps.plugin" - if [ $? -ne 0 ] + if [ ${setcap_ret} -ne 0 ] then # fix apps.plugin to be setuid to root run chown root "${NETDATA_PREFIX}/usr/libexec/netdata/plugins.d/apps.plugin" run chmod 4755 "${NETDATA_PREFIX}/usr/libexec/netdata/plugins.d/apps.plugin" fi +else + run chown "${NETDATA_USER}:${NETDATA_USER}" "${NETDATA_LOG_DIR}" + run chown -R "${NETDATA_USER}:${NETDATA_USER}" "${NETDATA_PREFIX}/usr/libexec/netdata" + run find "${NETDATA_PREFIX}/usr/libexec/netdata" -type f -exec chmod 0755 {} \; + run find "${NETDATA_PREFIX}/usr/libexec/netdata" -type d -exec chmod 0755 {} \; fi +# --- fix #1292 bug --- + +[ -d "${NETDATA_PREFIX}/usr/libexec" ] && run chmod a+rX "${NETDATA_PREFIX}/usr/libexec" +[ -d "${NETDATA_PREFIX}/usr/share/netdata" ] && run chmod a+rX "${NETDATA_PREFIX}/usr/share/netdata" + + # ----------------------------------------------------------------------------- # check if we can re-start netdata @@ -696,7 +888,7 @@ if [ ${DONOTSTART} -eq 1 ] then chown "${NETDATA_USER}" "${NETDATA_PREFIX}/etc/netdata/netdata.conf" fi - chmod 0664 "${NETDATA_PREFIX}/etc/netdata/netdata.conf" + chmod 0644 "${NETDATA_PREFIX}/etc/netdata/netdata.conf" fi banner "is installed now!" echo >&2 " enjoy real-time performance and health monitoring..." @@ -707,46 +899,52 @@ fi # stop a running netdata isnetdata() { - [ -z "$1" -o ! -f "/proc/$1/stat" ] && return 1 - [ "$(cat "/proc/$1/stat" | cut -d '(' -f 2 | cut -d ')' -f 1)" = "netdata" ] && return 0 - return 1 + if [ -d /proc/self ] + then + [ -z "$1" -o ! -f "/proc/$1/stat" ] && return 1 + [ "$(cat "/proc/$1/stat" | cut -d '(' -f 2 | cut -d ')' -f 1)" = "netdata" ] && return 0 + return 1 + fi + return 0 } stop_netdata_on_pid() { - local pid="$1" ret=0 count=0 + local pid="${1}" ret=0 count=0 - isnetdata $pid || return 0 + isnetdata ${pid} || return 0 - printf >&2 "Stopping netdata on pid $pid ..." - while [ ! -z "$pid" -a $ret -eq 0 ] + printf >&2 "Stopping netdata on pid ${pid} ..." + while [ ! -z "$pid" -a ${ret} -eq 0 ] do - if [ $count -gt 45 ] + if [ ${count} -gt 45 ] then - echo >&2 "Cannot stop the running netdata on pid $pid." + echo >&2 "Cannot stop the running netdata on pid ${pid}." return 1 fi count=$(( count + 1 )) - run kill $pid 2>/dev/null + run kill ${pid} 2>/dev/null ret=$? - test $ret -eq 0 && printf >&2 "." && sleep 2 + test ${ret} -eq 0 && printf >&2 "." && sleep 2 done echo >&2 - if [ $ret -eq 0 ] + if [ ${ret} -eq 0 ] then - echo >&2 "SORRY! CANNOT STOP netdata ON PID $pid !" + echo >&2 "SORRY! CANNOT STOP netdata ON PID ${pid} !" return 1 fi - echo >&2 "netdata on pid $pid stopped." + echo >&2 "netdata on pid ${pid} stopped." return 0 } stop_all_netdata() { - local p + local p myns ns + + myns="$(readlink /proc/self/ns/pid 2>/dev/null)" echo >&2 "Stopping a (possibly) running netdata..." @@ -755,27 +953,93 @@ stop_all_netdata() { $(cat /var/run/netdata/netdata.pid 2>/dev/null) \ $(pidof netdata 2>/dev/null) do - stop_netdata_on_pid $p + ns="$(readlink /proc/${p}/ns/pid 2>/dev/null)" + + if [ -z "${myns}" -o -z "${ns}" -o "${myns}" = "${ns}" ] + then + stop_netdata_on_pid ${p} + fi done } # ----------------------------------------------------------------------------- -# check netdata for systemd +# check for systemd issystemd() { + local pids p myns ns systemctl + # if the directory /etc/systemd/system does not exit, it is not systemd [ ! -d /etc/systemd/system ] && return 1 + # if there is no systemctl command, it is not systemd + systemctl=$(which systemctl 2>/dev/null || command -v systemctl 2>/dev/null) + [ -z "${systemctl}" -o ! -x "${systemctl}" ] && return 1 + # if pid 1 is systemd, it is systemd [ "$(basename $(readlink /proc/1/exe) 2>/dev/null)" = "systemd" ] && return 0 - # if systemd is running, it is systemd - pidof systemd >/dev/null 2>&1 && return 0 + # if systemd is not running, it is not systemd + pids=$(pidof systemd 2>/dev/null) + [ -z "${pids}" ] && return 1 + + # check if the running systemd processes are not in our namespace + myns="$(readlink /proc/self/ns/pid 2>/dev/null)" + for p in ${pids} + do + ns="$(readlink /proc/${p}/ns/pid 2>/dev/null)" + + # if pid of systemd is in our namespace, it is systemd + [ ! -z "${myns}" && "${myns}" = "${ns}" ] && return 0 + done # else, it is not systemd return 1 } +installed_init_d=0 +install_non_systemd_init() { + [ "${UID}" != 0 ] && return 1 + + local key="unknown" + if [ -f /etc/os-release ] + then + source /etc/os-release || return 1 + key="${ID}-${VERSION_ID}" + + elif [ -f /etc/centos-release ] + then + key=$(</etc/centos-release) + fi + + if [ -d /etc/init.d -a ! -f /etc/init.d/netdata ] + then + if [ "${key}" = "gentoo" ] + then + run cp system/netdata-openrc /etc/init.d/netdata && \ + run chmod 755 /etc/init.d/netdata && \ + run rc-update add netdata default && \ + installed_init_d=1 + + elif [ "${key}" = "ubuntu-12.04" -o "${key}" = "ubuntu-14.04" -o "${key}" = "debian-7" ] + then + run cp system/netdata-lsb /etc/init.d/netdata && \ + run chmod 755 /etc/init.d/netdata && \ + run update-rc.d netdata defaults && \ + run update-rc.d netdata enable && \ + installed_init_d=1 + + elif [ "${key}" = "CentOS release 6.8 (Final)" -o "${key}" = "amzn-2016.09" ] + then + run cp system/netdata-init-d /etc/init.d/netdata && \ + run chmod 755 /etc/init.d/netdata && \ + run chkconfig netdata on && \ + installed_init_d=1 + fi + fi + + return 0 +} + started=0 if [ "${UID}" -eq 0 ] then @@ -796,6 +1060,8 @@ if [ "${UID}" -eq 0 ] stop_all_netdata service netdata restart && started=1 + else + install_non_systemd_init fi if [ ${started} -eq 0 ] @@ -854,13 +1120,13 @@ if [ ! -s "${NETDATA_PREFIX}/etc/netdata/netdata.conf" ] ret=$? # try curl - if [ $ret -ne 0 -o ! -s "${NETDATA_PREFIX}/etc/netdata/netdata.conf.new" ] + if [ ${ret} -ne 0 -o ! -s "${NETDATA_PREFIX}/etc/netdata/netdata.conf.new" ] then curl -s -o "${NETDATA_PREFIX}/etc/netdata/netdata.conf.new" "http://localhost:${NETDATA_PORT}/netdata.conf" ret=$? fi - if [ $ret -eq 0 -a -s "${NETDATA_PREFIX}/etc/netdata/netdata.conf.new" ] + if [ ${ret} -eq 0 -a -s "${NETDATA_PREFIX}/etc/netdata/netdata.conf.new" ] then mv "${NETDATA_PREFIX}/etc/netdata/netdata.conf.new" "${NETDATA_PREFIX}/etc/netdata/netdata.conf" echo >&2 "New configuration saved for you to edit at ${NETDATA_PREFIX}/etc/netdata/netdata.conf" @@ -1040,6 +1306,12 @@ if [ -f /etc/systemd/system/netdata.service ] rm -i /etc/systemd/system/netdata.service fi +if [ -f /etc/init.d/netdata ] + then + echo "Deleting /etc/init.d/netdata ..." + rm -i /etc/init.d/netdata +fi + getent passwd netdata > /dev/null if [ $? -eq 0 ] then @@ -1067,6 +1339,34 @@ if [ $? -eq 0 -a "${NETDATA_ADDED_TO_DOCKER}" = "1" ] echo " gpasswd -d netdata docker" fi +getent group nginx > /dev/null +if [ $? -eq 0 -a "${NETDATA_ADDED_TO_NGINX}" = "1" ] + then + echo + echo "You may also want to remove the netdata user from the nginx group" + echo "by running:" + echo " gpasswd -d netdata nginx" +fi + +getent group varnish > /dev/null +if [ $? -eq 0 -a "${NETDATA_ADDED_TO_VARNISH}" = "1" ] + then + echo + echo "You may also want to remove the netdata user from the varnish group" + echo "by running:" + echo " gpasswd -d netdata varnish" +fi + +getent group haproxy > /dev/null +if [ $? -eq 0 -a "${NETDATA_ADDED_TO_HAPROXY}" = "1" ] + then + echo + echo "You may also want to remove the netdata user from the haproxy group" + echo "by running:" + echo " gpasswd -d netdata haproxy" +fi + + UNINSTALL chmod 750 netdata-uninstaller.sh diff --git a/netdata.spec b/netdata.spec index 72f7b5a03..1d0988579 100644 --- a/netdata.spec +++ b/netdata.spec @@ -1,4 +1,15 @@ %define contentdir %{_datadir}/netdata +%if 0%{?suse_version} +%define distro_post %service_add_post netdata.service +%define distro_preun %service_del_preun netdata.service +%define distro_postun %service_del_postun netdata.service +%define distro_buildrequires BuildRequires:\ systemd-rpm-macros +%else +%define distro_post %systemd_post netdata.service +%define distro_preun %systemd_preun netdata.service +%define distro_postun %systemd_postun_with_restart netdata.service +%define distro_buildrequires %{nil} +%endif # This is temporary and should eventually be resolved. This bypasses # the default rhel __os_install_post which throws a python compile @@ -10,43 +21,62 @@ %bcond_without systemd # systemd %bcond_with nfacct # build with nfacct plugin -%if 0%{?fedora} || 0%{?rhel} >= 7 +%if 0%{?fedora} || 0%{?rhel} >= 7 || 0%{?suse_version} >= 1140 %else %undefine with_systemd %endif Summary: Real-time performance monitoring, done right Name: netdata -Version: 1.4.0 +Version: 1.5.0 Release: 1%{?dist} License: GPL v3+ Group: Applications/System -Source0: http://firehol.org/download/netdata/releases/v1.4.0/%{name}-1.4.0.tar.xz -URL: http://netdata.firehol.org/ +Source0: http://firehol.org/download/netdata/releases/v1.5.0/%{name}-1.5.0.tar.xz +URL: http://my-netdata.io/ +%distro_buildrequires +BuildRequires: /usr/bin/autoconf +BuildRequires: /usr/bin/automake BuildRequires: pkgconfig BuildRequires: xz BuildRequires: zlib-devel BuildRequires: libuuid-devel +Requires: zlib +Requires: libuuid +Requires(post): libcap +Recommends: curl +Recommends: iproute-tc +Recommends: lm_sensors +Recommends: nmap-ncat +Recommends: nodejs +Recommends: python +Recommends: PyYAML +Recommends: python2-PyMySQL +Recommends: python2-psycopg2 # Packages can be found in the EPEL repo %if %{with nfacct} BuildRequires: libmnl-devel BuildRequires: libnetfilter_acct-devel +Requires: libmnl +Requires: libnetfilter_acct %endif Requires(pre): /usr/sbin/groupadd Requires(pre): /usr/sbin/useradd %if %{with systemd} +%if 0%{?suse_version} +%{?systemd_requires} +%else Requires(preun): systemd-units Requires(postun): systemd-units Requires(post): systemd-units +%endif %else Requires(post): chkconfig %endif -BuildRoot: %{tmpdir}/%{name}-%{version}-root-%(id -u -n) - %description netdata is the fastest way to visualize metrics. It is a resource efficient, highly optimized system for collecting and visualizing any @@ -58,9 +88,10 @@ so that you can get insights of what is happening now and what just happened, on your systems and applications. %prep -%setup -q -n %{name}-1.4.0 +%setup -q %build +./autogen.sh %configure \ --with-zlib \ --with-math \ @@ -72,11 +103,10 @@ happened, on your systems and applications. rm -rf $RPM_BUILD_ROOT %{__make} %{?_smp_mflags} DESTDIR=$RPM_BUILD_ROOT install -find $RPM_BUILD_ROOT -name .keep -print0 | xargs --null --no-run-if-empty rm +find $RPM_BUILD_ROOT -name .keep -delete install -m 644 -p system/netdata.conf $RPM_BUILD_ROOT%{_sysconfdir}/%{name} - -mkdir -p $RPM_BUILD_ROOT%{_sysconfdir}/logrotate.d +install -d $RPM_BUILD_ROOT%{_sysconfdir}/logrotate.d install -m 644 -p system/netdata.logrotate $RPM_BUILD_ROOT%{_sysconfdir}/logrotate.d/%{name} %if %{with systemd} @@ -84,7 +114,7 @@ install -d $RPM_BUILD_ROOT%{_unitdir} install -m 644 -p system/netdata.service $RPM_BUILD_ROOT%{_unitdir}/netdata.service %else # install SYSV init stuff -mkdir -p $RPM_BUILD_ROOT/etc/rc.d/init.d +install -d $RPM_BUILD_ROOT/etc/rc.d/init.d install -m755 system/netdata-init-d \ $RPM_BUILD_ROOT/etc/rc.d/init.d/netdata %endif @@ -97,23 +127,26 @@ install -m755 system/netdata-init-d \ -s /sbin/nologin -r -d %{contentdir} netdata 2> /dev/null || : %post -%systemd_post netdata.service +%distro_post +setcap cap_dac_read,cap_sys_ptrace+ep /usr/libexec/netdata/plugins.d/apps.plugin || chmod 1755 /usr/libexec/netdata/plugins.d/apps.plugin %preun -%systemd_preun netdata.service +%distro_preun %postun -%systemd_postun_with_restart netdata.service +%distro_postun %else %pre # Add the "netdata" user getent group netdata >/dev/null || groupadd -r netdata +getent group docker >/dev/null || groupadd -r docker getent passwd netdata >/dev/null || \ - useradd -r -g netdata -s /sbin/nologin \ + useradd -r -g netdata -G docker -s /sbin/nologin \ -d %{contentdir} -c "netdata" netdata exit 0 %post +setcap cap_dac_read,cap_sys_ptrace+ep /usr/libexec/netdata/plugins.d/apps.plugin || chmod 1755 /usr/libexec/netdata/plugins.d/apps.plugin # Register the netdata service /sbin/chkconfig --add netdata # Only gets run on initial install (not upgrades or uninstalls) @@ -143,14 +176,23 @@ exit 0 rm -rf $RPM_BUILD_ROOT %files +%doc README.md %defattr(-,root,root) %dir %{_sysconfdir}/%{name} + %config(noreplace) %{_sysconfdir}/%{name}/*.conf +%config(noreplace) %{_sysconfdir}/%{name}/charts.d/*.conf %config(noreplace) %{_sysconfdir}/%{name}/health.d/*.conf +#%%config(noreplace) %{_sysconfdir}/%{name}/node.d/*.conf %config(noreplace) %{_sysconfdir}/%{name}/python.d/*.conf %config(noreplace) %{_sysconfdir}/logrotate.d/%{name} +# To be eventually moved to %%_defaultdocdir +%{_sysconfdir}/%{name}/node.d/*.md + +%caps(cap_dac_read_search,cap_sys_ptrace=ep) %{_libexecdir}/%{name}/plugins.d/apps.plugin + %{_libexecdir}/%{name} %{_sbindir}/%{name} @@ -159,6 +201,8 @@ rm -rf $RPM_BUILD_ROOT %attr(0700,netdata,netdata) %dir %{_localstatedir}/lib/%{name} %dir %{_datadir}/%{name} +%dir %{_sysconfdir}/%{name}/health.d +%dir %{_sysconfdir}/%{name}/python.d %if %{with systemd} %{_unitdir}/netdata.service @@ -172,6 +216,11 @@ rm -rf $RPM_BUILD_ROOT %{_datadir}/%{name}/web %changelog +* Sun Jan 22 2017 Costa Tsaousis <costa@tsaousis.gr> - 1.5.0-1 +- FreeBSD, MacOS, FreeNAS +- Backends support +- dozens of new and improved plugins +- dozens of new and improved alarms and notification methods * Tue Oct 4 2016 Costa Tsaousis <costa@tsaousis.gr> - 1.4.0-1 - the fastest netdata ever (with a better look too)! - improved IoT and containers support! diff --git a/netdata.spec.in b/netdata.spec.in index 278850e06..2fc4e99e4 100644 --- a/netdata.spec.in +++ b/netdata.spec.in @@ -1,4 +1,15 @@ %define contentdir %{_datadir}/netdata +%if 0%{?suse_version} +%define distro_post %service_add_post netdata.service +%define distro_preun %service_del_preun netdata.service +%define distro_postun %service_del_postun netdata.service +%define distro_buildrequires BuildRequires:\ systemd-rpm-macros +%else +%define distro_post %systemd_post netdata.service +%define distro_preun %systemd_preun netdata.service +%define distro_postun %systemd_postun_with_restart netdata.service +%define distro_buildrequires %{nil} +%endif # This is temporary and should eventually be resolved. This bypasses # the default rhel __os_install_post which throws a python compile @@ -10,7 +21,7 @@ %bcond_without systemd # systemd %bcond_with nfacct # build with nfacct plugin -%if 0%{?fedora} || 0%{?rhel} >= 7 +%if 0%{?fedora} || 0%{?rhel} >= 7 || 0%{?suse_version} >= 1140 %else %undefine with_systemd %endif @@ -22,31 +33,50 @@ Release: 1%{?dist} License: GPL v3+ Group: Applications/System Source0: http://firehol.org/download/netdata/releases/v@PACKAGE_VERSION@/%{name}-@PACKAGE_VERSION@.tar.xz -URL: http://netdata.firehol.org/ +URL: http://my-netdata.io/ +%distro_buildrequires +BuildRequires: /usr/bin/autoconf +BuildRequires: /usr/bin/automake BuildRequires: pkgconfig BuildRequires: xz BuildRequires: zlib-devel BuildRequires: libuuid-devel +Requires: zlib +Requires: libuuid +Requires(post): libcap +Recommends: curl +Recommends: iproute-tc +Recommends: lm_sensors +Recommends: nmap-ncat +Recommends: nodejs +Recommends: python +Recommends: PyYAML +Recommends: python2-PyMySQL +Recommends: python2-psycopg2 # Packages can be found in the EPEL repo %if %{with nfacct} BuildRequires: libmnl-devel BuildRequires: libnetfilter_acct-devel +Requires: libmnl +Requires: libnetfilter_acct %endif Requires(pre): /usr/sbin/groupadd Requires(pre): /usr/sbin/useradd %if %{with systemd} +%if 0%{?suse_version} +%{?systemd_requires} +%else Requires(preun): systemd-units Requires(postun): systemd-units Requires(post): systemd-units +%endif %else Requires(post): chkconfig %endif -BuildRoot: %{tmpdir}/%{name}-%{version}-root-%(id -u -n) - %description netdata is the fastest way to visualize metrics. It is a resource efficient, highly optimized system for collecting and visualizing any @@ -58,9 +88,10 @@ so that you can get insights of what is happening now and what just happened, on your systems and applications. %prep -%setup -q -n %{name}-@PACKAGE_VERSION@ +%setup -q %build +./autogen.sh %configure \ --with-zlib \ --with-math \ @@ -72,11 +103,10 @@ happened, on your systems and applications. rm -rf $RPM_BUILD_ROOT %{__make} %{?_smp_mflags} DESTDIR=$RPM_BUILD_ROOT install -find $RPM_BUILD_ROOT -name .keep -print0 | xargs --null --no-run-if-empty rm +find $RPM_BUILD_ROOT -name .keep -delete install -m 644 -p system/netdata.conf $RPM_BUILD_ROOT%{_sysconfdir}/%{name} - -mkdir -p $RPM_BUILD_ROOT%{_sysconfdir}/logrotate.d +install -d $RPM_BUILD_ROOT%{_sysconfdir}/logrotate.d install -m 644 -p system/netdata.logrotate $RPM_BUILD_ROOT%{_sysconfdir}/logrotate.d/%{name} %if %{with systemd} @@ -84,7 +114,7 @@ install -d $RPM_BUILD_ROOT%{_unitdir} install -m 644 -p system/netdata.service $RPM_BUILD_ROOT%{_unitdir}/netdata.service %else # install SYSV init stuff -mkdir -p $RPM_BUILD_ROOT/etc/rc.d/init.d +install -d $RPM_BUILD_ROOT/etc/rc.d/init.d install -m755 system/netdata-init-d \ $RPM_BUILD_ROOT/etc/rc.d/init.d/netdata %endif @@ -97,23 +127,26 @@ install -m755 system/netdata-init-d \ -s /sbin/nologin -r -d %{contentdir} netdata 2> /dev/null || : %post -%systemd_post netdata.service +%distro_post +setcap cap_dac_read,cap_sys_ptrace+ep /usr/libexec/netdata/plugins.d/apps.plugin || chmod 1755 /usr/libexec/netdata/plugins.d/apps.plugin %preun -%systemd_preun netdata.service +%distro_preun %postun -%systemd_postun_with_restart netdata.service +%distro_postun %else %pre # Add the "netdata" user getent group netdata >/dev/null || groupadd -r netdata +getent group docker >/dev/null || groupadd -r docker getent passwd netdata >/dev/null || \ - useradd -r -g netdata -s /sbin/nologin \ + useradd -r -g netdata -G docker -s /sbin/nologin \ -d %{contentdir} -c "netdata" netdata exit 0 %post +setcap cap_dac_read,cap_sys_ptrace+ep /usr/libexec/netdata/plugins.d/apps.plugin || chmod 1755 /usr/libexec/netdata/plugins.d/apps.plugin # Register the netdata service /sbin/chkconfig --add netdata # Only gets run on initial install (not upgrades or uninstalls) @@ -143,14 +176,23 @@ exit 0 rm -rf $RPM_BUILD_ROOT %files +%doc README.md %defattr(-,root,root) %dir %{_sysconfdir}/%{name} + %config(noreplace) %{_sysconfdir}/%{name}/*.conf +%config(noreplace) %{_sysconfdir}/%{name}/charts.d/*.conf %config(noreplace) %{_sysconfdir}/%{name}/health.d/*.conf +#%%config(noreplace) %{_sysconfdir}/%{name}/node.d/*.conf %config(noreplace) %{_sysconfdir}/%{name}/python.d/*.conf %config(noreplace) %{_sysconfdir}/logrotate.d/%{name} +# To be eventually moved to %%_defaultdocdir +%{_sysconfdir}/%{name}/node.d/*.md + +%caps(cap_dac_read_search,cap_sys_ptrace=ep) %{_libexecdir}/%{name}/plugins.d/apps.plugin + %{_libexecdir}/%{name} %{_sbindir}/%{name} @@ -159,6 +201,8 @@ rm -rf $RPM_BUILD_ROOT %attr(0700,netdata,netdata) %dir %{_localstatedir}/lib/%{name} %dir %{_datadir}/%{name} +%dir %{_sysconfdir}/%{name}/health.d +%dir %{_sysconfdir}/%{name}/python.d %if %{with systemd} %{_unitdir}/netdata.service @@ -172,6 +216,11 @@ rm -rf $RPM_BUILD_ROOT %{_datadir}/%{name}/web %changelog +* Sun Jan 22 2017 Costa Tsaousis <costa@tsaousis.gr> - 1.5.0-1 +- FreeBSD, MacOS, FreeNAS +- Backends support +- dozens of new and improved plugins +- dozens of new and improved alarms and notification methods * Tue Oct 4 2016 Costa Tsaousis <costa@tsaousis.gr> - 1.4.0-1 - the fastest netdata ever (with a better look too)! - improved IoT and containers support! diff --git a/node.d/Makefile.in b/node.d/Makefile.in index b338c0bdc..66803fc02 100644 --- a/node.d/Makefile.in +++ b/node.d/Makefile.in @@ -1,7 +1,7 @@ -# Makefile.in generated by automake 1.14.1 from Makefile.am. +# Makefile.in generated by automake 1.15 from Makefile.am. # @configure_input@ -# Copyright (C) 1994-2013 Free Software Foundation, Inc. +# Copyright (C) 1994-2014 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -15,7 +15,17 @@ @SET_MAKE@ VPATH = @srcdir@ -am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ @@ -79,19 +89,20 @@ POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = node.d -DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ - $(dist_node_DATA) $(dist_nodemodules_DATA) \ - $(dist_nodemodulesber_DATA) ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_c___atomic.m4 \ $(top_srcdir)/m4/ax_c__generic.m4 \ $(top_srcdir)/m4/ax_c_mallinfo.m4 \ $(top_srcdir)/m4/ax_c_mallopt.m4 \ $(top_srcdir)/m4/ax_check_compile_flag.m4 \ + $(top_srcdir)/m4/ax_gcc_func_attribute.m4 \ $(top_srcdir)/m4/ax_pthread.m4 $(top_srcdir)/m4/jemalloc.m4 \ $(top_srcdir)/m4/tcmalloc.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(dist_node_DATA) \ + $(dist_nodemodules_DATA) $(dist_nodemodulesber_DATA) \ + $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = @@ -147,6 +158,7 @@ am__installdirs = "$(DESTDIR)$(nodedir)" "$(DESTDIR)$(nodemodulesdir)" \ DATA = $(dist_node_DATA) $(dist_nodemodules_DATA) \ $(dist_nodemodulesber_DATA) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +am__DIST_COMMON = $(srcdir)/Makefile.in DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ @@ -325,7 +337,6 @@ $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__confi echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu node.d/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu node.d/Makefile -.PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ @@ -566,6 +577,8 @@ uninstall-am: uninstall-dist_nodeDATA uninstall-dist_nodemodulesDATA \ uninstall-dist_nodemodulesDATA \ uninstall-dist_nodemodulesberDATA +.PRECIOUS: Makefile + # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. diff --git a/node.d/named.node.js b/node.d/named.node.js index 19b8ce29b..3d7946557 100644 --- a/node.d/named.node.js +++ b/node.d/named.node.js @@ -6,8 +6,8 @@ // http://jpmens.net/2013/03/18/json-in-bind-9-s-statistics-server/ // https://ftp.isc.org/isc/bind/9.10.3/doc/arm/Bv9ARM.ch06.html#statistics -// example configuration in /etc/netdata/named.conf -// the module supports auto-detection if bind is running in localhost +// example configuration in /etc/netdata/node.d/named.conf +// the module supports auto-detection if bind is running at localhost /* { @@ -41,7 +41,7 @@ var http = require('http'); var XML = require('pixl-xml'); var netdata = require('netdata'); -if(netdata.options.DEBUG === true) netdata.debug('loaded ' + __filename + ' plugin'); +if(netdata.options.DEBUG === true) netdata.debug('loaded', __filename, 'plugin'); var named = { name: __filename, @@ -62,10 +62,14 @@ var named = { priority: priority, // the priority relative to others in the same family update_every: service.update_every, // the expected update frequency of the chart dimensions: {} - } + }; var found = 0; - for(var x in obj) { + var dims = Object.keys(obj); + var len = dims.length; + for(var i = 0; i < len ;i++) { + var x = dims[i]; + if(typeof(obj[x]) !== 'undefined' && obj[x] !== 0) { found++; chart.dimensions[x] = { @@ -90,6 +94,7 @@ var named = { chartFromMembers: function(service, obj, id_suffix, title_suffix, units, family, context, type, priority, algorithm, multiplier, divisor) { var id = 'named_' + service.name + '.' + id_suffix; var chart = this.charts[id]; + var dims, len, x, i; if(typeof chart === 'undefined') { chart = this.chartFromMembersCreate(service, obj, id, title_suffix, units, family, context, type, priority, algorithm, multiplier, divisor); @@ -97,7 +102,10 @@ var named = { } else { // check if we need to re-generate the chart - for(var x in obj) { + dims = Object.keys(obj); + len = dims.length; + for(i = 0; i < len ;i++) { + x = dims[i]; if(typeof(chart.dimensions[x]) === 'undefined') { chart = this.chartFromMembersCreate(service, obj, id, title_suffix, units, family, context, type, priority, algorithm, multiplier, divisor); if(chart === null) return false; @@ -106,18 +114,22 @@ var named = { } } - var found = 0; service.begin(chart); - for(var x in obj) { + + var found = 0; + dims = Object.keys(obj); + len = dims.length; + for(i = 0; i < len ;i++) { + x = dims[i]; if(typeof(chart.dimensions[x]) !== 'undefined') { found++; service.set(x, obj[x]); } } + service.end(); - if(found > 0) return true; - return false; + return (found > 0); }, // an index to map values to different charts @@ -133,19 +145,21 @@ var named = { var d = XML.parse(data_xml); if(d === null) return null; + var a, aa, alen, alen2; + var data = {}; var len = d.server.counters.length; while(len--) { - var a = d.server.counters[len]; + a = d.server.counters[len]; if(typeof a.counter === 'undefined') continue; if(a.type === 'opcode') a.type = 'opcodes'; else if(a.type === 'qtype') a.type = 'qtypes'; else if(a.type === 'nsstat') a.type = 'nsstats'; - var aa = data[a.type] = {}; - var alen = 0 - var alen2 = a.counter.length; + aa = data[a.type] = {}; + alen = 0; + alen2 = a.counter.length; while(alen < alen2) { - aa[a.counter[alen].name] = parseInt(a.counter[alen]._Data); + aa[a.counter[alen].name] = parseInt(a.counter[alen]._Data, 10); alen++; } } @@ -155,18 +169,18 @@ var named = { while(vlen--) { var vname = d.views.view[vlen].name; data.views[vname] = { resolver: {} }; - var len = d.views.view[vlen].counters.length; + len = d.views.view[vlen].counters.length; while(len--) { - var a = d.views.view[vlen].counters[len]; + a = d.views.view[vlen].counters[len]; if(typeof a.counter === 'undefined') continue; if(a.type === 'resstats') a.type = 'stats'; else if(a.type === 'resqtype') a.type = 'qtypes'; else if(a.type === 'adbstat') a.type = 'adb'; - var aa = data.views[vname].resolver[a.type] = {}; - var alen = 0; - var alen2 = a.counter.length; + aa = data.views[vname].resolver[a.type] = {}; + alen = 0; + alen2 = a.counter.length; while(alen < alen2) { - aa[a.counter[alen].name] = parseInt(a.counter[alen]._Data); + aa[a.counter[alen].name] = parseInt(a.counter[alen]._Data, 10); alen++; } } @@ -177,7 +191,7 @@ var named = { processResponse: function(service, data) { if(data !== null) { - var r; + var r, x, look, id, chart, keys, len; // parse XML or JSON // pepending on the URL given @@ -212,11 +226,15 @@ var named = { delete r.nsstats['RecursClients']; } - for( var x in r.nsstats ) { + keys = Object.keys(r.nsstats); + len = keys.length; + while(len--) { + x = keys[len]; + // we maintain an index of the values found // mapping them to objects splitted - var look = named.lookups.nsstats[x]; + look = named.lookups.nsstats[x]; if(typeof look === 'undefined') { // a new value, not found in the index // index it: @@ -299,66 +317,64 @@ var named = { } } - if(global_requests_enable == true) + if(global_requests_enable === true) service.module.chartFromMembers(service, global_requests, 'received_requests', 'Bind, Global Received Requests by IP version', 'requests/s', 'requests', 'named.requests', netdata.chartTypes.stacked, named.base_priority + 1, netdata.chartAlgorithms.incremental, 1, 1); - if(global_queries_success_enable == true) + if(global_queries_success_enable === true) service.module.chartFromMembers(service, global_queries_success, 'global_queries_success', 'Bind, Global Successful Queries', 'queries/s', 'queries', 'named.queries_succcess', netdata.chartTypes.line, named.base_priority + 2, netdata.chartAlgorithms.incremental, 1, 1); - if(protocol_queries_enable == true) + if(protocol_queries_enable === true) service.module.chartFromMembers(service, protocol_queries, 'protocols_queries', 'Bind, Global Queries by IP Protocol', 'queries/s', 'queries', 'named.protocol_queries', netdata.chartTypes.stacked, named.base_priority + 3, netdata.chartAlgorithms.incremental, 1, 1); - if(global_queries_enable == true) + if(global_queries_enable === true) service.module.chartFromMembers(service, global_queries, 'global_queries', 'Bind, Global Queries Analysis', 'queries/s', 'queries', 'named.global_queries', netdata.chartTypes.stacked, named.base_priority + 4, netdata.chartAlgorithms.incremental, 1, 1); - if(global_updates_enable == true) + if(global_updates_enable === true) service.module.chartFromMembers(service, global_updates, 'received_updates', 'Bind, Global Received Updates', 'updates/s', 'updates', 'named.global_updates', netdata.chartTypes.stacked, named.base_priority + 5, netdata.chartAlgorithms.incremental, 1, 1); - if(global_failures_enable == true) + if(global_failures_enable === true) service.module.chartFromMembers(service, global_failures, 'query_failures', 'Bind, Global Query Failures', 'failures/s', 'failures', 'named.global_failures', netdata.chartTypes.line, named.base_priority + 6, netdata.chartAlgorithms.incremental, 1, 1); - if(global_failures_detail_enable == true) + if(global_failures_detail_enable === true) service.module.chartFromMembers(service, global_failures_detail, 'query_failures_detail', 'Bind, Global Query Failures Analysis', 'failures/s', 'failures', 'named.global_failures_detail', netdata.chartTypes.stacked, named.base_priority + 7, netdata.chartAlgorithms.incremental, 1, 1); if(default_enable === true) service.module.chartFromMembers(service, r.nsstats, 'nsstats', 'Bind, Other Global Server Statistics', 'operations/s', 'other', 'named.nsstats', netdata.chartTypes.line, named.base_priority + 8, netdata.chartAlgorithms.incremental, 1, 1); // RecursClients chart - { - var id = 'named_' + service.name + '.recursive_clients'; - var chart = named.charts[id]; - - if(typeof chart === 'undefined') { - chart = { - id: id, // the unique id of the chart - name: '', // the unique name of the chart - title: service.name + ' Bind, Current Recursive Clients', // the title of the chart - units: 'clients', // the units of the chart dimensions - family: 'clients', // the family of the chart - context: 'named.recursive_clients', // the context of the chart - type: netdata.chartTypes.line, // the type of the chart - priority: named.base_priority + 1, // the priority relative to others in the same family - update_every: service.update_every, // the expected update frequency of the chart - dimensions: { - 'clients': { - id: 'clients', // the unique id of the dimension - name: '', // the name of the dimension - algorithm: netdata.chartAlgorithms.absolute,// the id of the netdata algorithm - multiplier: 1, // the multiplier - divisor: 1, // the divisor - hidden: false // is hidden (boolean) - } + id = 'named_' + service.name + '.recursive_clients'; + chart = named.charts[id]; + + if(typeof chart === 'undefined') { + chart = { + id: id, // the unique id of the chart + name: '', // the unique name of the chart + title: service.name + ' Bind, Current Recursive Clients', // the title of the chart + units: 'clients', // the units of the chart dimensions + family: 'clients', // the family of the chart + context: 'named.recursive_clients', // the context of the chart + type: netdata.chartTypes.line, // the type of the chart + priority: named.base_priority + 1, // the priority relative to others in the same family + update_every: service.update_every, // the expected update frequency of the chart + dimensions: { + 'clients': { + id: 'clients', // the unique id of the dimension + name: '', // the name of the dimension + algorithm: netdata.chartAlgorithms.absolute,// the id of the netdata algorithm + multiplier: 1, // the multiplier + divisor: 1, // the divisor + hidden: false // is hidden (boolean) } - }; - - chart = service.chart(id, chart); - named.charts[id] = chart; - } + } + }; - service.begin(chart); - service.set('clients', RecursClients); - service.end(); + chart = service.chart(id, chart); + named.charts[id] = chart; } + + service.begin(chart); + service.set('clients', RecursClients); + service.end(); } if(typeof r.opcodes !== 'undefined') @@ -371,15 +387,18 @@ var named = { service.module.chartFromMembers(service, r.sockstats, 'in_sockstats', 'Bind, Global Socket Statistics', 'operations/s', 'sockets', 'named.in_sockstats', netdata.chartTypes.line, named.base_priority + 11, netdata.chartAlgorithms.incremental, 1, 1); if(typeof r.views !== 'undefined') { - for( var x in r.views ) { + keys = Object.keys(r.views); + len = keys.length; + while(len--) { + x = keys[len]; var resolver = r.views[x].resolver; if(typeof resolver !== 'undefined') { if(typeof resolver.stats !== 'undefined') { var NumFetch = 0; var key = service.name + '.' + x; - var default_enable = false; var rtt = {}, rtt_enable = false; + default_enable = false; // NumFetch is an absolute value if(typeof resolver.stats['NumFetch'] !== 'undefined') { @@ -392,11 +411,15 @@ var named = { } // split the QryRTT* from the main chart - for( var y in resolver.stats ) { + var ykeys = Object.keys(resolver.stats); + var ylen = ykeys.length; + while(ylen--) { + var y = ykeys[ylen]; + // we maintain an index of the values found // mapping them to objects splitted - var look = named.lookups.resolver_stats[y]; + look = named.lookups.resolver_stats[y]; if(typeof look === 'undefined') { if(y.match(/^QryRTT/) !== null) { named.lookups.resolver_stats[y] = { @@ -429,8 +452,8 @@ var named = { // NumFetch chart if(typeof named.lookups.numfetch[key] !== 'undefined') { - var id = 'named_' + service.name + '.view_resolver_numfetch_' + x; - var chart = named.charts[id]; + id = 'named_' + service.name + '.view_resolver_numfetch_' + x; + chart = named.charts[id]; if(typeof chart === 'undefined') { chart = { @@ -473,8 +496,8 @@ var named = { // service.module.chartFromMembers(service, resolver.cache, 'view_resolver_cache_' + x, 'Bind, ' + x + ' View, Cache Entries', 'entries', 'view_' + x, 'named.resolver_cache', netdata.chartTypes.stacked, named.base_priority + 15, netdata.chartAlgorithms.absolute, 1, 1); if(typeof resolver.cachestats['CacheHits'] !== 'undefined' && resolver.cachestats['CacheHits'] > 0) { - var id = 'named_' + service.name + '.view_resolver_cachehits_' + x; - var chart = named.charts[id]; + id = 'named_' + service.name + '.view_resolver_cachehits_' + x; + chart = named.charts[id]; if(typeof chart === 'undefined') { chart = { @@ -580,7 +603,7 @@ var named = { service.module.processResponse(serv, data); callback(); }); - }, + } }; module.exports = named; diff --git a/node.d/node_modules/netdata.js b/node.d/node_modules/netdata.js index 6183993b5..11202061e 100644 --- a/node.d/node_modules/netdata.js +++ b/node.d/node_modules/netdata.js @@ -1,5 +1,10 @@ 'use strict'; +// netdata +// real-time performance and health monitoring, done right! +// (C) 2016 Costa Tsaousis <costa@tsaousis.gr> +// GPL v3+ + var url = require('url'); var http = require('http'); var util = require('util'); @@ -8,609 +13,641 @@ var util = require('util'); var netdata = require('netdata'); var example_chart = { - id: 'id', // the unique id of the chart - name: 'name', // the name of the chart - title: 'title', // the title of the chart - units: 'units', // the units of the chart dimensions - family: 'family', // the family of the chart - context: 'context', // the context of the chart - type: netdata.chartTypes.line, // the type of the chart - priority: 0, // the priority relative to others in the same family - update_every: 1, // the expected update frequency of the chart - dimensions: { - 'dim1': { - id: 'dim1', // the unique id of the dimension - name: 'name', // the name of the dimension - algorithm: netdata.chartAlgorithms.absolute, // the id of the netdata algorithm - multiplier: 1, // the multiplier - divisor: 1, // the divisor - hidden: false, // is hidden (boolean) - }, - 'dim2': { - id: 'dim2', // the unique id of the dimension - name: 'name', // the name of the dimension - algorithm: 'absolute', // the id of the netdata algorithm - multiplier: 1, // the multiplier - divisor: 1, // the divisor - hidden: false, // is hidden (boolean) - } - // add as many dimensions as needed - } + id: 'id', // the unique id of the chart + name: 'name', // the name of the chart + title: 'title', // the title of the chart + units: 'units', // the units of the chart dimensions + family: 'family', // the family of the chart + context: 'context', // the context of the chart + type: netdata.chartTypes.line, // the type of the chart + priority: 0, // the priority relative to others in the same family + update_every: 1, // the expected update frequency of the chart + dimensions: { + 'dim1': { + id: 'dim1', // the unique id of the dimension + name: 'name', // the name of the dimension + algorithm: netdata.chartAlgorithms.absolute, // the id of the netdata algorithm + multiplier: 1, // the multiplier + divisor: 1, // the divisor + hidden: false, // is hidden (boolean) + }, + 'dim2': { + id: 'dim2', // the unique id of the dimension + name: 'name', // the name of the dimension + algorithm: 'absolute', // the id of the netdata algorithm + multiplier: 1, // the multiplier + divisor: 1, // the divisor + hidden: false, // is hidden (boolean) + } + // add as many dimensions as needed + } }; */ var netdata = { - options: { - filename: __filename, - DEBUG: false, - update_every: 1, - }, - - chartAlgorithms: { - incremental: 'incremental', - absolute: 'absolute', - percentage_of_absolute_row: 'percentage-of-absolute-row', - percentage_of_incremental_row: 'percentage-of-incremental-row' - }, - - chartTypes: { - line: 'line', - area: 'area', - stacked: 'stacked' - }, - - services: new Array(), - modules_configuring: 0, - charts: {}, - - - processors: { - http: { - name: 'http', - - process: function(service, callback) { - if(netdata.options.DEBUG === true) netdata.debug(service.module.name + ': ' + service.name + ': making ' + this.name + ' request: ' + netdata.stringify(service.request)); - - var req = http.request(service.request, function(response) { - if(netdata.options.DEBUG === true) netdata.debug(service.module.name + ': ' + service.name + ': got server response...'); - - var end = false; - var data = ''; - response.setEncoding('utf8'); - - if(response.statusCode !== 200) { - if(end === false) { - service.error('Got HTTP code ' + response.statusCode + ', failed to get data.'); - end = true; - callback(null); - } - } - - response.on('data', function(chunk) { - if(end === false) data += chunk; - }); - - response.on('error', function() { - if(end === false) { - service.error(': Read error, failed to get data.'); - end = true; - callback(null); - } - }); - - response.on('end', function() { - if(end === false) { - if(netdata.options.DEBUG === true) netdata.debug(service.module.name + ': ' + service.name + ': read completed.'); - end = true; - callback(data); - } - }); - }); - - req.on('error', function(e) { - service.error('Failed to make request: ' + netdata.stringify(service.request) + ', message: ' + e.message); - callback(null); - }); - - // write data to request body - if(typeof service.postData !== 'undefined' && service.request.method === 'POST') { - if(netdata.options.DEBUG === true) netdata.debug(service.module.name + ': ' + service.name + ': posting data: ' + service.postData); - req.write(service.postData); - } - - req.end(); - } - } - }, - - stringify: function(obj) { - return util.inspect(obj, {depth: 10}); - }, - - // show debug info, if debug is enabled - debug: function(msg) { - if(this.options.DEBUG === true) { - var now = new Date(); - console.error(now.toString() + ': ' + netdata.options.filename + ': DEBUG: ' + ((typeof(msg) === 'object')?netdata.stringify(msg):msg).toString()); - } - }, - - // log an error - error: function(msg) { - var now = new Date(); - console.error(now.toString() + ': ' + netdata.options.filename + ': ERROR: ' + ((typeof(msg) === 'object')?netdata.stringify(msg):msg).toString()); - }, - - // send data to netdata - send: function(msg) { - console.log(msg.toString()); - }, - - service: function(service) { - if(typeof service === 'undefined') - service = {}; - - var now = new Date().getTime(); - - service._current_chart = null; // the current chart we work on - service._queue = ''; // data to be sent to netdata - - service.error_reported = false; // error log flood control - - service.added = false; // added to netdata.services - service.enabled = true; - service.updates = 0; - service.running = false; - service.started = 0; - service.ended = 0; - - if(typeof service.module === 'undefined') { - service.module = { name: 'not-defined-module' }; - service.error('Attempted to create service without a module.'); - service.enabled = false; - } - - if(typeof service.name === 'undefined') { - service.name = 'unnamed@' + service.module.name + '/' + now; - } - - if(typeof service.processor === 'undefined') - service.processor = netdata.processors.http; - - if(typeof service.update_every === 'undefined') - service.update_every = service.module.update_every; - - if(typeof service.update_every === 'undefined') - service.update_every = netdata.options.update_every; - - if(service.update_every < netdata.options.update_every) - service.update_every = netdata.options.update_every; - - // align the runs - service.next_run = now - (now % (service.update_every * 1000)); - - service.commit = function() { - if(this.added !== true) { - this.added = true; - - var now = new Date().getTime(); - while( this.next_run < now ) - this.next_run += (this.update_every * 1000); - - netdata.services.push(this); - if(netdata.options.DEBUG === true) netdata.debug(this.module.name + ': ' + this.name + ': service committed.'); - } - }; - - service.execute = function(callback) { - if(service.enabled === false) { - callback(null); - return; - } - - this.module.active++; - this.running = true; - this.started = new Date().getTime(); - this.updates++; - - if(netdata.options.DEBUG === true) - netdata.debug(this.module.name + ': ' + this.name + ': making ' + this.processor.name + ' request: ' + netdata.stringify(this)); - - this.processor.process(this, function(response) { - service.ended = new Date().getTime(); - service.duration = service.ended - service.started; - - if(typeof response === 'undefined') - response = null; - - if(response !== null) - service.errorClear(); - - if(netdata.options.DEBUG === true) - netdata.debug(service.module.name + ': ' + service.name + ': processing ' + service.processor.name + ' response (received in ' + (service.ended - service.started).toString() + ' ms)'); - - callback(service, response); - - service.running = false; - service.module.active--; - if(service.module.active < 0) { - service.module.active = 0; - if(netdata.options.DEBUG === true) netdata.debug(service.module.name + ': active module counter below zero.'); - } - - if(service.module.active === 0) { - // check if we run under configure - if(service.module.configure_callback !== null) { - if(netdata.options.DEBUG === true) netdata.debug(service.module.name + ': configuration finish callback called from processResponse().'); - var ccallback = service.module.configure_callback; - service.module.configure_callback = null; - ccallback(); - } - } - }); - }; - - service.update = function() { - if(netdata.options.DEBUG === true) netdata.debug(this.module.name + ': ' + this.name + ': starting data collection...'); - - this.module.update(this, function() { - if(netdata.options.DEBUG === true) netdata.debug(service.module.name + ': ' + service.name + ': data collection ended in ' + service.duration.toString() + ' ms.'); - }); - }; - - service.error = function(message) { - if(this.error_reported === false) { - netdata.error(this.module.name + ': ' + this.name + ': ' + message); - this.error_reported = true; - } - else if(netdata.options.DEBUG === true) - netdata.debug(this.module.name + ': ' + this.name + ': ' + message); - }; - - service.errorClear = function() { - this.error_reported = false; - }; - - service.queue = function(txt) { - this._queue += txt + '\n'; - }; - - service._send_chart_to_netdata = function(chart) { - // internal function to send a chart to netdata - this.queue('CHART "' + chart.id + '" "' + chart.name + '" "' + chart.title + '" "' + chart.units + '" "' + chart.family + '" "' + chart.context + '" "' + chart.type + '" ' + chart.priority.toString() + ' ' + chart.update_every.toString()); - - for(var dim in chart.dimensions) { - var d = chart.dimensions[dim]; - - this.queue('DIMENSION "' + d.id + '" "' + d.name + '" "' + d.algorithm + '" ' + d.multiplier.toString() + ' ' + d.divisor.toString() + ' ' + ((d.hidden === true)?'hidden':'').toString()); - d._created = true; - d._updated = false; - } - - chart._created = true; - chart._updated = false; - }; - - // begin data collection for a chart - service.begin = function(chart) { - if(this._current_chart !== null && this._current_chart !== chart) { - this.error('Called begin() for chart ' + chart.id + ' while chart ' + this._current_chart.id + ' is still open. Closing it.'); - this.end(); - } - - if(typeof(chart.id) === 'undefined' || netdata.charts[chart.id] != chart) { - this.error('Called begin() for chart ' + chart.id + ' that is not mine. Where did you find it? Ignoring it.'); - return false; - } - - if(netdata.options.DEBUG === true) netdata.debug('setting current chart to ' + chart.id); - this._current_chart = chart; - this._current_chart._began = true; - - if(this._current_chart._dimensions_count !== 0) { - if(this._current_chart._created === false || this._current_chart._updated === true) - this._send_chart_to_netdata(this._current_chart); - - var now = this.ended; - this.queue('BEGIN ' + this._current_chart.id + ' ' + ((this._current_chart._last_updated > 0)?((now - this._current_chart._last_updated) * 1000):'').toString()); - } - // else this.error('Called begin() for chart ' + chart.id + ' which is empty.'); - - this._current_chart._last_updated = now; - this._current_chart._began = true; - this._current_chart._counter++; - - return true; - }; - - // set a collected value for a chart - // we do most things on the first value we attempt to set - service.set = function(dimension, value) { - if(this._current_chart === null) { - this.error('Called set(' + dimension + ', ' + value + ') without an open chart.'); - return false; - } - - if(typeof(this._current_chart.dimensions[dimension]) === 'undefined') { - this.error('Called set(' + dimension + ', ' + value + ') but dimension "' + dimension + '" does not exist in chart "' + this._current_chart.id + '".'); - return false; - } - - if(typeof value === 'undefined' || value === null) - return false; - - if(this._current_chart._dimensions_count !== 0) { - if (value instanceof Buffer) - this.queue('SET ' + dimension + ' = 0x' + value.toString('hex')); - else - this.queue('SET ' + dimension + ' = ' + value.toString()); - } - - return true; - }; - - // end data collection for the current chart - after calling begin() - service.end = function() { - if(this._current_chart !== null && this._current_chart._began === false) { - this.error('Called end() without an open chart.'); - return false; - } - - if(this._current_chart._dimensions_count !== 0) { - this.queue('END'); - netdata.send(this._queue); - } - - this._queue = ''; - this._current_chart._began = false; - if(netdata.options.DEBUG === true) netdata.debug('sent chart ' + this._current_chart.id); - this._current_chart = null; - return true; - }; - - // discard the collected values for the current chart - after calling begin() - service.flush = function() { - if(this._current_chart === null || this._current_chart._began === false) { - this.error('Called flush() without an open chart.'); - return false; - } - - this._queue = ''; - this._current_chart._began = false; - this._current_chart = null; - return true; - }; - - // create a netdata chart - service.chart = function(id, chart) { - if(typeof(netdata.charts[id]) === 'undefined') { - netdata.charts[id] = { - _created: false, - _updated: true, - _began: false, - _counter: 0, - _last_updated: 0, - _dimensions_count: 0, - id: id, - name: id, - title: 'untitled chart', - units: 'a unit', - family: '', - context: '', - type: netdata.chartTypes.line, - priority: 50000, - update_every: netdata.options.update_every, - dimensions: {} - }; - } - - var c = netdata.charts[id]; - - if(typeof(chart.name) !== 'undefined' && chart.name !== c.name) { - if(netdata.options.DEBUG === true) netdata.debug('chart ' + id + ' updated its name'); - c.name = chart.name; - c._updated = true; - } - - if(typeof(chart.title) !== 'undefined' && chart.title !== c.title) { - if(netdata.options.DEBUG === true) netdata.debug('chart ' + id + ' updated its title'); - c.title = chart.title; - c._updated = true; - } - - if(typeof(chart.units) !== 'undefined' && chart.units !== c.units) { - if(netdata.options.DEBUG === true) netdata.debug('chart ' + id + ' updated its units'); - c.units = chart.units; - c._updated = true; - } - - if(typeof(chart.family) !== 'undefined' && chart.family !== c.family) { - if(netdata.options.DEBUG === true) netdata.debug('chart ' + id + ' updated its family'); - c.family = chart.family; - c._updated = true; - } - - if(typeof(chart.context) !== 'undefined' && chart.context !== c.context) { - if(netdata.options.DEBUG === true) netdata.debug('chart ' + id + ' updated its context'); - c.context = chart.context; - c._updated = true; - } - - if(typeof(chart.type) !== 'undefined' && chart.type !== c.type) { - if(netdata.options.DEBUG === true) netdata.debug('chart ' + id + ' updated its type'); - c.type = chart.type; - c._updated = true; - } - - if(typeof(chart.priority) !== 'undefined' && chart.priority !== c.priority) { - if(netdata.options.DEBUG === true) netdata.debug('chart ' + id + ' updated its priority'); - c.priority = chart.priority; - c._updated = true; - } - - if(typeof(chart.update_every) !== 'undefined' && chart.update_every !== c.update_every) { - if(netdata.options.DEBUG === true) netdata.debug('chart ' + id + ' updated its update_every from ' + c.update_every + ' to ' + chart.update_every); - c.update_every = chart.update_every; - c._updated = true; - } - - if(typeof(chart.dimensions) !== 'undefined') { - for(var x in chart.dimensions) { - if(typeof(c.dimensions[x]) === 'undefined') { - c._dimensions_count++; - - c.dimensions[x] = { - _created: false, - _updated: false, - id: x, // the unique id of the dimension - name: x, // the name of the dimension - algorithm: netdata.chartAlgorithms.absolute, // the id of the netdata algorithm - multiplier: 1, // the multiplier - divisor: 1, // the divisor - hidden: false, // is hidden (boolean) - }; - - if(netdata.options.DEBUG === true) netdata.debug('chart ' + id + ' created dimension ' + x); - c._updated = true; - } - - var dim = chart.dimensions[x]; - var d = c.dimensions[x]; - - if(typeof(dim.name) !== 'undefined' && d.name !== dim.name) { - if(netdata.options.DEBUG === true) netdata.debug('chart ' + id + ', dimension ' + x + ' updated its name'); - d.name = dim.name; - d._updated = true; - } - - if(typeof(dim.algorithm) !== 'undefined' && d.algorithm !== dim.algorithm) { - if(netdata.options.DEBUG === true) netdata.debug('chart ' + id + ', dimension ' + x + ' updated its algorithm from ' + d.algorithm + ' to ' + dim.algorithm); - d.algorithm = dim.algorithm; - d._updated = true; - } - - if(typeof(dim.multiplier) !== 'undefined' && d.multiplier !== dim.multiplier) { - if(netdata.options.DEBUG === true) netdata.debug('chart ' + id + ', dimension ' + x + ' updated its multiplier'); - d.multiplier = dim.multiplier; - d._updated = true; - } - - if(typeof(dim.divisor) !== 'undefined' && d.divisor !== dim.divisor) { - if(netdata.options.DEBUG === true) netdata.debug('chart ' + id + ', dimension ' + x + ' updated its divisor'); - d.divisor = dim.divisor; - d._updated = true; - } - - if(typeof(dim.hidden) !== 'undefined' && d.hidden !== dim.hidden) { - if(netdata.options.DEBUG === true) netdata.debug('chart ' + id + ', dimension ' + x + ' updated its hidden status'); - d.hidden = dim.hidden; - d._updated = true; - } - - if(d._updated) c._updated = true; - } - } - - //if(netdata.options.DEBUG === true) netdata.debug(netdata.charts); - return netdata.charts[id]; - }; - - return service; - }, - - runAllServices: function() { - if(netdata.options.DEBUG === true) netdata.debug('runAllServices()'); - - var now = new Date().getTime(); - var len = netdata.services.length; - while(len--) { - var service = netdata.services[len]; - - if(service.enabled === false || service.running === true) continue; - if(now <= service.next_run) continue; - - service.update(); - - now = new Date().getTime(); - while(service.next_run < now) - service.next_run += (service.update_every * 1000); - } - - // 1/10th of update_every in pause - setTimeout(netdata.runAllServices, netdata.options.update_every * 100); - }, - - start: function() { - if(netdata.options.DEBUG === true) this.debug('started, services: ' + netdata.stringify(this.services)); - - if(this.services.length === 0) { - this.disableNodePlugin(); - process.exit(1); - } - else this.runAllServices(); - }, - - // disable the whole node.js plugin - disableNodePlugin: function() { - this.send('DISABLE'); - process.exit(1); - }, - - requestFromParams: function(protocol, hostname, port, path, method) { - return { - protocol: protocol, - hostname: hostname, - port: port, - path: path, - //family: 4, - method: method, - headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - 'Connection': 'keep-alive' - }, - agent: new http.Agent({ - keepAlive: true, - keepAliveMsecs: netdata.options.update_every * 1000, - maxSockets: 2, // it must be 2 to work - maxFreeSockets: 1 - }) - }; - }, - - requestFromURL: function(a_url) { - var u = url.parse(a_url); - return netdata.requestFromParams(u.protocol, u.hostname, u.port, u.path, 'GET'); - }, - - configure: function(module, config, callback) { - if(netdata.options.DEBUG === true) this.debug(module.name + ': configuring (update_every: ' + this.options.update_every + ')...'); - - module.active = 0; - module.update_every = this.options.update_every; - - if(typeof config.update_every !== 'undefined') - module.update_every = config.update_every; - - module.enable_autodetect = (config.enable_autodetect)?true:false; - - if(typeof(callback) === 'function') - module.configure_callback = callback; - else - module.configure_callback = null; - - var added = module.configure(config); - - if(netdata.options.DEBUG === true) this.debug(module.name + ': configured, reporting ' + added + ' eligible services.'); - - if(module.configure_callback !== null && added === 0) { - if(netdata.options.DEBUG === true) this.debug(module.name + ': configuration finish callback called from configure().'); - module.configure_callback = null; - callback(); - } - - return added; - } + options: { + filename: __filename, + DEBUG: false, + update_every: 1 + }, + + chartAlgorithms: { + incremental: 'incremental', + absolute: 'absolute', + percentage_of_absolute_row: 'percentage-of-absolute-row', + percentage_of_incremental_row: 'percentage-of-incremental-row' + }, + + chartTypes: { + line: 'line', + area: 'area', + stacked: 'stacked' + }, + + services: new Array(), + modules_configuring: 0, + charts: {}, + + + processors: { + http: { + name: 'http', + + process: function(service, callback) { + var __DEBUG = netdata.options.DEBUG; + + if(__DEBUG === true) + netdata.debug(service.module.name + ': ' + service.name + ': making ' + this.name + ' request: ' + netdata.stringify(service.request)); + + var req = http.request(service.request, function(response) { + if(__DEBUG === true) netdata.debug(service.module.name + ': ' + service.name + ': got server response...'); + + var end = false; + var data = ''; + response.setEncoding('utf8'); + + if(response.statusCode !== 200) { + if(end === false) { + service.error('Got HTTP code ' + response.statusCode + ', failed to get data.'); + end = true; + return callback(null); + } + } + + response.on('data', function(chunk) { + if(end === false) data += chunk; + }); + + response.on('error', function() { + if(end === false) { + service.error(': Read error, failed to get data.'); + end = true; + return callback(null); + } + }); + + response.on('end', function() { + if(end === false) { + if(__DEBUG === true) netdata.debug(service.module.name + ': ' + service.name + ': read completed.'); + end = true; + return callback(data); + } + }); + }); + + req.on('error', function(e) { + if(__DEBUG === true) netdata.debug('Failed to make request: ' + netdata.stringify(service.request) + ', message: ' + e.message); + service.error('Failed to make request, message: ' + e.message); + return callback(null); + }); + + // write data to request body + if(typeof service.postData !== 'undefined' && service.request.method === 'POST') { + if(__DEBUG === true) netdata.debug(service.module.name + ': ' + service.name + ': posting data: ' + service.postData); + req.write(service.postData); + } + + req.end(); + } + } + }, + + stringify: function(obj) { + return util.inspect(obj, {depth: 10}); + }, + + zeropad2: function(s) { + return ("00" + s).slice(-2); + }, + + logdate: function(d) { + if(typeof d === 'undefined') d = new Date(); + return d.getFullYear().toString() + '-' + this.zeropad2(d.getMonth() + 1) + '-' + this.zeropad2(d.getDate()) + + ' ' + this.zeropad2(d.getHours()) + ':' + this.zeropad2(d.getMinutes()) + ':' + this.zeropad2(d.getSeconds()); + }, + + // show debug info, if debug is enabled + debug: function(msg) { + if(this.options.DEBUG === true) { + console.error(this.logdate() + ': ' + netdata.options.filename + ': DEBUG: ' + ((typeof(msg) === 'object')?netdata.stringify(msg):msg).toString()); + } + }, + + // log an error + error: function(msg) { + console.error(this.logdate() + ': ' + netdata.options.filename + ': ERROR: ' + ((typeof(msg) === 'object')?netdata.stringify(msg):msg).toString()); + }, + + // send data to netdata + send: function(msg) { + console.log(msg.toString()); + }, + + service: function(service) { + if(typeof service === 'undefined') + service = {}; + + var now = Date.now(); + + service._current_chart = null; // the current chart we work on + service._queue = ''; // data to be sent to netdata + + service.error_reported = false; // error log flood control + + service.added = false; // added to netdata.services + service.enabled = true; + service.updates = 0; + service.running = false; + service.started = 0; + service.ended = 0; + + if(typeof service.module === 'undefined') { + service.module = { name: 'not-defined-module' }; + service.error('Attempted to create service without a module.'); + service.enabled = false; + } + + if(typeof service.name === 'undefined') { + service.name = 'unnamed@' + service.module.name + '/' + now; + } + + if(typeof service.processor === 'undefined') + service.processor = netdata.processors.http; + + if(typeof service.update_every === 'undefined') + service.update_every = service.module.update_every; + + if(typeof service.update_every === 'undefined') + service.update_every = netdata.options.update_every; + + if(service.update_every < netdata.options.update_every) + service.update_every = netdata.options.update_every; + + // align the runs + service.next_run = now - (now % (service.update_every * 1000)) + (service.update_every * 1000); + + service.commit = function() { + if(this.added !== true) { + this.added = true; + + var now = Date.now(); + this.next_run = now - (now % (service.update_every * 1000)) + (service.update_every * 1000); + + netdata.services.push(this); + if(netdata.options.DEBUG === true) netdata.debug(this.module.name + ': ' + this.name + ': service committed.'); + } + }; + + service.execute = function(responseProcessor) { + var __DEBUG = netdata.options.DEBUG; + + if(service.enabled === false) + return responseProcessor(null); + + this.module.active++; + this.running = true; + this.started = Date.now(); + this.updates++; + + if(__DEBUG === true) + netdata.debug(this.module.name + ': ' + this.name + ': making ' + this.processor.name + ' request: ' + netdata.stringify(this)); + + this.processor.process(this, function(response) { + service.ended = Date.now(); + service.duration = service.ended - service.started; + + if(typeof response === 'undefined') + response = null; + + if(response !== null) + service.errorClear(); + + if(__DEBUG === true) + netdata.debug(service.module.name + ': ' + service.name + ': processing ' + service.processor.name + ' response (received in ' + (service.ended - service.started).toString() + ' ms)'); + + responseProcessor(service, response); + + service.running = false; + service.module.active--; + if(service.module.active < 0) { + service.module.active = 0; + if(__DEBUG === true) + netdata.debug(service.module.name + ': active module counter below zero.'); + } + + if(service.module.active === 0) { + // check if we run under configure + if(service.module.configure_callback !== null) { + if(__DEBUG === true) + netdata.debug(service.module.name + ': configuration finish callback called from processResponse().'); + + var configure_callback = service.module.configure_callback; + service.module.configure_callback = null; + configure_callback(); + } + } + }); + }; + + service.update = function() { + if(netdata.options.DEBUG === true) + netdata.debug(this.module.name + ': ' + this.name + ': starting data collection...'); + + this.module.update(this, function() { + if(netdata.options.DEBUG === true) + netdata.debug(service.module.name + ': ' + service.name + ': data collection ended in ' + service.duration.toString() + ' ms.'); + }); + }; + + service.error = function(message) { + if(this.error_reported === false) { + netdata.error(this.module.name + ': ' + this.name + ': ' + message); + this.error_reported = true; + } + else if(netdata.options.DEBUG === true) + netdata.debug(this.module.name + ': ' + this.name + ': ' + message); + }; + + service.errorClear = function() { + this.error_reported = false; + }; + + service.queue = function(txt) { + this._queue += txt + '\n'; + }; + + service._send_chart_to_netdata = function(chart) { + // internal function to send a chart to netdata + this.queue('CHART "' + chart.id + '" "' + chart.name + '" "' + chart.title + '" "' + chart.units + '" "' + chart.family + '" "' + chart.context + '" "' + chart.type + '" ' + chart.priority.toString() + ' ' + chart.update_every.toString()); + + if(typeof(chart.dimensions) !== 'undefined') { + var dims = Object.keys(chart.dimensions); + var len = dims.length; + while(len--) { + var d = chart.dimensions[dims[len]]; + + this.queue('DIMENSION "' + d.id + '" "' + d.name + '" "' + d.algorithm + '" ' + d.multiplier.toString() + ' ' + d.divisor.toString() + ' ' + ((d.hidden === true) ? 'hidden' : '').toString()); + d._created = true; + d._updated = false; + } + } + + chart._created = true; + chart._updated = false; + }; + + // begin data collection for a chart + service.begin = function(chart) { + if(this._current_chart !== null && this._current_chart !== chart) { + this.error('Called begin() for chart ' + chart.id + ' while chart ' + this._current_chart.id + ' is still open. Closing it.'); + this.end(); + } + + if(typeof(chart.id) === 'undefined' || netdata.charts[chart.id] !== chart) { + this.error('Called begin() for chart ' + chart.id + ' that is not mine. Where did you find it? Ignoring it.'); + return false; + } + + if(netdata.options.DEBUG === true) netdata.debug('setting current chart to ' + chart.id); + this._current_chart = chart; + this._current_chart._began = true; + + if(this._current_chart._dimensions_count !== 0) { + if(this._current_chart._created === false || this._current_chart._updated === true) + this._send_chart_to_netdata(this._current_chart); + + var now = this.ended; + this.queue('BEGIN ' + this._current_chart.id + ' ' + ((this._current_chart._last_updated > 0)?((now - this._current_chart._last_updated) * 1000):'').toString()); + } + // else this.error('Called begin() for chart ' + chart.id + ' which is empty.'); + + this._current_chart._last_updated = now; + this._current_chart._began = true; + this._current_chart._counter++; + + return true; + }; + + // set a collected value for a chart + // we do most things on the first value we attempt to set + service.set = function(dimension, value) { + if(this._current_chart === null) { + this.error('Called set(' + dimension + ', ' + value + ') without an open chart.'); + return false; + } + + if(typeof(this._current_chart.dimensions[dimension]) === 'undefined') { + this.error('Called set(' + dimension + ', ' + value + ') but dimension "' + dimension + '" does not exist in chart "' + this._current_chart.id + '".'); + return false; + } + + if(typeof value === 'undefined' || value === null) + return false; + + if(this._current_chart._dimensions_count !== 0) { + if (value instanceof Buffer) + this.queue('SET ' + dimension + ' = 0x' + value.toString('hex')); + else + this.queue('SET ' + dimension + ' = ' + value.toString()); + } + + return true; + }; + + // end data collection for the current chart - after calling begin() + service.end = function() { + if(this._current_chart !== null && this._current_chart._began === false) { + this.error('Called end() without an open chart.'); + return false; + } + + if(this._current_chart._dimensions_count !== 0) { + this.queue('END'); + netdata.send(this._queue); + } + + this._queue = ''; + this._current_chart._began = false; + if(netdata.options.DEBUG === true) netdata.debug('sent chart ' + this._current_chart.id); + this._current_chart = null; + return true; + }; + + // discard the collected values for the current chart - after calling begin() + service.flush = function() { + if(this._current_chart === null || this._current_chart._began === false) { + this.error('Called flush() without an open chart.'); + return false; + } + + this._queue = ''; + this._current_chart._began = false; + this._current_chart = null; + return true; + }; + + // create a netdata chart + service.chart = function(id, chart) { + var __DEBUG = netdata.options.DEBUG; + + if(typeof(netdata.charts[id]) === 'undefined') { + netdata.charts[id] = { + _created: false, + _updated: true, + _began: false, + _counter: 0, + _last_updated: 0, + _dimensions_count: 0, + id: id, + name: id, + title: 'untitled chart', + units: 'a unit', + family: '', + context: '', + type: netdata.chartTypes.line, + priority: 50000, + update_every: netdata.options.update_every, + dimensions: {} + }; + } + + var c = netdata.charts[id]; + + if(typeof(chart.name) !== 'undefined' && chart.name !== c.name) { + if(__DEBUG === true) netdata.debug('chart ' + id + ' updated its name'); + c.name = chart.name; + c._updated = true; + } + + if(typeof(chart.title) !== 'undefined' && chart.title !== c.title) { + if(__DEBUG === true) netdata.debug('chart ' + id + ' updated its title'); + c.title = chart.title; + c._updated = true; + } + + if(typeof(chart.units) !== 'undefined' && chart.units !== c.units) { + if(__DEBUG === true) netdata.debug('chart ' + id + ' updated its units'); + c.units = chart.units; + c._updated = true; + } + + if(typeof(chart.family) !== 'undefined' && chart.family !== c.family) { + if(__DEBUG === true) netdata.debug('chart ' + id + ' updated its family'); + c.family = chart.family; + c._updated = true; + } + + if(typeof(chart.context) !== 'undefined' && chart.context !== c.context) { + if(__DEBUG === true) netdata.debug('chart ' + id + ' updated its context'); + c.context = chart.context; + c._updated = true; + } + + if(typeof(chart.type) !== 'undefined' && chart.type !== c.type) { + if(__DEBUG === true) netdata.debug('chart ' + id + ' updated its type'); + c.type = chart.type; + c._updated = true; + } + + if(typeof(chart.priority) !== 'undefined' && chart.priority !== c.priority) { + if(__DEBUG === true) netdata.debug('chart ' + id + ' updated its priority'); + c.priority = chart.priority; + c._updated = true; + } + + if(typeof(chart.update_every) !== 'undefined' && chart.update_every !== c.update_every) { + if(__DEBUG === true) netdata.debug('chart ' + id + ' updated its update_every from ' + c.update_every + ' to ' + chart.update_every); + c.update_every = chart.update_every; + c._updated = true; + } + + if(typeof(chart.dimensions) !== 'undefined') { + var dims = Object.keys(chart.dimensions); + var len = dims.length; + while(len--) { + var x = dims[len]; + + if(typeof(c.dimensions[x]) === 'undefined') { + c._dimensions_count++; + + c.dimensions[x] = { + _created: false, + _updated: false, + id: x, // the unique id of the dimension + name: x, // the name of the dimension + algorithm: netdata.chartAlgorithms.absolute, // the id of the netdata algorithm + multiplier: 1, // the multiplier + divisor: 1, // the divisor + hidden: false // is hidden (boolean) + }; + + if(__DEBUG === true) netdata.debug('chart ' + id + ' created dimension ' + x); + c._updated = true; + } + + var dim = chart.dimensions[x]; + var d = c.dimensions[x]; + + if(typeof(dim.name) !== 'undefined' && d.name !== dim.name) { + if(__DEBUG === true) netdata.debug('chart ' + id + ', dimension ' + x + ' updated its name'); + d.name = dim.name; + d._updated = true; + } + + if(typeof(dim.algorithm) !== 'undefined' && d.algorithm !== dim.algorithm) { + if(__DEBUG === true) netdata.debug('chart ' + id + ', dimension ' + x + ' updated its algorithm from ' + d.algorithm + ' to ' + dim.algorithm); + d.algorithm = dim.algorithm; + d._updated = true; + } + + if(typeof(dim.multiplier) !== 'undefined' && d.multiplier !== dim.multiplier) { + if(__DEBUG === true) netdata.debug('chart ' + id + ', dimension ' + x + ' updated its multiplier'); + d.multiplier = dim.multiplier; + d._updated = true; + } + + if(typeof(dim.divisor) !== 'undefined' && d.divisor !== dim.divisor) { + if(__DEBUG === true) netdata.debug('chart ' + id + ', dimension ' + x + ' updated its divisor'); + d.divisor = dim.divisor; + d._updated = true; + } + + if(typeof(dim.hidden) !== 'undefined' && d.hidden !== dim.hidden) { + if(__DEBUG === true) netdata.debug('chart ' + id + ', dimension ' + x + ' updated its hidden status'); + d.hidden = dim.hidden; + d._updated = true; + } + + if(d._updated) c._updated = true; + } + } + + //if(netdata.options.DEBUG === true) netdata.debug(netdata.charts); + return netdata.charts[id]; + }; + + return service; + }, + + runAllServices: function() { + if(netdata.options.DEBUG === true) netdata.debug('runAllServices()'); + + var now = Date.now(); + var len = netdata.services.length; + while(len--) { + var service = netdata.services[len]; + + if(service.enabled === false || service.running === true) continue; + if(now <= service.next_run) continue; + + service.update(); + + now = Date.now(); + service.next_run = now - (now % (service.update_every * 1000)) + (service.update_every * 1000); + } + + // 1/10th of update_every in pause + setTimeout(netdata.runAllServices, netdata.options.update_every * 100); + }, + + start: function() { + if(netdata.options.DEBUG === true) this.debug('started, services: ' + netdata.stringify(this.services)); + + if(this.services.length === 0) { + this.disableNodePlugin(); + + // eslint suggested way to exit + var exit = process.exit; + exit(1); + } + else this.runAllServices(); + }, + + // disable the whole node.js plugin + disableNodePlugin: function() { + this.send('DISABLE'); + + // eslint suggested way to exit + var exit = process.exit; + exit(1); + }, + + requestFromParams: function(protocol, hostname, port, path, method) { + return { + protocol: protocol, + hostname: hostname, + port: port, + path: path, + //family: 4, + method: method, + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + 'Connection': 'keep-alive' + }, + agent: new http.Agent({ + keepAlive: true, + keepAliveMsecs: netdata.options.update_every * 1000, + maxSockets: 2, // it must be 2 to work + maxFreeSockets: 1 + }) + }; + }, + + requestFromURL: function(a_url) { + var u = url.parse(a_url); + return netdata.requestFromParams(u.protocol, u.hostname, u.port, u.path, 'GET'); + }, + + configure: function(module, config, callback) { + if(netdata.options.DEBUG === true) this.debug(module.name + ': configuring (update_every: ' + this.options.update_every + ')...'); + + module.active = 0; + module.update_every = this.options.update_every; + + if(typeof config.update_every !== 'undefined') + module.update_every = config.update_every; + + module.enable_autodetect = (config.enable_autodetect)?true:false; + + if(typeof(callback) === 'function') + module.configure_callback = callback; + else + module.configure_callback = null; + + var added = module.configure(config); + + if(netdata.options.DEBUG === true) this.debug(module.name + ': configured, reporting ' + added + ' eligible services.'); + + if(module.configure_callback !== null && added === 0) { + if(netdata.options.DEBUG === true) this.debug(module.name + ': configuration finish callback called from configure().'); + var configure_callback = module.configure_callback; + module.configure_callback = null; + configure_callback(); + } + + return added; + } }; -if(netdata.options.DEBUG === true) netdata.debug('loaded netdata from: ' + __filename); +if(netdata.options.DEBUG === true) netdata.debug('loaded netdata from:', __filename); module.exports = netdata; diff --git a/node.d/sma_webbox.node.js b/node.d/sma_webbox.node.js index 3dd17a5c1..3d99943d4 100644 --- a/node.d/sma_webbox.node.js +++ b/node.d/sma_webbox.node.js @@ -3,7 +3,7 @@ // This program will connect to one or more SMA Sunny Webboxes // to get the Solar Power Generated (current, today, total). -// example configuration in /etc/netdata/sma_webbox.conf +// example configuration in /etc/netdata/node.d/sma_webbox.conf /* { "enable_autodetect": false, diff --git a/node.d/snmp.node.js b/node.d/snmp.node.js index ddc898527..5a478937e 100644 --- a/node.d/snmp.node.js +++ b/node.d/snmp.node.js @@ -2,7 +2,7 @@ // This program will connect to one or more SNMP Agents -// example configuration in /etc/netdata/snmp.conf +// example configuration in /etc/netdata/node.d/snmp.conf /* { "enable_autodetect": false, @@ -109,7 +109,7 @@ var net_snmp = require('net-snmp'); var extend = require('extend'); var netdata = require('netdata'); -if(netdata.options.DEBUG === true) netdata.debug('loaded ' + __filename + ' plugin'); +if(netdata.options.DEBUG === true) netdata.debug('loaded', __filename, ' plugin'); netdata.processors.snmp = { name: 'snmp', @@ -125,62 +125,72 @@ netdata.processors.snmp = { }, prepare: function(service) { + var __DEBUG = netdata.options.DEBUG; + if(typeof service.snmp_oids === 'undefined' || service.snmp_oids === null || service.snmp_oids.length === 0) { // this is the first time we see this service - if(netdata.options.DEBUG === true) + if(__DEBUG === true) netdata.debug(service.module.name + ': ' + service.name + ': preparing ' + this.name + ' OIDs'); // build an index of all OIDs service.snmp_oids_index = {}; - for(var c in service.request.charts) { + var chart_keys = Object.keys(service.request.charts); + var chart_keys_len = chart_keys.length; + while(chart_keys_len--) { + var c = chart_keys[chart_keys_len]; + var chart = service.request.charts[c]; + // for each chart - if(netdata.options.DEBUG === true) + if(__DEBUG === true) netdata.debug(service.module.name + ': ' + service.name + ': indexing ' + this.name + ' chart: ' + c); - if(typeof service.request.charts[c].titleoid !== 'undefined') { - service.snmp_oids_index[this.fixoid(service.request.charts[c].titleoid)] = { + if(typeof chart.titleoid !== 'undefined') { + service.snmp_oids_index[this.fixoid(chart.titleoid)] = { type: 'title', - link: service.request.charts[c] + link: chart }; } - for(var d in service.request.charts[c].dimensions) { + var dim_keys = Object.keys(chart.dimensions); + var dim_keys_len = dim_keys.length; + while(dim_keys_len--) { + var d = dim_keys[dim_keys_len]; + var dim = chart.dimensions[d]; + // for each dimension in the chart - var oid = this.fixoid(service.request.charts[c].dimensions[d].oid); - var oidname = this.fixoid(service.request.charts[c].dimensions[d].oidname); + var oid = this.fixoid(dim.oid); + var oidname = this.fixoid(dim.oidname); - if(netdata.options.DEBUG === true) + if(__DEBUG === true) netdata.debug(service.module.name + ': ' + service.name + ': indexing ' + this.name + ' chart: ' + c + ', dimension: ' + d + ', OID: ' + oid + ", OID name: " + oidname); // link it to the point we need to set the value to service.snmp_oids_index[oid] = { type: 'value', - link: service.request.charts[c].dimensions[d] + link: dim }; if(typeof oidname !== 'undefined') service.snmp_oids_index[oidname] = { type: 'name', - link: service.request.charts[c].dimensions[d] + link: dim }; // and set the value to null - service.request.charts[c].dimensions[d].value = null; + dim.value = null; } } - if(netdata.options.DEBUG === true) + if(__DEBUG === true) netdata.debug(service.module.name + ': ' + service.name + ': indexed ' + this.name + ' OIDs: ' + netdata.stringify(service.snmp_oids_index)); // now create the array of OIDs needed by net-snmp - service.snmp_oids = new Array(); - for(var o in service.snmp_oids_index) - service.snmp_oids.push(o); + service.snmp_oids = Object.keys(service.snmp_oids_index); - if(netdata.options.DEBUG === true) + if(__DEBUG === true) netdata.debug(service.module.name + ': ' + service.name + ': final list of ' + this.name + ' OIDs: ' + netdata.stringify(service.snmp_oids)); service.snmp_oids_cleaned = 0; @@ -189,14 +199,19 @@ netdata.processors.snmp = { service.snmp_oids_cleaned = 1; // the second time, keep only values + service.snmp_oids = new Array(); - for(var o in service.snmp_oids_index) - if(service.snmp_oids_index[o].type === 'value') - service.snmp_oids.push(o); + var oid_keys = Object.keys(service.snmp_oids_index); + var oid_keys_len = oid_keys.length; + while(oid_keys_len--) { + if (service.snmp_oids_index[oid_keys[oid_keys_len]].type === 'value') + service.snmp_oids.push(oid_keys[oid_keys_len]); + } } }, getdata: function(service, index, ok, failed, callback) { + var __DEBUG = netdata.options.DEBUG; var that = this; if(index >= service.snmp_oids.length) { @@ -218,7 +233,7 @@ netdata.processors.snmp = { index += service.request.max_request_size; } - if(netdata.options.DEBUG === true) + if(__DEBUG === true) netdata.debug(service.module.name + ': ' + service.name + ': making ' + slice.length + ' entries request, max is: ' + service.request.max_request_size); service.snmp_session.get(slice, function(error, varbinds) { @@ -231,14 +246,15 @@ netdata.processors.snmp = { service.snmp_oids_index[slice[len]].value = null; } else { - if(netdata.options.DEBUG === true) + if(__DEBUG === true) netdata.debug(service.module.name + ': ' + service.name + ': got valid ' + service.module.name + ' response: ' + netdata.stringify(varbinds)); - for(var i = 0; i < varbinds.length; i++) { + var varbinds_len = varbinds.length; + for(var i = 0; i < varbinds_len ; i++) { var value = null; if(net_snmp.isVarbindError(varbinds[i])) { - if(netdata.options.DEBUG === true) + if(__DEBUG === true) netdata.debug(service.module.name + ': ' + service.name + ': failed ' + service.module.name + ' get for OIDs ' + varbinds[i].oid); service.error('OID ' + varbinds[i].oid + ' gave error: ' + snmp.varbindError(varbinds[i])); @@ -246,10 +262,14 @@ netdata.processors.snmp = { failed++; } else { - if(netdata.options.DEBUG === true) + if(__DEBUG === true) netdata.debug(service.module.name + ': ' + service.name + ': found ' + service.module.name + ' value of OIDs ' + varbinds[i].oid + " = " + varbinds[i].value); - value = varbinds[i].value; + if(varbinds[i].type === net_snmp.ObjectType.OctetString) + value = parseFloat(varbinds[i].value) * 1000; + else + value = varbinds[i].value; + ok++; } @@ -262,7 +282,7 @@ netdata.processors.snmp = { } } - if(netdata.options.DEBUG === true) + if(__DEBUG === true) netdata.debug(service.module.name + ': ' + service.name + ': finished ' + service.module.name + ' with ' + ok + ' successful and ' + failed + ' failed values'); } that.getdata(service, index, ok, failed, callback); @@ -270,12 +290,14 @@ netdata.processors.snmp = { }, process: function(service, callback) { + var __DEBUG = netdata.options.DEBUG; + this.prepare(service); if(service.snmp_oids.length === 0) { // no OIDs found for this service - if(netdata.options.DEBUG === true) + if(__DEBUG === true) service.error('no OIDs to process.'); callback(null); @@ -286,13 +308,13 @@ netdata.processors.snmp = { // no SNMP session has been created for this service // the SNMP session is just the initialization of NET-SNMP - if(netdata.options.DEBUG === true) + if(__DEBUG === true) netdata.debug(service.module.name + ': ' + service.name + ': opening ' + this.name + ' session on ' + service.request.hostname + ' community ' + service.request.community + ' options ' + netdata.stringify(service.request.options)); // create the SNMP session service.snmp_session = net_snmp.createSession (service.request.hostname, service.request.community, service.request.options); - if(netdata.options.DEBUG === true) + if(__DEBUG === true) netdata.debug(service.module.name + ': ' + service.name + ': got ' + this.name + ' session: ' + netdata.stringify(service.snmp_session)); // if we later need traps, this is how to do it: @@ -319,19 +341,28 @@ var snmp = { if(service.added !== true) service.commit(); - for(var c in service.request.charts) { - var chart = snmp.charts[c]; + var chart_keys = Object.keys(service.request.charts); + var chart_keys_len = chart_keys.length; + for(var i = 0; i < chart_keys_len; i++) { + var c = chart_keys[i]; + var chart = snmp.charts[c]; if(typeof chart === 'undefined') { chart = service.chart(c, service.request.charts[c]); snmp.charts[c] = chart; } service.begin(chart); - - for( var d in service.request.charts[c].dimensions ) - if(service.request.charts[c].dimensions[d].value !== null) - service.set(d, service.request.charts[c].dimensions[d].value); + + var dimensions = service.request.charts[c].dimensions; + var dim_keys = Object.keys(dimensions); + var dim_keys_len = dim_keys.length; + for(var j = 0; j < dim_keys_len ; j++) { + var d = dim_keys[j]; + + if (dimensions[d].value !== null) + service.set(d, dimensions[d].value); + } service.end(); } @@ -343,7 +374,9 @@ var snmp = { // its purpose is to prepare the request and call // netdata.serviceExecute() serviceExecute: function(conf) { - if(netdata.options.DEBUG === true) + var __DEBUG = netdata.options.DEBUG; + + if(__DEBUG === true) netdata.debug(this.name + ': snmp hostname: ' + conf.hostname + ', update_every: ' + conf.update_every); var service = netdata.service({ @@ -355,30 +388,40 @@ var snmp = { }); // multiply the charts, if required - for(var c in service.request.charts) { - if(netdata.options.DEBUG === true) + var chart_keys = Object.keys(service.request.charts); + var chart_keys_len = chart_keys.length; + for( var i = 0; i < chart_keys_len ; i++ ) { + var c = chart_keys[i]; + var service_request_chart = service.request.charts[c]; + + if(__DEBUG === true) netdata.debug(this.name + ': snmp hostname: ' + conf.hostname + ', examining chart: ' + c); - if(typeof service.request.charts[c].update_every === 'undefined') - service.request.charts[c].update_every = service.update_every; + if(typeof service_request_chart.update_every === 'undefined') + service_request_chart.update_every = service.update_every; - if(typeof service.request.charts[c].multiply_range !== 'undefined') { - var from = service.request.charts[c].multiply_range[0]; - var to = service.request.charts[c].multiply_range[1]; - var prio = service.request.charts[c].priority || 1; + if(typeof service_request_chart.multiply_range !== 'undefined') { + var from = service_request_chart.multiply_range[0]; + var to = service_request_chart.multiply_range[1]; + var prio = service_request_chart.priority || 1; if(prio < snmp.base_priority) prio += snmp.base_priority; while(from <= to) { var id = c + from.toString(); - var chart = extend(true, {}, service.request.charts[c]); + var chart = extend(true, {}, service_request_chart); chart.title += from.toString(); if(typeof chart.titleoid !== 'undefined') chart.titleoid += from.toString(); chart.priority = prio++; - for(var d in chart.dimensions) { + + var dim_keys = Object.keys(chart.dimensions); + var dim_keys_len = dim_keys.length; + for(var j = 0; j < dim_keys_len ; j++) { + var d = dim_keys[j]; + chart.dimensions[d].oid += from.toString(); if(typeof chart.dimensions[d].oidname !== 'undefined') @@ -430,7 +473,7 @@ var snmp = { service.module.processResponse(serv, data); callback(); }); - }, + } }; module.exports = snmp; diff --git a/plugins.d/Makefile.am b/plugins.d/Makefile.am index 4bc0dc447..7d3bc44f7 100644 --- a/plugins.d/Makefile.am +++ b/plugins.d/Makefile.am @@ -10,9 +10,11 @@ dist_plugins_DATA = \ dist_plugins_SCRIPTS = \ alarm-email.sh \ alarm-notify.sh \ + alarm-test.sh \ cgroup-name.sh \ charts.d.dryrun-helper.sh \ charts.d.plugin \ + fping.plugin \ node.d.plugin \ python.d.plugin \ tc-qos-helper.sh \ diff --git a/plugins.d/Makefile.in b/plugins.d/Makefile.in index 1854ea861..7e90c9808 100644 --- a/plugins.d/Makefile.in +++ b/plugins.d/Makefile.in @@ -1,7 +1,7 @@ -# Makefile.in generated by automake 1.14.1 from Makefile.am. +# Makefile.in generated by automake 1.15 from Makefile.am. # @configure_input@ -# Copyright (C) 1994-2013 Free Software Foundation, Inc. +# Copyright (C) 1994-2014 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -16,7 +16,17 @@ VPATH = @srcdir@ -am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ @@ -80,18 +90,19 @@ POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = plugins.d -DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ - $(dist_plugins_SCRIPTS) $(dist_plugins_DATA) ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_c___atomic.m4 \ $(top_srcdir)/m4/ax_c__generic.m4 \ $(top_srcdir)/m4/ax_c_mallinfo.m4 \ $(top_srcdir)/m4/ax_c_mallopt.m4 \ $(top_srcdir)/m4/ax_check_compile_flag.m4 \ + $(top_srcdir)/m4/ax_gcc_func_attribute.m4 \ $(top_srcdir)/m4/ax_pthread.m4 $(top_srcdir)/m4/jemalloc.m4 \ $(top_srcdir)/m4/tcmalloc.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(dist_plugins_SCRIPTS) \ + $(dist_plugins_DATA) $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = @@ -146,6 +157,7 @@ am__can_run_installinfo = \ esac DATA = $(dist_plugins_DATA) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +am__DIST_COMMON = $(srcdir)/Makefile.in DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ @@ -295,9 +307,11 @@ dist_plugins_DATA = \ dist_plugins_SCRIPTS = \ alarm-email.sh \ alarm-notify.sh \ + alarm-test.sh \ cgroup-name.sh \ charts.d.dryrun-helper.sh \ charts.d.plugin \ + fping.plugin \ node.d.plugin \ python.d.plugin \ tc-qos-helper.sh \ @@ -319,7 +333,6 @@ $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__confi echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu plugins.d/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu plugins.d/Makefile -.PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ @@ -549,6 +562,8 @@ uninstall-am: uninstall-dist_pluginsDATA uninstall-dist_pluginsSCRIPTS pdf-am ps ps-am tags-am uninstall uninstall-am \ uninstall-dist_pluginsDATA uninstall-dist_pluginsSCRIPTS +.PRECIOUS: Makefile + # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. diff --git a/plugins.d/alarm-notify.sh b/plugins.d/alarm-notify.sh index feec6ceae..d6f3d8b2a 100755 --- a/plugins.d/alarm-notify.sh +++ b/plugins.d/alarm-notify.sh @@ -5,7 +5,7 @@ # (C) 2016 Costa Tsaousis <costa@tsaousis.gr> # GPL v3+ # -# Script the send alarm notifications for netdata +# Script to send alarm notifications for netdata # # Features: # - multiple notification methods @@ -14,20 +14,103 @@ # - severity filtering per recipient # # Supported notification methods: -# - emails -# - pushover.net notifications -# - slack.com notifications -# - telegram.org notifications -# +# - emails by @ktsaou +# - slack.com notifications by @ktsaou +# - pushover.net notifications by @ktsaou +# - pushbullet.com push notifications by Tiago Peralta @tperalta82 PR #1070 +# - telegram.org notifications by @hashworks PR #1002 +# - twilio.com notifications by Levi Blaney @shadycuz PR #1211 +# - kafka notifications by @ktsaou #1342 +# - pagerduty.com notifications by Jim Cooley @jimcooley PR #1373 +# - messagebird.com notifications by @tech_no_logical #1453 +# - hipchart notifications by @ktsaou #1561 + +# ----------------------------------------------------------------------------- +# testing notifications + +if [ \( "${1}" = "test" -o "${2}" = "test" \) -a "${#}" -le 2 ] +then + if [ "${2}" = "test" ] + then + recipient="${1}" + else + recipient="${2}" + fi + + [ -z "${recipient}" ] && recipient="sysadmin" + + id=1 + last="CLEAR" + for x in "CRITICAL" "WARNING" "CLEAR" + do + echo >&2 + echo >&2 "# SENDING TEST ${x} ALARM TO ROLE: ${recipient}" + + "${0}" "${recipient}" "$(hostname)" 1 1 "${id}" "$(date +%s)" "test_alarm" "test.chart" "test.family" "${x}" "${last}" 100 90 "${0}" 1 $((0 + id)) "units" "this is a test alarm to verify notifications work" + if [ $? -ne 0 ] + then + echo >&2 "# FAILED" + else + echo >&2 "# OK" + fi + + last="${x}" + id=$((id + 1)) + done + + exit 1 +fi + +export PATH="${PATH}:/sbin:/usr/sbin:/usr/local/sbin" +export LC_ALL=C + +# ----------------------------------------------------------------------------- + +PROGRAM_NAME="$(basename "${0}")" + +logdate() { + date "+%Y-%m-%d %H:%M:%S" +} + +log() { + local status="${1}" + shift + + echo >&2 "$(logdate): ${PROGRAM_NAME}: ${status}: ${*}" -me="${0}" +} + +warning() { + log WARNING "${@}" +} + +error() { + log ERROR "${@}" +} + +info() { + log INFO "${@}" +} + +fatal() { + log FATAL "${@}" + exit 1 +} + +debug=0 +debug() { + [ ${debug} -eq 1 ] && log DEBUG "${@}" +} + +# ----------------------------------------------------------------------------- # check for BASH v4+ (required for associative arrays) [ $(( ${BASH_VERSINFO[0]} )) -lt 4 ] && \ - echo >&2 "${me}: BASH version 4 or later is required (this is ${BASH_VERSION})." && \ - exit 1 + fatal "BASH version 4 or later is required (this is ${BASH_VERSION})." +# ----------------------------------------------------------------------------- # defaults to allow running this script by hand + NETDATA_CONFIG_DIR="${NETDATA_CONFIG_DIR-/etc/netdata}" NETDATA_CACHE_DIR="${NETDATA_CACHE_DIR-/var/cache/netdata}" [ -z "${NETDATA_REGISTRY_URL}" ] && NETDATA_REGISTRY_URL="https://registry.my-netdata.io" @@ -62,14 +145,14 @@ info="${18}" # a short description of the alarm # don't do anything if this is not WARNING, CRITICAL or CLEAR if [ "${status}" != "WARNING" -a "${status}" != "CRITICAL" -a "${status}" != "CLEAR" ] then - echo >&2 "${me}: not sending notification for ${status} on '${chart}.${name}'" + info "not sending notification for ${status} on '${chart}.${name}'" exit 1 fi # don't do anything if this is CLEAR, but it was not WARNING or CRITICAL if [ "${old_status}" != "WARNING" -a "${old_status}" != "CRITICAL" -a "${status}" = "CLEAR" ] then - echo >&2 "${me}: not sending notification for ${status} on '${chart}.${name}' (last status was ${old_status})" + info "not sending notification for ${status} on '${chart}.${name}' (last status was ${old_status})" exit 1 fi @@ -90,8 +173,14 @@ sendmail= # enable / disable features SEND_SLACK="YES" SEND_PUSHOVER="YES" +SEND_TWILIO="YES" +SEND_HIPCHAT="YES" +SEND_MESSAGEBIRD="YES" SEND_TELEGRAM="YES" SEND_EMAIL="YES" +SEND_PUSHBULLET="YES" +SEND_KAFKA="YES" +SEND_PD="YES" # slack configs SLACK_WEBHOOK_URL= @@ -103,11 +192,42 @@ PUSHOVER_APP_TOKEN= DEFAULT_RECIPIENT_PUSHOVER= declare -A role_recipients_pushover=() +# pushbullet configs +PUSHBULLET_ACCESS_TOKEN= +DEFAULT_RECIPIENT_PUSHBULLET= +declare -A role_recipients_pushbullet=() + +# twilio configs +TWILIO_ACCOUNT_SID= +TWILIO_ACCOUNT_TOKEN= +TWILIO_NUMBER= +DEFAULT_RECIPIENT_TWILIO= +declare -A role_recipients_twilio=() + +# hipchat configs +HIPCHAT_AUTH_TOKEN= +DEFAULT_RECIPIENT_HIPCHAT= +declare -A role_recipients_hipchat=() + +# messagebird configs +MESSAGEBIRD_ACCESS_KEY= +MESSAGEBIRD_NUMBER= +DEFAULT_RECIPIENT_MESSAGEBIRD= +declare -A role_recipients_messagebird=() + # telegram configs TELEGRAM_BOT_TOKEN= DEFAULT_RECIPIENT_TELEGRAM= declare -A role_recipients_telegram=() +# kafka configs +KAFKA_URL= +KAFKA_SENDER_IP= + +# pagerduty.com configs +PD_SERVICE_KEY= +declare -A role_recipients_pd=() + # email configs DEFAULT_RECIPIENT_EMAIL="root" declare -A role_recipients_email=() @@ -164,7 +284,11 @@ filter_recipient_by_criticality() { declare -A arr_slack=() declare -A arr_pushover=() +declare -A arr_pushbullet=() +declare -A arr_twilio=() +declare -A arr_hipchat=() declare -A arr_telegram=() +declare -A arr_pd=() declare -A arr_email=() # netdata may call us with multiple roles, and roles may have multiple but @@ -191,6 +315,38 @@ do [ "${r}" != "disabled" ] && filter_recipient_by_criticality pushover "${r}" && arr_pushover[${r/|*/}]="1" done + # pushbullet + a="${role_recipients_pushbullet[${x}]}" + [ -z "${a}" ] && a="${DEFAULT_RECIPIENT_PUSHBULLET}" + for r in ${a//,/ } + do + [ "${r}" != "disabled" ] && filter_recipient_by_criticality pushbullet "${r}" && arr_pushbullet[${r/|*/}]="1" + done + + # twilio + a="${role_recipients_twilio[${x}]}" + [ -z "${a}" ] && a="${DEFAULT_RECIPIENT_TWILIO}" + for r in ${a//,/ } + do + [ "${r}" != "disabled" ] && filter_recipient_by_criticality twilio "${r}" && arr_twilio[${r/|*/}]="1" + done + + # hipchat + a="${role_recipients_hipchat[${x}]}" + [ -z "${a}" ] && a="${DEFAULT_RECIPIENT_HIPCHAT}" + for r in ${a//,/ } + do + [ "${r}" != "disabled" ] && filter_recipient_by_criticality hipchat "${r}" && arr_hipchat[${r/|*/}]="1" + done + + # messagebird + a="${role_recipients_messagebird[${x}]}" + [ -z "${a}" ] && a="${DEFAULT_RECIPIENT_MESSAGEBIRD}" + for r in ${a//,/ } + do + [ "${r}" != "disabled" ] && filter_recipient_by_criticality messagebird "${r}" && arr_messagebird[${r/|*/}]="1" + done + # telegram a="${role_recipients_telegram[${x}]}" [ -z "${a}" ] && a="${DEFAULT_RECIPIENT_TELEGRAM}" @@ -206,6 +362,14 @@ do do [ "${r}" != "disabled" ] && filter_recipient_by_criticality slack "${r}" && arr_slack[${r/|*/}]="1" done + + # pagerduty.com + a="${role_recipients_pd[${x}]}" + [ -z "${a}" ] && a="${DEFAULT_RECIPIENT_PD}" + for r in ${a//,/ } + do + [ "${r}" != "disabled" ] && filter_recipient_by_criticality pd "${r}" && arr_pd[${r/|*/}]="1" + done done # build the list of slack recipients (channels) @@ -216,10 +380,30 @@ to_slack="${!arr_slack[*]}" to_pushover="${!arr_pushover[*]}" [ -z "${to_pushover}" ] && SEND_PUSHOVER="NO" +# build the list of pushbulet recipients (user tokens) +to_pushbullet="${!arr_pushbullet[*]}" +[ -z "${to_pushbullet}" ] && SEND_PUSHBULLET="NO" + +# build the list of twilio recipients (phone numbers) +to_twilio="${!arr_twilio[*]}" +[ -z "${to_twilio}" ] && SEND_TWILIO="NO" + +# build the list of hipchat recipients (rooms) +to_hipchat="${!arr_hipchat[*]}" +[ -z "${to_hipchat}" ] && SEND_HIPCHAT="NO" + +# build the list of messagebird recipients (phone numbers) +to_messagebird="${!arr_messagebird[*]}" +[ -z "${to_messagebird}" ] && SEND_MESSAGEBIRD="NO" + # check array of telegram recipients (chat ids) to_telegram="${!arr_telegram[*]}" [ -z "${to_telegram}" ] && SEND_TELEGRAM="NO" +# build the list of pagerduty recipients (service keys) +to_pd="${!arr_pd[*]}" +[ -z "${to_pd}" ] && SEND_PD="NO" + # build the list of email recipients (email addresses) to_email= for x in "${!arr_email[@]}" @@ -239,20 +423,67 @@ done # check pushover [ -z "${PUSHOVER_APP_TOKEN}" ] && SEND_PUSHOVER="NO" +# check pushbullet +[ -z "${PUSHBULLET_ACCESS_TOKEN}" ] && SEND_PUSHBULLET="NO" + +# check twilio +[ -z "${TWILIO_ACCOUNT_TOKEN}" -o -z "${TWILIO_ACCOUNT_SID}" -o -z "${TWILIO_NUMBER}" ] && SEND_TWILIO="NO" + +# check hipchat +[ -z "${HIPCHAT_AUTH_TOKEN}" ] && SEND_HIPCHAT="NO" + +# check messagebird +[ -z "${MESSAGEBIRD_ACCESS_KEY}" -o -z "${MESSAGEBIRD_NUMBER}" ] && SEND_MESSAGEBIRD="NO" + # check telegram [ -z "${TELEGRAM_BOT_TOKEN}" ] && SEND_TELEGRAM="NO" -if [ \( "${SEND_PUSHOVER}" = "YES" -o "${SEND_SLACK}" = "YES" -o "${SEND_TELEGRAM}" = "YES" \) -a -z "${curl}" ] +# check kafka +[ -z "${KAFKA_URL}" -o -z "${KAFKA_SENDER_IP}" ] && SEND_KAFKA="NO" + +# check pagerduty.com +# if we need pd-send, check for the pd-send command +# https://www.pagerduty.com/docs/guides/agent-install-guide/ +if [ "${SEND_PD}" = "YES" ] + then + pd_send="$(which pd-send 2>/dev/null || command -v pd-send 2>/dev/null)" + if [ -z "${pd_send}" ] + then + # no pd-send available + # disable pagerduty.com + SEND_PD="NO" + fi +fi + +# if we need curl, check for the curl command +if [ \( \ + "${SEND_PUSHOVER}" = "YES" \ + -o "${SEND_SLACK}" = "YES" \ + -o "${SEND_HIPCHAT}" = "YES" \ + -o "${SEND_TWILIO}" = "YES" \ + -o "${SEND_MESSAGEBIRD}" = "YES" \ + -o "${SEND_TELEGRAM}" = "YES" \ + -o "${SEND_PUSHBULLET}" = "YES" \ + -o "${SEND_KAFKA}" = "YES" \ + \) -a -z "${curl}" ] then curl="$(which curl 2>/dev/null || command -v curl 2>/dev/null)" if [ -z "${curl}" ] then + # no curl available + # disable all curl based methods SEND_PUSHOVER="NO" + SEND_PUSHBULLET="NO" SEND_TELEGRAM="NO" SEND_SLACK="NO" + SEND_TWILIO="NO" + SEND_HIPCHAT="NO" + SEND_MESSAGEBIRD="NO" + SEND_KAFKA="NO" fi fi +# if we need sendmail, check for the sendmail command if [ "${SEND_EMAIL}" = "YES" -a -z "${sendmail}" ] then sendmail="$(which sendmail 2>/dev/null || command -v sendmail 2>/dev/null)" @@ -260,14 +491,23 @@ if [ "${SEND_EMAIL}" = "YES" -a -z "${sendmail}" ] fi # check that we have at least a method enabled -if [ "${SEND_EMAIL}" != "YES" -a "${SEND_PUSHOVER}" != "YES" -a "${SEND_TELEGRAM}" != "YES" -a "${SEND_SLACK}" != "YES" ] +if [ "${SEND_EMAIL}" != "YES" \ + -a "${SEND_PUSHOVER}" != "YES" \ + -a "${SEND_TELEGRAM}" != "YES" \ + -a "${SEND_SLACK}" != "YES" \ + -a "${SEND_TWILIO}" != "YES" \ + -a "${SEND_HIPCHAT}" != "YES" \ + -a "${SEND_MESSAGEBIRD}" != "YES" \ + -a "${SEND_PUSHBULLET}" != "YES" \ + -a "${SEND_KAFKA}" != "YES" \ + -a "${SEND_PD}" != "YES" \ + ] then - echo >&2 "All notification methods are disabled. Not sending a notification." - exit 1 + fatal "All notification methods are disabled. Not sending notification to '${roles}' for '${name}' = '${value}' of chart '${chart}' for status '${status}'." fi # ----------------------------------------------------------------------------- -# get the system hostname +# find a suitable hostname to use, if netdata did not supply a hostname [ -z "${host}" ] && host="${NETDATA_HOSTNAME}" [ -z "${host}" ] && host="${NETDATA_REGISTRY_HOSTNAME}" @@ -280,7 +520,7 @@ date="$(date --date=@${when} 2>/dev/null)" [ -z "${date}" ] && date="$(date 2>/dev/null)" # ----------------------------------------------------------------------------- -# URL encode a string +# function to URL encode a string urlencode() { local string="${1}" strlen encoded pos c o @@ -288,14 +528,14 @@ urlencode() { strlen=${#string} for (( pos=0 ; pos<strlen ; pos++ )) do - c=${string:$pos:1} - case "$c" in + c=${string:${pos}:1} + case "${c}" in [-_.~a-zA-Z0-9]) o="${c}" ;; *) - printf -v o '%%%02x' "'$c" + printf -v o '%%%02x' "'${c}" ;; esac encoded+="${o}" @@ -306,7 +546,7 @@ urlencode() { } # ----------------------------------------------------------------------------- -# convert a duration in seconds, to a human readable duration +# function to convert a duration in seconds, to a human readable duration # using DAYS, MINUTES, SECONDS duration4human() { @@ -370,12 +610,12 @@ send_email() { "${sendmail}" -t ret=$? - if [ $ret -eq 0 ] + if [ ${ret} -eq 0 ] then - echo >&2 "${me}: Sent email notification for: ${host} ${chart}.${name} is ${status} to '${to_email}'" + info "sent email notification for: ${host} ${chart}.${name} is ${status} to '${to_email}'" return 0 else - echo >&2 "${me}: Failed to send email notification for: ${host} ${chart}.${name} is ${status} to '${to_email}' with error code ${ret}." + error "failed to send email notification for: ${host} ${chart}.${name} is ${status} to '${to_email}' with error code ${ret}." return 1 fi fi @@ -417,10 +657,10 @@ send_pushover() { if [ "${httpcode}" == "200" ] then - echo >&2 "${me}: Sent pushover notification for: ${host} ${chart}.${name} is ${status} to '${user}'" + info "sent pushover notification for: ${host} ${chart}.${name} is ${status} to '${user}'" sent=$((sent + 1)) else - echo >&2 "${me}: Failed to send pushover notification for: ${host} ${chart}.${name} is ${status} to '${user}' with HTTP error code ${httpcode}." + error "failed to send pushover notification for: ${host} ${chart}.${name} is ${status} to '${user}' with HTTP error code ${httpcode}." fi done @@ -430,6 +670,242 @@ send_pushover() { return 1 } +# ----------------------------------------------------------------------------- +# pushbullet sender + +send_pushbullet() { + local userapikey="${1}" recipients="${2}" title="${3}" message="${4}" httpcode sent=0 user + if [ "${SEND_PUSHBULLET}" = "YES" -a ! -z "${userapikey}" -a ! -z "${recipients}" -a ! -z "${message}" -a ! -z "${title}" ] + then + #https://docs.pushbullet.com/#create-push + for user in ${recipients} + do + httpcode=$(${curl} --write-out %{http_code} --silent --output /dev/null \ + --header 'Access-Token: '${userapikey}'' \ + --header 'Content-Type: application/json' \ + --data-binary @<(cat <<EOF + {"title": "${title}", + "type": "note", + "email": "${user}", + "body": "$( echo -n ${message})"} +EOF + ) "https://api.pushbullet.com/v2/pushes" -X POST) + + if [ "${httpcode}" == "200" ] + then + info "sent pushbullet notification for: ${host} ${chart}.${name} is ${status} to '${user}'" + sent=$((sent + 1)) + else + error "failed to send pushbullet notification for: ${host} ${chart}.${name} is ${status} to '${user}' with HTTP error code ${httpcode}." + fi + done + + [ ${sent} -gt 0 ] && return 0 + fi + + return 1 +} + +# ----------------------------------------------------------------------------- +# kafka sender + +send_kafka() { + local httpcode sent=0 + if [ "${SEND_KAFKA}" = "YES" ] + then + httpcode=$(${curl} -X POST --write-out %{http_code} --silent --output /dev/null \ + --data "{host_ip:\"${KAFKA_SENDER_IP}\",when:${when},name:\"${name}\",chart:\"${chart}\",family:\"${family}\",status:\"${status}\",old_status:\"${old_status}\",value:${value},old_value:${old_value},duration:${duration},non_clear_duration:${non_clear_duration},units:\"${units}\",info:\"${info}\"}" \ + "${KAFKA_URL}") + + if [ "${httpcode}" == "204" ] + then + info "sent kafka data for: ${host} ${chart}.${name} is ${status} and ip '${KAFKA_SENDER_IP}'" + sent=$((sent + 1)) + else + error "failed to send kafka data for: ${host} ${chart}.${name} is ${status} and ip '${KAFKA_SENDER_IP}' with HTTP error code ${httpcode}." + fi + + [ ${sent} -gt 0 ] && return 0 + fi + + return 1 +} + +# ----------------------------------------------------------------------------- +# pagerduty.com sender + +send_pd() { + local recipients="${1}" sent=0 + unset t + case ${status} in + CLEAR) t='resolve';; + WARNING) t='trigger';; + CRITICAL) t='trigger';; + esac + + if [ ${SEND_PD} = "YES" -a ! -z "${t}" ] + then + for PD_SERVICE_KEY in ${recipients} + do + d="${status} ${name}=${value} ${units} - ${host}, ${family}" + ${pd_send} -k ${PD_SERVICE_KEY} \ + -t ${t} \ + -d "${d}" \ + -i ${alarm_id} \ + -f 'info'="${info}" \ + -f 'value_w_units'="${value} ${units}" \ + -f 'when'="${when}" \ + -f 'duration'="${duration}" \ + -f 'roles'="${roles}" \ + -f 'host'="${host}" \ + -f 'unique_id'="${unique_id}" \ + -f 'alarm_id'="${alarm_id}" \ + -f 'event_id'="${event_id}" \ + -f 'name'="${name}" \ + -f 'chart'="${chart}" \ + -f 'family'="${family}" \ + -f 'status'="${status}" \ + -f 'old_status'="${old_status}" \ + -f 'value'="${value}" \ + -f 'old_value'="${old_value}" \ + -f 'src'="${src}" \ + -f 'non_clear_duration'="${non_clear_duration}" \ + -f 'units'="${units}" + retval=$? + if [ ${retval} -eq 0 ] + then + info "sent pagerduty.com notification using service key ${PD_SERVICE_KEY::-26}....: ${d}" + sent=$((sent + 1)) + else + error "failed to send pagerduty.com notification using service key ${PD_SERVICE_KEY::-26}.... (error code ${retval}): ${d}" + fi + done + + [ ${sent} -gt 0 ] && return 0 + fi + + return 1 +} + +# ----------------------------------------------------------------------------- +# twilio sender + +send_twilio() { + local accountsid="${1}" accounttoken="${2}" twilionumber="${3}" recipients="${4}" title="${5}" message="${6}" httpcode sent=0 user + if [ "${SEND_TWILIO}" = "YES" -a ! -z "${accountsid}" -a ! -z "${accounttoken}" -a ! -z "${twilionumber}" -a ! -z "${recipients}" -a ! -z "${message}" -a ! -z "${title}" ] + then + #https://www.twilio.com/packages/labs/code/bash/twilio-sms + for user in ${recipients} + do + httpcode=$(${curl} -X POST --write-out %{http_code} --silent --output /dev/null \ + --data-urlencode "From=${twilionumber}" \ + --data-urlencode "To=${user}" \ + --data-urlencode "Body=${title} ${message}" \ + -u "${accountsid}:${accounttoken}" \ + "https://api.twilio.com/2010-04-01/Accounts/${accountsid}/Messages.json") + + if [ "${httpcode}" == "201" ] + then + info "sent Twilio SMS for: ${host} ${chart}.${name} is ${status} to '${user}'" + sent=$((sent + 1)) + else + error "failed to send Twilio SMS for: ${host} ${chart}.${name} is ${status} to '${user}' with HTTP error code ${httpcode}." + fi + done + + [ ${sent} -gt 0 ] && return 0 + fi + + return 1 +} + + +# ----------------------------------------------------------------------------- +# hipchat sender + +send_hipchat() { + local authtoken="${1}" recipients="${2}" message="${3}" httpcode sent=0 room color sender msg_format notify + + if [ "${SEND_HIPCHAT}" = "YES" -a ! -z "${authtoken}" -a ! -z "${recipients}" -a ! -z "${message}" ] + then + + # A label to be shown in addition to the sender's name + # Valid length range: 0 - 64. + sender="netdata" + + # Valid values: html, text. + # Defaults to 'html'. + msg_format="text" + + # Background color for message. Valid values: yellow, green, red, purple, gray, random. Defaults to 'yellow'. + case "${status}" in + WARNING) color="yellow" ;; + CRITICAL) color="red" ;; + CLEAR) color="green" ;; + *) color="gray" ;; + esac + + # Whether this message should trigger a user notification (change the tab color, play a sound, notify mobile phones, etc). + # Each recipient's notification preferences are taken into account. + # Defaults to false. + notify="true" + + for room in ${recipients} + do + httpcode=$(${curl} -X POST --write-out %{http_code} --silent --output /dev/null \ + -H "Content-type: application/json" \ + -H "Authorization: Bearer ${authtoken}" \ + -d "{\"color\": \"${color}\", \"from\": \"${netdata}\", \"message_format\": \"${msg_format}\", \"message\": \"${message}\", \"notify\": \"${notify}\"}" \ + "https://api.hipchat.com/v2/room/${room}/notification") + + if [ "${httpcode}" == "200" ] + then + info "sent HipChat notification for: ${host} ${chart}.${name} is ${status} to '${room}'" + sent=$((sent + 1)) + else + error "failed to send HipChat notification for: ${host} ${chart}.${name} is ${status} to '${room}' with HTTP error code ${httpcode}." + fi + done + + [ ${sent} -gt 0 ] && return 0 + fi + + return 1 +} + + +# ----------------------------------------------------------------------------- +# messagebird sender + +send_messagebird() { + local accesskey="${1}" messagebirdnumber="${2}" recipients="${3}" title="${4}" message="${5}" httpcode sent=0 user + if [ "${SEND_MESSAGEBIRD}" = "YES" -a ! -z "${accesskey}" -a ! -z "${messagebirdnumber}" -a ! -z "${recipients}" -a ! -z "${message}" -a ! -z "${title}" ] + then + #https://developers.messagebird.com/docs/messaging + for user in ${recipients} + do + httpcode=$(${curl} -X POST --write-out %{http_code} --silent --output /dev/null \ + --data-urlencode "originator=${messagebirdnumber}" \ + --data-urlencode "recipients=${user}" \ + --data-urlencode "body=${title} ${message}" \ + --data-urlencode "datacoding=auto" \ + -H "Authorization: AccessKey ${accesskey}" \ + "https://rest.messagebird.com/messages") + + if [ "${httpcode}" == "201" ] + then + info "sent Messagebird SMS for: ${host} ${chart}.${name} is ${status} to '${user}'" + sent=$((sent + 1)) + else + error "failed to send Messagebird SMS for: ${host} ${chart}.${name} is ${status} to '${user}' with HTTP error code ${httpcode}." + fi + done + + [ ${sent} -gt 0 ] && return 0 + fi + + return 1 +} # ----------------------------------------------------------------------------- # telegram sender @@ -447,18 +923,18 @@ send_telegram() { httpcode=$(${curl} --write-out %{http_code} --silent --output /dev/null ${disableNotification} \ --data-urlencode "parse_mode=HTML" \ --data-urlencode "disable_web_page_preview=true" \ - --data-urlencode "text=$message" \ - "https://api.telegram.org/bot${bottoken}/sendMessage?chat_id=$chatid") + --data-urlencode "text=${message}" \ + "https://api.telegram.org/bot${bottoken}/sendMessage?chat_id=${chatid}") if [ "${httpcode}" == "200" ] then - echo >&2 "${me}: Sent telegram notification for: ${host} ${chart}.${name} is ${status} to '${chatid}'" + info "sent telegram notification for: ${host} ${chart}.${name} is ${status} to '${chatid}'" sent=$((sent + 1)) elif [ "${httpcode}" == "401" ] then - echo >&2 "${me}: Failed to send telegram notification for: ${host} ${chart}.${name} is ${status} to '${chatid}': Wrong bot token." + error "failed to send telegram notification for: ${host} ${chart}.${name} is ${status} to '${chatid}': Wrong bot token." else - echo >&2 "${me}: Failed to send telegram notification for: ${host} ${chart}.${name} is ${status} to '${chatid}' with HTTP error code ${httpcode}." + error "failed to send telegram notification for: ${host} ${chart}.${name} is ${status} to '${chatid}' with HTTP error code ${httpcode}." fi done @@ -477,10 +953,10 @@ send_slack() { [ "${SEND_SLACK}" != "YES" ] && return 1 case "${status}" in - WARNING) color="warning" ;; + WARNING) color="warning" ;; CRITICAL) color="danger" ;; - CLEAR) color="good" ;; - *) color="#777777" ;; + CLEAR) color="good" ;; + *) color="#777777" ;; esac for channel in ${channels} @@ -520,10 +996,10 @@ EOF httpcode=$(${curl} --write-out %{http_code} --silent --output /dev/null -X POST --data-urlencode "payload=${payload}" "${webhook}") if [ "${httpcode}" == "200" ] then - echo >&2 "${me}: Sent slack notification for: ${host} ${chart}.${name} is ${status} to '${channel}'" + info "sent slack notification for: ${host} ${chart}.${name} is ${status} to '${channel}'" sent=$((sent + 1)) else - echo >&2 "${me}: Failed to send slack notification for: ${host} ${chart}.${name} is ${status} to '${channel}', with HTTP error code ${httpcode}." + error "failed to send slack notification for: ${host} ${chart}.${name} is ${status} to '${channel}', with HTTP error code ${httpcode}." fi done @@ -581,25 +1057,25 @@ case "${status}" in image="${images_base_url}/images/check-mark-2-128-green.png" status_message="recovered" color="#77ca6d" - - # don't show the value when the status is CLEAR - # for certain alarms, this value might not have any meaning - alarm="${name//_/ } ${raised_for}" ;; esac if [ "${status}" = "CLEAR" ] then severity="Recovered from ${old_status}" - if [ $non_clear_duration -gt $duration ] + if [ ${non_clear_duration} -gt ${duration} ] then raised_for="(alarm was raised for ${non_clear_duration_txt})" fi + # don't show the value when the status is CLEAR + # for certain alarms, this value might not have any meaning + alarm="${name//_/ } ${raised_for}" + elif [ "${old_status}" = "WARNING" -a "${status}" = "CRITICAL" ] then severity="Escalated to ${status}" - if [ $non_clear_duration -gt $duration ] + if [ ${non_clear_duration} -gt ${duration} ] then raised_for="(alarm is raised for ${non_clear_duration_txt})" fi @@ -607,7 +1083,7 @@ then elif [ "${old_status}" = "CRITICAL" -a "${status}" = "WARNING" ] then severity="Demoted to ${status}" - if [ $non_clear_duration -gt $duration ] + if [ ${non_clear_duration} -gt ${duration} ] then raised_for="(alarm is raised for ${non_clear_duration_txt})" fi @@ -648,20 +1124,81 @@ send_pushover "${PUSHOVER_APP_TOKEN}" "${to_pushover}" "${when}" "${goto_url}" " SENT_PUSHOVER=$? # ----------------------------------------------------------------------------- +# send the pushbullet notification + +send_pushbullet "${PUSHBULLET_ACCESS_TOKEN}" "${to_pushbullet}" "${host} ${status_message} - ${name//_/ } - ${chart}" "${alarm}\n +Severity: ${severity}\n +Chart: ${chart}\n +Family: ${family}\n +To View Netdata go to: ${goto_url}\n +The source of this alarm is line ${src}" + +SENT_PUSHBULLET=$? + +# ----------------------------------------------------------------------------- +# send the twilio SMS + +send_twilio "${TWILIO_ACCOUNT_SID}" "${TWILIO_ACCOUNT_TOKEN}" "${TWILIO_NUMBER}" "${to_twilio}" "${host} ${status_message} - ${name//_/ } - ${chart}" "${alarm} +Severity: ${severity} +Chart: ${chart} +Family: ${family} +${info}" + +SENT_TWILIO=$? + +# ----------------------------------------------------------------------------- +# send the messagebird SMS + +send_messagebird "${MESSAGEBIRD_ACCESS_KEY}" "${MESSAGEBIRD_NUMBER}" "${to_messagebird}" "${host} ${status_message} - ${name//_/ } - ${chart}" "${alarm} +Severity: ${severity} +Chart: ${chart} +Family: ${family} +${info}" + +SENT_MESSAGEBIRD=$? + + +# ----------------------------------------------------------------------------- # send the telegram.org message # https://core.telegram.org/bots/api#formatting-options -telegram_message="<b>${severity}" -[ "${status_message}" != "recovered" ] && telegram_message="${telegram_message}, ${status_message}" -telegram_message="${telegram_message} -${chart} (${family})</b> +send_telegram "${TELEGRAM_BOT_TOKEN}" "${to_telegram}" "${host} ${status_message} - <b>${name//_/ }</b> +${chart} (${family}) <a href=\"${goto_url}\">${alarm}</a> <i>${info}</i>" -send_telegram "${TELEGRAM_BOT_TOKEN}" "${to_telegram}" "${telegram_message}" - SENT_TELEGRAM=$? + +# ----------------------------------------------------------------------------- +# send the kafka message + +send_kafka +SENT_KAFKA=$? + + +# ----------------------------------------------------------------------------- +# send the pagerduty.com message + +send_pd "${to_pd}" +SENT_PD=$? + + +# ----------------------------------------------------------------------------- +# send hipchat message + +send_hipchat "${HIPCHAT_AUTH_TOKEN}" "${to_hipchat}" " +<b>${alarm}</b> ${info_html}<br/> +<small><b>${chart}</b><br/>Chart<br/> </small> +<small><b>${family}</b><br/>Family<br/> </small> +<small><b>${severity}</b><br/>Severity<br/> </small> +<small><b>${date}${raised_for_html}</b><br/>Time<br/> </small> +<a href=\"${goto_url}\">View Netdata</a><br/> +<small><small>The source of this alarm is line ${src}</small></small> +" + +SENT_HIPCHAT=$? + # ----------------------------------------------------------------------------- # send the email @@ -760,8 +1297,21 @@ SENT_EMAIL=$? # ----------------------------------------------------------------------------- # let netdata know -# we did send something -[ ${SENT_EMAIL} -eq 0 -o ${SENT_PUSHOVER} -eq 0 -o ${SENT_TELEGRAM} -eq 0 -o ${SENT_SLACK} -eq 0 ] && exit 0 +if [ ${SENT_EMAIL} -eq 0 \ + -o ${SENT_PUSHOVER} -eq 0 \ + -o ${SENT_TELEGRAM} -eq 0 \ + -o ${SENT_SLACK} -eq 0 \ + -o ${SENT_TWILIO} -eq 0 \ + -o ${SENT_HIPCHAT} -eq 0 \ + -o ${SENT_MESSAGEBIRD} -eq 0 \ + -o ${SENT_PUSHBULLET} -eq 0 \ + -o ${SENT_KAFKA} -eq 0 \ + -o ${SENT_PD} -eq 0 \ + ] + then + # we did send something + exit 0 +fi # we did not send anything exit 1 diff --git a/plugins.d/alarm-test.sh b/plugins.d/alarm-test.sh new file mode 100755 index 000000000..1963111a5 --- /dev/null +++ b/plugins.d/alarm-test.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash + +# netdata +# real-time performance and health monitoring, done right! +# (C) 2016 Costa Tsaousis <costa@tsaousis.gr> +# GPL v3+ +# +# Script to test alarm notifications for netdata + +dir="$(dirname "${0}")" +${dir}/alarm-notify.sh test "${1}" +exit $? diff --git a/plugins.d/cgroup-name.sh b/plugins.d/cgroup-name.sh index 1c6f564b4..9bb3bcabb 100755 --- a/plugins.d/cgroup-name.sh +++ b/plugins.d/cgroup-name.sh @@ -1,17 +1,66 @@ #!/usr/bin/env bash +# netdata +# real-time performance and health monitoring, done right! +# (C) 2016 Costa Tsaousis <costa@tsaousis.gr> +# GPL v3+ +# +# Script to find a better name for cgroups +# + export PATH="${PATH}:/sbin:/usr/sbin:/usr/local/sbin" export LC_ALL=C +# ----------------------------------------------------------------------------- + +PROGRAM_NAME="$(basename "${0}")" + +logdate() { + date "+%Y-%m-%d %H:%M:%S" +} + +log() { + local status="${1}" + shift + + echo >&2 "$(logdate): ${PROGRAM_NAME}: ${status}: ${*}" + +} + +warning() { + log WARNING "${@}" +} + +error() { + log ERROR "${@}" +} + +info() { + log INFO "${@}" +} + +fatal() { + log FATAL "${@}" + exit 1 +} + +debug=0 +debug() { + [ $debug -eq 1 ] && log DEBUG "${@}" +} + +# ----------------------------------------------------------------------------- + NETDATA_CONFIG_DIR="${NETDATA_CONFIG_DIR-/etc/netdata}" CONFIG="${NETDATA_CONFIG_DIR}/cgroups-names.conf" CGROUP="${1}" NAME= +# ----------------------------------------------------------------------------- + if [ -z "${CGROUP}" ] then - echo >&2 "${0}: called without a cgroup name. Nothing to do." - exit 1 + fatal "called without a cgroup name. Nothing to do." fi if [ -f "${CONFIG}" ] @@ -19,15 +68,15 @@ if [ -f "${CONFIG}" ] NAME="$(grep "^${CGROUP} " "${CONFIG}" | sed "s/[[:space:]]\+/ /g" | cut -d ' ' -f 2)" if [ -z "${NAME}" ] then - echo >&2 "${0}: cannot find cgroup '${CGROUP}' in '${CONFIG}'." + info "cannot find cgroup '${CGROUP}' in '${CONFIG}'." fi #else -# echo >&2 "${0}: configuration file '${CONFIG}' is not available." +# info "configuration file '${CONFIG}' is not available." fi function get_name_classic { local DOCKERID="$1" - echo >&2 "Running command: docker ps --filter=id=\"${DOCKERID}\" --format=\"{{.Names}}\"" + info "Running command: docker ps --filter=id=\"${DOCKERID}\" --format=\"{{.Names}}\"" NAME="$( docker ps --filter=id="${DOCKERID}" --format="{{.Names}}" )" return 0 } @@ -36,10 +85,10 @@ function get_name_api { local DOCKERID="$1" if [ ! -S "/var/run/docker.sock" ] then - echo >&2 "Can't find /var/run/docker.sock" + warning "Can't find /var/run/docker.sock" return 1 fi - echo >&2 "Running API command: /containers/${DOCKERID}/json" + info "Running API command: /containers/${DOCKERID}/json" JSON=$(echo -e "GET /containers/${DOCKERID}/json HTTP/1.0\r\n" | nc -U /var/run/docker.sock | egrep '^{.*') NAME=$(echo $JSON | jq -r .Name,.Config.Hostname | grep -v null | head -n1 | sed 's|^/||') return 0 @@ -62,10 +111,10 @@ if [ -z "${NAME}" ] fi if [ -z "${NAME}" ] then - echo >&2 "Cannot find the name of docker container '${DOCKERID}'" + warning "cannot find the name of docker container '${DOCKERID}'" NAME="${DOCKERID:0:12}" else - echo >&2 "Docker container '${DOCKERID}' is named '${NAME}'" + info "docker container '${DOCKERID}' is named '${NAME}'" fi fi fi @@ -74,5 +123,5 @@ if [ -z "${NAME}" ] [ ${#NAME} -gt 100 ] && NAME="${NAME:0:100}" fi -echo >&2 "${0}: cgroup '${CGROUP}' is called '${NAME}'" +info "cgroup '${CGROUP}' is called '${NAME}'" echo "${NAME}" diff --git a/plugins.d/charts.d.plugin b/plugins.d/charts.d.plugin index df9998ece..00206f95f 100755 --- a/plugins.d/charts.d.plugin +++ b/plugins.d/charts.d.plugin @@ -1,36 +1,95 @@ #!/usr/bin/env bash +# netdata +# real-time performance and health monitoring, done right! +# (C) 2016 Costa Tsaousis <costa@tsaousis.gr> +# GPL v3+ +# +# charts.d.plugin allows easy development of BASH plugins +# +# if you need to run parallel charts.d processes, link this file to a different name +# in the same directory, with a .plugin suffix and netdata will start both of them, +# each will have a different config file and modules configuration directory. +# + +export PATH="${PATH}:/sbin:/usr/sbin:/usr/local/bin:/usr/local/sbin" + PROGRAM_FILE="$0" PROGRAM_NAME="$(basename $0)" PROGRAM_NAME="${PROGRAM_NAME/.plugin}" +MODULE_NAME="main" -# if you need to run parallel charts.d processes -# just link this files with a different name -# in the same directory, with a .plugin suffix -# netdata will start multiple of them -# each will have a different config file +# ----------------------------------------------------------------------------- +# create temp dir -echo >&2 "$PROGRAM_NAME: started from '$PROGRAM_FILE' with options: $*" +debug=0 +TMP_DIR= +chartsd_cleanup() { + if [ ! -z "$TMP_DIR" -a -d "$TMP_DIR" ] + then + [ $debug -eq 1 ] && echo >&2 "$PROGRAM_NAME: cleaning up temporary directory $TMP_DIR ..." + rm -rf "$TMP_DIR" + fi + exit 0 +} +trap chartsd_cleanup EXIT +trap chartsd_cleanup SIGHUP +trap chartsd_cleanup INT -if [ $(( ${BASH_VERSINFO[0]} )) -lt 4 ] +if [ $UID = "0" ] then - echo >&2 - echo >&2 "$PROGRAM_NAME: ERROR" - echo >&2 "BASH version 4 or later is required." - echo >&2 "You are running version: ${BASH_VERSION}" - echo >&2 "Please upgrade." - echo >&2 - exit 1 + TMP_DIR="$( mktemp -d /var/run/netdata-${PROGRAM_NAME}-XXXXXXXXXX )" +else + TMP_DIR="$( mktemp -d /tmp/.netdata-${PROGRAM_NAME}-XXXXXXXXXX )" fi +logdate() { + date "+%Y-%m-%d %H:%M:%S" +} + +log() { + local status="${1}" + shift + + echo >&2 "$(logdate): ${PROGRAM_NAME}: ${status}: ${MODULE_NAME}: ${*}" + +} + +warning() { + log WARNING "${@}" +} + +error() { + log ERROR "${@}" +} + +info() { + log INFO "${@}" +} + +fatal() { + log FATAL "${@}" + echo "DISABLE" + exit 1 +} + +debug() { + [ $debug -eq 1 ] && log DEBUG "${@}" +} + +# ----------------------------------------------------------------------------- # check a few commands + require_cmd() { - which "$1" >/dev/null - if [ $? -ne 0 ] + local x=$(which "${1}" 2>/dev/null || command -v "${1}" 2>/dev/null) + if [ -z "${x}" -o ! -x "${x}" ] then - echo >&2 "$PROGRAM_NAME: ERROR: Command '$1' is not found in the system path." + warning "command '${1}' is not found in ${PATH}." + eval "${1^^}_CMD=\"\"" return 1 fi + + eval "${1^^}_CMD=\"${x}\"" return 0 } @@ -43,9 +102,17 @@ require_cmd grep || exit 1 require_cmd egrep || exit 1 require_cmd mktemp || exit 1 require_cmd awk || exit 1 +require_cmd timeout || exit 1 +require_cmd curl || exit 1 + +# ----------------------------------------------------------------------------- + +[ $(( ${BASH_VERSINFO[0]} )) -lt 4 ] && fatal "BASH version 4 or later is required, but found version: ${BASH_VERSION}. Please upgrade." + +info "started from '$PROGRAM_FILE' with options: $*" # ----------------------------------------------------------------------------- -# insternal defaults +# internal defaults # netdata exposes a few environment variables for us pluginsd="${NETDATA_PLUGINS_DIR}" @@ -97,7 +164,6 @@ enable_all_charts="yes" # ----------------------------------------------------------------------------- # parse parameters -debug=0 check=0 chart_only= while [ ! -z "$1" ] @@ -143,9 +209,7 @@ do continue fi - echo >&2 "Cannot understand parameter $1. Aborting." - echo "DISABLE" - exit 1 + fatal "Cannot understand parameter $1. Aborting." done @@ -173,17 +237,13 @@ mysleep="sleep" if [ -f "$myconfig" ] then . "$myconfig" - if [ $? -ne 0 ] - then - echo >&2 "$PROGRAM_NAME: cannot load $myconfig" - echo "DISABLE" - exit 1 - fi + [ $? -ne 0 ] && fatal "cannot load $myconfig" + time_divisor=$((time_divisor)) [ $time_divisor -lt 10 ] && time_divisor=10 [ $time_divisor -gt 100 ] && time_divisor=100 else - echo >&2 "$PROGRAM_NAME: configuration file '$myconfig' not found. Using defaults." + info "configuration file '$myconfig' not found. Using defaults." fi # we check for the timeout command, after we load our @@ -204,12 +264,7 @@ update_every=$(( update_every + 1 - 1)) # makes sure it is a number test $update_every -eq 0 && update_every=1 # if it is zero, make it 1 # check the charts.d directory -if [ ! -d "$chartsd" ] - then - echo >&2 "$PROGRAM_NAME: cannot find charts directory '$chartsd'" - echo "DISABLE" -fi - +[ ! -d "$chartsd" ] && fatal "cannot find charts directory '$chartsd'" # ----------------------------------------------------------------------------- # library functions @@ -221,6 +276,35 @@ fixid() { tr "[A-Z]" "[a-z]" } +run() { + local ret pid="${BASHPID}" t + + if [ "z${1}" = "z-t" -a "${2}" != "0" ] + then + t="${2}" + shift 2 + timeout ${t} "${@}" 2>"${TMP_DIR}/run.${pid}" + ret=$? + else + "${@}" 2>"${TMP_DIR}/run.${pid}" + ret=$? + fi + + if [ ${ret} -ne 0 ] + then + { + printf "$(logdate): ${PROGRAM_NAME}: ${status}: ${MODULE_NAME}: command '" + printf "%q " "${@}" + printf "' failed:\n --- BEGIN TRACE ---\n" + cat "${TMP_DIR}/run.${pid}" + printf " --- END TRACE ---\n" + } >&2 + fi + rm "${TMP_DIR}/run.${pid}" + + return ${ret} +} + # convert any floating point number # to integer, give a multiplier # the result is stored in ${FLOAT2INT_RESULT} @@ -230,8 +314,6 @@ float2int() { local f m="$2" a b l v=($1) f=${v[0]} - # echo >&2 "value='${1}' f='${f}', m='${m}'" - # the length of the multiplier - 1 l=$(( ${#m} - 1 )) @@ -277,7 +359,6 @@ float2int() { # store the result FLOAT2INT_RESULT=$(( (a * m) + b )) - #echo >&2 "FLOAT2INT_RESULT='${FLOAT2INT_RESULT}'" } @@ -286,7 +367,7 @@ float2int() { all_charts() { cd "$chartsd" - [ $? -ne 0 ] && echo >&2 "$PROGRAM_NAME: Cannot cd to $chartsd" && return 1 + [ $? -ne 0 ] && error "cannot cd to $chartsd" && return 1 ls *.chart.sh | sed "s/\.chart\.sh$//g" } @@ -316,6 +397,8 @@ all_enabled_charts() { for chart in $( all_charts ) do + MODULE_NAME="${chart}" + eval "enabled=\$$chart" if [ -z "${enabled}" ] then @@ -327,35 +410,38 @@ all_enabled_charts() { if [ ! "${enabled}" = "${required}" ] then - echo >&2 "$PROGRAM_NAME: '$chart' is NOT enabled. Add a line with $chart=$required in $myconfig to enable it (or remove the line that disables it)." + info "is disabled. Add a line with $chart=$required in $myconfig to enable it (or remove the line that disables it)." else - [ $debug -eq 1 ] && echo >&2 "$PROGRAM_NAME: '$chart' is enabled." + debug "is enabled for auto-detection." local charts="$charts $chart" fi done + MODULE_NAME="main" local charts2= for chart in $charts do + MODULE_NAME="${chart}" + # check the enabled charts local check="$( cat "$chartsd/$chart.chart.sh" | sed "s/^ \+//g" | grep "^$chart$charts_check()" )" if [ -z "$check" ] then - echo >&2 "$PROGRAM_NAME: chart '$chart' does not seem to have a $chart$charts_check() function. Disabling it." + error "module '$chart' does not seem to have a $chart$charts_check() function. Disabling it." continue fi local create="$( cat "$chartsd/$chart.chart.sh" | sed "s/^ \+//g" | grep "^$chart$charts_create()" )" if [ -z "$create" ] then - echo >&2 "$PROGRAM_NAME: chart '$chart' does not seem to have a $chart$charts_create() function. Disabling it." + error "module '$chart' does not seem to have a $chart$charts_create() function. Disabling it." continue fi local update="$( cat "$chartsd/$chart.chart.sh" | sed "s/^ \+//g" | grep "^$chart$charts_update()" )" if [ -z "$update" ] then - echo >&2 "$PROGRAM_NAME: chart '$chart' does not seem to have a $chart$charts_update() function. Disabling it." + error "module '$chart' does not seem to have a $chart$charts_update() function. Disabling it." continue fi @@ -364,7 +450,7 @@ all_enabled_charts() { #then # if [ ! -z "$( cat "$confd/$chart.conf" | sed "s/^ \+//g" | grep -v "^$" | grep -v "^#" | grep -v "^$chart$charts_undescore" )" ] # then - # echo >&2 "$PROGRAM_NAME: chart's $chart config $confd/$chart.conf should only have lines starting with $chart$charts_undescore . Disabling it." + # error "module's $chart config $confd/$chart.conf should only have lines starting with $chart$charts_undescore . Disabling it." # continue # fi #fi @@ -374,19 +460,19 @@ all_enabled_charts() { # "$pluginsd/charts.d.dryrun-helper.sh" "$chart" "$chartsd/$chart.chart.sh" "$confd/$chart.conf" >/dev/null # if [ $? -ne 0 ] # then - # echo >&2 "$PROGRAM_NAME: chart's $chart did not pass the dry run check. This means it uses global variables not starting with $chart. Disabling it." + # error "module's $chart did not pass the dry run check. This means it uses global variables not starting with $chart. Disabling it." # continue # fi #fi local charts2="$charts2 $chart" done + MODULE_NAME="main" echo $charts2 - [ $debug -eq 1 ] && echo >&2 "$PROGRAM_NAME: enabled charts: $charts2" + debug "enabled charts: $charts2" } - # ----------------------------------------------------------------------------- # load the charts @@ -394,19 +480,22 @@ suffix_update_every="_update_every" active_charts= for chart in $( all_enabled_charts ) do - [ $debug -eq 1 ] && echo >&2 "$PROGRAM_NAME: loading chart: '$chartsd/$chart.chart.sh'" + MODULE_NAME="${chart}" + + debug "loading module: '$chartsd/$chart.chart.sh'" + . "$chartsd/$chart.chart.sh" - if [ -f "$confd/charts.d/$chart.conf" ] + if [ -f "$confd/$PROGRAM_NAME/$chart.conf" ] then - [ $debug -eq 1 ] && echo >&2 "$PROGRAM_NAME: loading chart options: '$confd/charts.d/$chart.conf'" - . "$confd/charts.d/$chart.conf" + debug "loading module configuration: '$confd/$PROGRAM_NAME/$chart.conf'" + . "$confd/$PROGRAM_NAME/$chart.conf" elif [ -f "$confd/$chart.conf" ] then - [ $debug -eq 1 ] && echo >&2 "$PROGRAM_NAME: loading chart options: '$confd/$chart.conf'" + debug "loading module configuration: '$confd/$chart.conf'" . "$confd/$chart.conf" else - echo >&2 "$PROGRAM_NAME: $chart: configuration file '$confd/charts.d/$chart.conf' not found. Using defaults." + warning "configuration file '$confd/$PROGRAM_NAME/$chart.conf' not found. Using defaults." fi eval "dt=\$$chart$suffix_update_every" @@ -419,13 +508,14 @@ do $chart$charts_check if [ $? -eq 0 ] then - [ $debug -eq 1 ] && echo >&2 "$PROGRAM_NAME: '$chart' activated" + debug "module '$chart' activated" active_charts="$active_charts $chart" else - echo >&2 "$PROGRAM_NAME: chart '$chart' check() function reports failure." + error "module's '$chart' check() function reports failure." fi done -[ $debug -eq 1 ] && echo >&2 "$PROGRAM_NAME: activated charts: $active_charts" +MODULE_NAME="main" +debug "activated modules: $active_charts" # ----------------------------------------------------------------------------- @@ -438,7 +528,7 @@ test $debug -eq 1 && debug_time=tellwork # if we only need a specific chart, remove all the others if [ ! -z "${chart_only}" ] then - [ $debug -eq 1 ] && echo >&2 "$PROGRAM_NAME: requested to run only for: '${chart_only}'" + debug "requested to run only for: '${chart_only}'" check_charts= for chart in $active_charts do @@ -450,41 +540,19 @@ then done active_charts="$check_charts" fi -[ $debug -eq 1 ] && echo >&2 "$PROGRAM_NAME: activated charts: $active_charts" +debug "activated charts: $active_charts" # stop if we just need a pre-check if [ $check -eq 1 ] then - echo >&2 "CHECK RESULT" - echo >&2 "Will run the charts: $active_charts" + info "CHECK RESULT" + info "Will run the charts: $active_charts" exit 0 fi # ----------------------------------------------------------------------------- -# create temp dir - -TMP_DIR= -chartsd_cleanup() { - cd /tmp - if [ ! -z "$TMP_DIR" -a -d "$TMP_DIR" ] - then - [ $debug -eq 1 ] && echo >&2 "$PROGRAM_NAME: cleaning up temporary directory $TMP_DIR ..." - rm -rf "$TMP_DIR" - fi - exit 0 -} -trap chartsd_cleanup EXIT -trap chartsd_cleanup SIGHUP -trap chartsd_cleanup INT -if [ $UID = "0" ] -then - TMP_DIR="$( mktemp -d /var/run/netdata-${PROGRAM_NAME}-XXXXXXXXXX )" -else - TMP_DIR="$( mktemp -d /tmp/.netdata-${PROGRAM_NAME}-XXXXXXXXXX )" -fi - -cd "$TMP_DIR" || exit 1 +cd "${TMP_DIR}" || exit 1 # ----------------------------------------------------------------------------- # create charts @@ -492,28 +560,26 @@ cd "$TMP_DIR" || exit 1 run_charts= for chart in $active_charts do - [ $debug -eq 1 ] && echo >&2 "$PROGRAM_NAME: Calling '$chart$charts_create()'..." + MODULE_NAME="${chart}" + + debug "calling '$chart$charts_create()'..." $chart$charts_create if [ $? -eq 0 ] then run_charts="$run_charts $chart" - [ $debug -eq 1 ] && echo >&2 "$PROGRAM_NAME: '$chart' has initialized." + debug "'$chart' initialized." else - echo >&2 "$PROGRAM_NAME: chart '$chart' function '$chart$charts_create()' reports failure." + error "module's '$chart' function '$chart$charts_create()' reports failure." fi done -[ $debug -eq 1 ] && echo >&2 "$PROGRAM_NAME: run_charts='$run_charts'" +MODULE_NAME="main" +debug "run_charts='$run_charts'" # ----------------------------------------------------------------------------- # update dimensions -if [ -z "$run_charts" ] - then - echo >&2 "$PROGRAM_NAME: No charts to collect data from." - echo "DISABLE" - exit 1 -fi +[ -z "$run_charts" ] && fatal "No charts to collect data from." declare -A charts_last_update=() charts_update_every=() charts_next_update=() charts_run_counter=() charts_serial_failures=() global_update() { @@ -552,12 +618,12 @@ global_update() { for chart in "${now_charts[@]}" do - #echo >&2 " DEBUG: chart: $chart last: ${charts_last_update[$chart]}, next: ${charts_next_update[$chart]}, now: ${now_ms}" + MODULE_NAME="${chart}" + if [ ${now_ms} -ge ${charts_next_update[$chart]} ] then last_ms=${charts_last_update[$chart]} dt=$(( (now_ms - last_ms) )) - #echo >&2 " DEBUG: chart: $chart last: ${charts_last_update[$chart]}, next: ${charts_next_update[$chart]}, now: ${now_ms}, dt: ${dt}" charts_last_update[$chart]=${now_ms} @@ -576,7 +642,6 @@ global_update() { fi exec_start_ms=$now_ms - #echo >&2 " EXEC: $chart$charts_update $dt" $chart$charts_update $dt ret=$? @@ -596,9 +661,9 @@ global_update() { if [ ${charts_serial_failures[$chart]} -gt 10 ] then - echo >&2 "$PROGRAM_NAME: chart '$chart' update() function reported failure ${charts_serial_failures[$chart]} times. Disabling it." + error "module's '$chart' update() function reported failure ${charts_serial_failures[$chart]} times. Disabling it." else - echo >&2 "$PROGRAM_NAME: chart '$chart' update() function reports failure. Will keep trying for a while." + error "module's '$chart' update() function reports failure. Will keep trying for a while." next_charts+=($chart) fi fi @@ -606,6 +671,7 @@ global_update() { next_charts+=($chart) fi done + MODULE_NAME="${chart}" # wait the time you are required to next_ms=$((now_ms + (update_every * 1000 * 100) )) @@ -625,18 +691,17 @@ global_update() { millis="0${millis}" fi - [ $debug -eq 1 ] && echo >&2 "$PROGRAM_NAME: sleeping for ${seconds}.${millis} seconds." + debug "sleeping for ${seconds}.${millis} seconds." ${mysleep} ${seconds}.${millis} else - [ $debug -eq 1 ] && echo >&2 "$PROGRAM_NAME: sleeping for ${update_every} seconds." + debug "sleeping for ${update_every} seconds." ${mysleep} $update_every fi test ${now_ms} -ge ${exit_at} && exit 0 done - echo >&2 "$PROGRAM_NAME: Nothing left to do. Disabling charts.d.plugin." - echo "DISABLE" + fatal "nothing left to do, exiting..." } global_update diff --git a/plugins.d/fping.plugin b/plugins.d/fping.plugin new file mode 100755 index 000000000..d523f4474 --- /dev/null +++ b/plugins.d/fping.plugin @@ -0,0 +1,178 @@ +#!/usr/bin/env bash + +# netdata +# real-time performance and health monitoring, done right! +# (C) 2016 Costa Tsaousis <costa@tsaousis.gr> +# GPL v3+ +# +# This plugin requires a latest version of fping. +# You can compile it from source, by running me with option: install + +export PATH="${PATH}:/sbin:/usr/sbin:/usr/local/sbin" +export LC_ALL=C + +if [ "${1}" = "install" ] + then + [ "${UID}" != 0 ] && echo >&2 "Please run me as root. This will install a single binary file: /usr/local/bin/fping." && exit 1 + + run() { + printf >&2 " > " + printf >&2 "%q " "${@}" + printf >&2 "\n" + "${@}" || exit 1 + } + + [ ! -d /usr/src ] && run mkdir -p /usr/src + [ ! -d /usr/local/bin ] && run mkdir -p /usr/local/bin + + run cd /usr/src + + if [ -d fping-ktsaou.git ] + then + run cd fping-ktsaou.git + run git pull + else + run git clone https://github.com/ktsaou/fping.git fping-ktsaou.git + run cd fping-ktsaou.git + fi + + run ./autogen.sh + run ./configure --prefix=/usr/local + run make clean + run make + if [ -f /usr/local/bin/fping ] + then + run mv -f /usr/local/bin/fping /usr/local/bin/fping.old + fi + run mv src/fping /usr/local/bin/fping + run chown root:root /usr/local/bin/fping + run chmod 4755 /usr/local/bin/fping + echo >&2 + echo >&2 "All done, you have a compatible fping now at /usr/local/bin/fping." + echo >&2 + + fping="$(which fping 2>/dev/null || command -v fping 2>/dev/null)" + if [ "${fping}" != "/usr/local/bin/fping" ] + then + echo >&2 "You have another fping installed at: ${fping}." + echo >&2 "Please set:" + echo >&2 + echo >&2 " fping=\"/usr/local/bin/fping\"" + echo >&2 + echo >&2 "at /etc/netdata/fping.conf" + echo >&2 + fi + exit 0 +fi + +# ----------------------------------------------------------------------------- + +PROGRAM_NAME="$(basename "${0}")" + +logdate() { + date "+%Y-%m-%d %H:%M:%S" +} + +log() { + local status="${1}" + shift + + echo >&2 "$(logdate): ${PROGRAM_NAME}: ${status}: ${*}" + +} + +warning() { + log WARNING "${@}" +} + +error() { + log ERROR "${@}" +} + +info() { + log INFO "${@}" +} + +fatal() { + log FATAL "${@}" + echo "DISABLE" + exit 1 +} + +debug=0 +debug() { + [ $debug -eq 1 ] && log DEBUG "${@}" +} + +# ----------------------------------------------------------------------------- + +# store in ${plugin} the name we run under +# this allows us to copy/link fping.plugin under a different name +# to have multiple fping plugins running with different settings +plugin="${PROGRAM_NAME/.plugin/}" + + +# ----------------------------------------------------------------------------- + +# the frequency to send info to netdata +# passed by netdata as the first parameter +update_every="${1-1}" + +# the netdata configuration directory +# passed by netdata as an environment variable +NETDATA_CONFIG_DIR="${NETDATA_CONFIG_DIR-/etc/netdata}" + +# ----------------------------------------------------------------------------- +# configuration options +# can be overwritten at /etc/netdata/fping.conf + +# the fping binary to use +# we need one that can output netdata friendly info (supporting: -N) +# if you have multiple versions, put here the full filename of the right one +fping="$( which fping 2>/dev/null || command -v fping 2>/dev/null )" + +# a space separated list of hosts to fping +# we suggest to put names here and the IPs of these names in /etc/hosts +hosts="" + +# the time in milliseconds (1 sec = 1000 ms) +# to ping the hosts - by default 5 pings per host per iteration +ping_every="$((update_every * 1000 / 5))" + +# fping options +fping_opts="-R -b 56 -i 1 -r 0 -t 5000" + +# ----------------------------------------------------------------------------- +# load the configuration file + +if [ ! -f "${NETDATA_CONFIG_DIR}/${plugin}.conf" ] +then + fatal "configuration file '${NETDATA_CONFIG_DIR}/${plugin}.conf' not found - nothing to do." +fi + +source "${NETDATA_CONFIG_DIR}/${plugin}.conf" + +if [ -z "${hosts}" ] +then + fatal "no hosts configued in '${NETDATA_CONFIG_DIR}/${plugin}.conf' - nothing to do." +fi + +if [ -z "${fping}" -o ! -x "${fping}" ] +then + fatal "command '${fping}' is not found or is not executable - cannot proceed." +fi + +if [ ${ping_every} -lt 20 ] + then + warning "ping every was set to ${ping_every} but 20 is the minimum for non-root users. Setting it to 20 ms." + ping_every=20 +fi + +# the fping options we will use +options=( -N -l -Q ${update_every} -p ${ping_every} ${fping_opts} ${hosts} ) + +# execute fping +exec "${fping}" "${options[@]}" + +# if we cannot execute fping, stop +fatal "command '${fping} ${options[@]}' failed to be executed." diff --git a/plugins.d/loopsleepms.sh.inc b/plugins.d/loopsleepms.sh.inc index 6de93043c..ef3db192d 100644 --- a/plugins.d/loopsleepms.sh.inc +++ b/plugins.d/loopsleepms.sh.inc @@ -1,6 +1,6 @@ # no need for shebang - this file is included from other scripts -LOOPSLEEP_DATE="$(which date)" +LOOPSLEEP_DATE="$(which date 2>/dev/null || command -v date 2>/dev/null)" if [ -z "$LOOPSLEEP_DATE" ] then echo >&2 "$0: ERROR: Cannot find the command 'date' in the system path." @@ -139,7 +139,7 @@ loopsleepms() { # calculate ms since last run [ ${LOOPSLEEPMS_LASTRUN} -gt 0 ] && \ - LOOPSLEEPMS_LASTWORK=$((now_ms - LOOPSLEEPMS_LASTRUN - LOOPSLEEPMS_LASTSLEEP)) + LOOPSLEEPMS_LASTWORK=$((now_ms - LOOPSLEEPMS_LASTRUN - LOOPSLEEPMS_LASTSLEEP + current_time_ms_accuracy)) # echo "# last loop's work took $LOOPSLEEPMS_LASTWORK ms" # remember this run diff --git a/plugins.d/node.d.plugin b/plugins.d/node.d.plugin index 21b04384e..8b7047fcb 100755 --- a/plugins.d/node.d.plugin +++ b/plugins.d/node.d.plugin @@ -8,6 +8,11 @@ // Then, the second line, finds nodejs or node or js in the system path // and executes it with the shell parameters. +// netdata +// real-time performance and health monitoring, done right! +// (C) 2016 Costa Tsaousis <costa@tsaousis.gr> +// GPL v3+ + // -------------------------------------------------------------------------------------------------------------------- 'use strict'; @@ -30,6 +35,7 @@ process.mainModule.paths.unshift(NODE_D_DIR); var fs = require('fs'); var url = require('url'); +var util = require('util'); var http = require('http'); var path = require('path'); var extend = require('extend'); @@ -61,8 +67,6 @@ extend(true, netdata.options, { update_every: NETDATA_UPDATE_EVERY, - exit_after_ms: 3600 * 4 * 1000, - paths: { plugins: NETDATA_PLUGINS_DIR, config: NETDATA_CONFIG_DIR, @@ -79,8 +83,15 @@ netdata.options.config_filename = pluginConfig(__filename); try { netdata.options_loaded = JSON.parse(fs.readFileSync(netdata.options.config_filename, 'utf8')); extend(true, netdata.options, netdata.options_loaded); - console.error('merged netdata object:'); - console.error(netdata); + + if(!netdata.options.paths.plugins) + netdata.options.paths.plugins = NETDATA_PLUGINS_DIR; + + if(!netdata.options.paths.config) + netdata.options.paths.config = NETDATA_CONFIG_DIR; + + // console.error('merged netdata object:'); + // console.error(util.inspect(netdata, {depth: 10})); } catch(e) { netdata.error('Cannot read configuration file ' + netdata.options.config_filename + ': ' + e.message + ', using internal defaults.'); diff --git a/plugins.d/python.d.plugin b/plugins.d/python.d.plugin index 5e81fb263..b4e6473a6 100755 --- a/plugins.d/python.d.plugin +++ b/plugins.d/python.d.plugin @@ -9,6 +9,7 @@ import os import sys import time import threading +from re import sub # ----------------------------------------------------------------------------- # globals & environment setup @@ -28,6 +29,7 @@ sys.path.append(MODULES_DIR + "python_modules") PROGRAM = os.path.basename(__file__).replace(".plugin", "") DEBUG_FLAG = False +TRACE_FLAG = False OVERRIDE_UPDATE_EVERY = False # ----------------------------------------------------------------------------- @@ -292,8 +294,8 @@ class PythonCharts(object): if job.name is not None and len(job.name) != 0: prefix += "/" + job.name try: + msg.error("DISABLED:", prefix) self.jobs.remove(job) - msg.info("Disabled", prefix) except Exception as e: msg.debug("This shouldn't happen. NO " + prefix + " IN LIST:" + str(self.jobs) + " ERROR: " + str(e)) @@ -332,21 +334,21 @@ class PythonCharts(object): job = self.jobs[i] try: if not job.check(): - msg.error(job.chart_name, "check function failed.") + msg.error(job.chart_name, "check() failed - disabling job") self._stop(job) else: - msg.debug(job.chart_name, "check succeeded") + msg.info("CHECKED OK:", job.chart_name) i += 1 try: if job.override_name is not None: - new_name = job.__module__ + '_' + job.override_name + new_name = job.__module__ + '_' + sub(r'\s+', '_', job.override_name) if new_name in overridden: - msg.error(job.override_name + " already exists. Stopping '" + job.name + "'") + msg.info("DROPPED:", job.name, ", job '" + job.override_name + "' is already served by another job.") self._stop(job) i -= 1 else: job.name = job.override_name - msg.debug(job.chart_name + " changing chart name to: '" + new_name + "'") + msg.info("RENAMED:", new_name, ", from " + job.chart_name) job.chart_name = new_name overridden.append(job.chart_name) except Exception: @@ -435,7 +437,7 @@ def parse_cmdline(directory, *commands): :param commands: list of str :return: dict """ - global DEBUG_FLAG + global DEBUG_FLAG, TRACE_FLAG global OVERRIDE_UPDATE_EVERY global BASE_CONFIG @@ -447,6 +449,8 @@ def parse_cmdline(directory, *commands): elif cmd == "debug" or cmd == "all": DEBUG_FLAG = True # redirect stderr to stdout? + elif cmd == "trace" or cmd == "all": + TRACE_FLAG = True elif os.path.isfile(directory + cmd + ".chart.py") or os.path.isfile(directory + cmd): #DEBUG_FLAG = True mods.append(cmd.replace(".chart.py", "")) @@ -470,14 +474,14 @@ def run(): """ Main program. """ - global DEBUG_FLAG, BASE_CONFIG + global DEBUG_FLAG, TRACE_FLAG, BASE_CONFIG # read configuration file disabled = [] configfile = CONFIG_DIR + "python.d.conf" msg.PROGRAM = PROGRAM msg.info("reading configuration file:", configfile) - log_counter = 200 + log_throttle = 200 log_interval = 3600 conf = read_config(configfile) @@ -488,23 +492,33 @@ def run(): msg.fatal('disabled in configuration file.\n') except (KeyError, TypeError): pass + try: for param in BASE_CONFIG: BASE_CONFIG[param] = conf[param] except (KeyError, TypeError): pass # use default update_every from NETDATA_UPDATE_EVERY + try: DEBUG_FLAG = conf['debug'] except (KeyError, TypeError): pass + try: - log_counter = conf['logs_per_interval'] + TRACE_FLAG = conf['trace'] except (KeyError, TypeError): pass + + try: + log_throttle = conf['logs_per_interval'] + except (KeyError, TypeError): + pass + try: log_interval = conf['log_interval'] except (KeyError, TypeError): pass + for k, v in conf.items(): if k in ("update_every", "debug", "enabled"): continue @@ -514,8 +528,11 @@ def run(): # parse passed command line arguments modules = parse_cmdline(MODULES_DIR, *sys.argv) msg.DEBUG_FLAG = DEBUG_FLAG - msg.LOG_COUNTER = log_counter + msg.TRACE_FLAG = TRACE_FLAG + msg.LOG_THROTTLE = log_throttle msg.LOG_INTERVAL = log_interval + msg.LOG_COUNTER = 0 + msg.LOG_NEXT_CHECK = 0 msg.info("MODULES_DIR='" + MODULES_DIR + "', CONFIG_DIR='" + CONFIG_DIR + "', UPDATE_EVERY=" + str(BASE_CONFIG['update_every']) + diff --git a/plugins.d/tc-qos-helper.sh b/plugins.d/tc-qos-helper.sh index 9caef85f7..e9253c8f2 100755 --- a/plugins.d/tc-qos-helper.sh +++ b/plugins.d/tc-qos-helper.sh @@ -1,16 +1,64 @@ #!/usr/bin/env bash +# netdata +# real-time performance and health monitoring, done right! +# (C) 2016 Costa Tsaousis <costa@tsaousis.gr> +# GPL v3+ +# +# This script is a helper to allow netdata collect tc data. +# tc output parsing has been implemented in C, inside netdata +# This script allows setting names to dimensions. + export PATH="${PATH}:/sbin:/usr/sbin:/usr/local/sbin" +export LC_ALL=C PROGRAM_FILE="$0" PROGRAM_NAME="$(basename $0)" PROGRAM_NAME="${PROGRAM_NAME/.plugin}" +# ----------------------------------------------------------------------------- + +logdate() { + date "+%Y-%m-%d %H:%M:%S" +} + +log() { + local status="${1}" + shift + + echo >&2 "$(logdate): ${PROGRAM_NAME}: ${status}: ${*}" + +} + +warning() { + log WARNING "${@}" +} + +error() { + log ERROR "${@}" +} + +info() { + log INFO "${@}" +} + +fatal() { + log FATAL "${@}" + exit 1 +} + +debug=0 +debug() { + [ $debug -eq 1 ] && log DEBUG "${@}" +} + +# ----------------------------------------------------------------------------- + plugins_dir="${NETDATA_PLUGINS_DIR}" [ -z "$plugins_dir" ] && plugins_dir="$( dirname $PROGRAM_FILE )" config_dir=${NETDATA_CONFIG_DIR-/etc/netdata} -tc="$(which tc 2>/dev/null)" +tc="$(which tc 2>/dev/null || command -v tc 2>/dev/null)" fireqos_run_dir="/var/run/fireqos" qos_get_class_names_every=120 qos_exit_every=3600 @@ -39,25 +87,37 @@ loopsleepms() { if [ -z "${tc}" -o ! -x "${tc}" ] then - echo >&2 "${PROGRAM_NAME}: Cannot find command 'tc' in this system." - exit 1 + fatal "cannot find command 'tc' in this system." fi -devices= +tc_devices= fix_names= setclassname() { echo "SETCLASSNAME $3 $2" } -show_tc() { - local x="${1}" interface_dev interface_classes interface_classes_monitor +show_tc_cls() { + local x="${1}" - echo "BEGIN ${x}" - ${tc} -s class show dev ${x} + if [ -f /etc/iproute2/tc_cls ] + then + local classid name rest + while read classid name rest + do + [ -z "${classid}" -o -z "${name}" -o "${classid}" = "#" -o "${name}" = "#" -o "${classid:0:1}" = "#" -o "${name:0:1}" = "#" ] && continue + setclassname "" "${name}" "${classid}" + done </etc/iproute2/tc_cls + return 0 + fi - # check FireQOS names for classes - if [ ! -z "${fix_names}" -a -f "${fireqos_run_dir}/ifaces/${x}" ] + return 1 +} + +show_fireqos_names() { + local x="${1}" name n interface_dev interface_classes interface_classes_monitor + + if [ -f "${fireqos_run_dir}/ifaces/${x}" ] then name="$(<"${fireqos_run_dir}/ifaces/${x}")" echo "SETDEVICENAME ${name}" @@ -71,15 +131,50 @@ show_tc() { setclassname ${n//|/ } done [ ! -z "${interface_dev}" ] && echo "SETDEVICEGROUP ${interface_dev}" + + return 0 fi + + return 1 +} + +show_tc() { + local x="${1}" + + echo "BEGIN ${x}" + + # netdata can parse the output of tc + ${tc} -s class show dev ${x} + + # check FireQOS names for classes + if [ ! -z "${fix_names}" ] + then + show_fireqos_names "${x}" || show_tc_cls "${x}" + fi + echo "END ${x}" } -all_devices() { - cat /proc/net/dev | grep ":" | cut -d ':' -f 1 | while read dev +find_tc_devices() { + local count=0 devs= dev rest l + + # find all the devices in the system + # without forking + while IFS=":| " read dev rest + do + count=$((count + 1)) + [ ${count} -le 2 ] && continue + devs="${devs} ${dev}" + done </proc/net/dev + + # from all the devices find the ones + # that have QoS defined + # unfortunately, one fork per device cannot be avoided + tc_devices= + for dev in ${devs} do - l=$(${tc} class show dev ${dev} | wc -l) - [ $l -ne 0 ] && echo ${dev} + l="$(${tc} class show dev ${dev} 2>/dev/null)" + [ ! -z "${l}" ] && tc_devices="${tc_devices} ${dev}" done } @@ -103,10 +198,10 @@ do then c=1 fix_names="YES" - devices="$( all_devices )" + find_tc_devices fi - for d in ${devices} + for d in ${tc_devices} do show_tc ${d} done diff --git a/python.d/Makefile.am b/python.d/Makefile.am index d769e3138..883f06c4c 100644 --- a/python.d/Makefile.am +++ b/python.d/Makefile.am @@ -1,46 +1,58 @@ MAINTAINERCLEANFILES= $(srcdir)/Makefile.in CLEANFILES = \ - python-modules-installer.sh \ - $(NULL) + python-modules-installer.sh \ + $(NULL) include $(top_srcdir)/build/subst.inc SUFFIXES = .in dist_python_SCRIPTS = \ - 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 \ - nginx_log.chart.py \ - phpfpm.chart.py \ - postfix.chart.py \ - redis.chart.py \ - retroshare.chart.py \ - sensors.chart.py \ - squid.chart.py \ - tomcat.chart.py \ - python-modules-installer.sh \ - $(NULL) + apache.chart.py \ + apache_cache.chart.py \ + bind_rndc.chart.py \ + cpufreq.chart.py \ + cpuidle.chart.py \ + dovecot.chart.py \ + elasticsearch.chart.py \ + example.chart.py \ + exim.chart.py \ + fail2ban.chart.py \ + freeradius.chart.py \ + gunicorn_log.chart.py \ + haproxy.chart.py \ + hddtemp.chart.py \ + ipfs.chart.py \ + isc_dhcpd.chart.py \ + mdstat.chart.py \ + memcached.chart.py \ + mysql.chart.py \ + nginx.chart.py \ + nginx_log.chart.py \ + ovpn_status_log.chart.py \ + phpfpm.chart.py \ + postfix.chart.py \ + postgres.chart.py \ + redis.chart.py \ + retroshare.chart.py \ + sensors.chart.py \ + squid.chart.py \ + tomcat.chart.py \ + varnish.chart.py \ + python-modules-installer.sh \ + $(NULL) dist_python_DATA = \ - README.md \ - $(NULL) + README.md \ + $(NULL) pythonmodulesdir=$(pythondir)/python_modules dist_pythonmodules_DATA = \ - python_modules/__init__.py \ - python_modules/base.py \ - python_modules/msg.py \ - python_modules/lm_sensors.py \ - $(NULL) + python_modules/__init__.py \ + python_modules/base.py \ + python_modules/msg.py \ + python_modules/lm_sensors.py \ + $(NULL) pythonyaml2dir=$(pythonmodulesdir)/pyyaml2 dist_pythonyaml2_DATA = \ diff --git a/python.d/Makefile.in b/python.d/Makefile.in index 426f43ed1..018e8b3eb 100644 --- a/python.d/Makefile.in +++ b/python.d/Makefile.in @@ -1,7 +1,7 @@ -# Makefile.in generated by automake 1.14.1 from Makefile.am. +# Makefile.in generated by automake 1.15 from Makefile.am. # @configure_input@ -# Copyright (C) 1994-2013 Free Software Foundation, Inc. +# Copyright (C) 1994-2014 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -16,7 +16,17 @@ VPATH = @srcdir@ -am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ @@ -79,10 +89,6 @@ PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ -DIST_COMMON = $(top_srcdir)/build/subst.inc $(srcdir)/Makefile.in \ - $(srcdir)/Makefile.am $(dist_python_SCRIPTS) \ - $(dist_python_DATA) $(dist_pythonmodules_DATA) \ - $(dist_pythonyaml2_DATA) $(dist_pythonyaml3_DATA) subdir = python.d ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_c___atomic.m4 \ @@ -90,10 +96,15 @@ am__aclocal_m4_deps = $(top_srcdir)/m4/ax_c___atomic.m4 \ $(top_srcdir)/m4/ax_c_mallinfo.m4 \ $(top_srcdir)/m4/ax_c_mallopt.m4 \ $(top_srcdir)/m4/ax_check_compile_flag.m4 \ + $(top_srcdir)/m4/ax_gcc_func_attribute.m4 \ $(top_srcdir)/m4/ax_pthread.m4 $(top_srcdir)/m4/jemalloc.m4 \ $(top_srcdir)/m4/tcmalloc.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(dist_python_SCRIPTS) \ + $(dist_python_DATA) $(dist_pythonmodules_DATA) \ + $(dist_pythonyaml2_DATA) $(dist_pythonyaml3_DATA) \ + $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = @@ -151,6 +162,7 @@ am__can_run_installinfo = \ DATA = $(dist_python_DATA) $(dist_pythonmodules_DATA) \ $(dist_pythonyaml2_DATA) $(dist_pythonyaml3_DATA) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/build/subst.inc DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ @@ -290,44 +302,56 @@ varlibdir = @varlibdir@ webdir = @webdir@ MAINTAINERCLEANFILES = $(srcdir)/Makefile.in CLEANFILES = \ - python-modules-installer.sh \ - $(NULL) + python-modules-installer.sh \ + $(NULL) SUFFIXES = .in dist_python_SCRIPTS = \ - 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 \ - nginx_log.chart.py \ - phpfpm.chart.py \ - postfix.chart.py \ - redis.chart.py \ - retroshare.chart.py \ - sensors.chart.py \ - squid.chart.py \ - tomcat.chart.py \ - python-modules-installer.sh \ - $(NULL) + apache.chart.py \ + apache_cache.chart.py \ + bind_rndc.chart.py \ + cpufreq.chart.py \ + cpuidle.chart.py \ + dovecot.chart.py \ + elasticsearch.chart.py \ + example.chart.py \ + exim.chart.py \ + fail2ban.chart.py \ + freeradius.chart.py \ + gunicorn_log.chart.py \ + haproxy.chart.py \ + hddtemp.chart.py \ + ipfs.chart.py \ + isc_dhcpd.chart.py \ + mdstat.chart.py \ + memcached.chart.py \ + mysql.chart.py \ + nginx.chart.py \ + nginx_log.chart.py \ + ovpn_status_log.chart.py \ + phpfpm.chart.py \ + postfix.chart.py \ + postgres.chart.py \ + redis.chart.py \ + retroshare.chart.py \ + sensors.chart.py \ + squid.chart.py \ + tomcat.chart.py \ + varnish.chart.py \ + python-modules-installer.sh \ + $(NULL) dist_python_DATA = \ - README.md \ - $(NULL) + README.md \ + $(NULL) pythonmodulesdir = $(pythondir)/python_modules dist_pythonmodules_DATA = \ - python_modules/__init__.py \ - python_modules/base.py \ - python_modules/msg.py \ - python_modules/lm_sensors.py \ - $(NULL) + python_modules/__init__.py \ + python_modules/base.py \ + python_modules/msg.py \ + python_modules/lm_sensors.py \ + $(NULL) pythonyaml2dir = $(pythonmodulesdir)/pyyaml2 dist_pythonyaml2_DATA = \ @@ -387,7 +411,6 @@ $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu python.d/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu python.d/Makefile -.PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ @@ -396,7 +419,7 @@ Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; -$(top_srcdir)/build/subst.inc: +$(top_srcdir)/build/subst.inc $(am__empty): $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh @@ -689,6 +712,8 @@ uninstall-am: uninstall-dist_pythonDATA uninstall-dist_pythonSCRIPTS \ uninstall-dist_pythonSCRIPTS uninstall-dist_pythonmodulesDATA \ uninstall-dist_pythonyaml2DATA uninstall-dist_pythonyaml3DATA +.PRECIOUS: Makefile + .in: if sed \ -e 's#[@]localstatedir_POST@#$(localstatedir)#g' \ diff --git a/python.d/README.md b/python.d/README.md index a3c7b4fac..75f5614a7 100644 --- a/python.d/README.md +++ b/python.d/README.md @@ -125,12 +125,82 @@ If no configuration is given, module will attempt to read log file at `/var/log/ --- +# bind_rndc + +Module parses bind dump file to collect real-time performance metrics + +**Requirements:** + * Version of bind must be 9.6 + + * Netdata must have permissions to run `rndc status` + +It produces: + +1. **Name server statistics** + * requests + * responses + * success + * auth_answer + * nonauth_answer + * nxrrset + * failure + * nxdomain + * recursion + * duplicate + * rejections + +2. **Incoming queries** + * RESERVED0 + * A + * NS + * CNAME + * SOA + * PTR + * MX + * TXT + * X25 + * AAAA + * SRV + * NAPTR + * A6 + * DS + * RSIG + * DNSKEY + * SPF + * ANY + * DLV + +3. **Outgoing queries** + * Same as Incoming queries + + +### configuration + +Sample: + +```yaml +local: + named_stats_path : '/var/log/bind/named.stats' +``` + +If no configuration is given, module will attempt to read named.stats file at `/var/log/bind/named.stats` + +--- + # cpufreq -Module shows current cpu frequency by looking at appropriate files in /sys/devices +This module shows the current CPU frequency as set by the cpufreq kernel +module. **Requirement:** -Processor which presents data scaling frequency data +You need to have `CONFIG_CPU_FREQ` and (optionally) `CONFIG_CPU_FREQ_STAT` +enabled in your kernel. + +This module tries to read from one of two possible locations. On +initialization, it tries to read the `time_in_state` files provided by +cpufreq\_stats. If this file does not exist, or doesn't contain valid data, it +falls back to using the more inaccurate `scaling_cur_freq` file (which only +represents the **current** CPU frequency, and doesn't account for any state +changes which happen between updates). It produces one chart with multiple lines (one line per core). @@ -142,11 +212,23 @@ Sample: sys_dir: "/sys/devices" ``` -If no configuration is given, module will search for `scaling_cur_freq` files in `/sys/devices` directory. +If no configuration is given, module will search for cpufreq files in `/sys/devices` directory. Directory is also prefixed with `NETDATA_HOST_PREFIX` if specified. --- +# cpuidle + +This module monitors the usage of CPU idle states. + +**Requirement:** +Your kernel needs to have `CONFIG_CPU_IDLE` enabled. + +It produces one stacked chart per CPU, showing the percentage of time spent in +each state. + +--- + # dovecot This module provides statistics information from dovecot server. @@ -158,45 +240,47 @@ Dovecot unix socket with R/W permissions for user netdata or dovecot with config Module gives information with following charts: -1. **logins and sessions** - * logins +1. **sessions** * active sessions -2. **commands** - number of IMAP commands +2. **logins** + * logins + +3. **commands** - number of IMAP commands * commands -3. **Faults** +4. **Faults** * minor * major -4. **Context Switches** +5. **Context Switches** * volountary * involountary -5. **disk** in bytes/s +6. **disk** in bytes/s * read * write -6. **bytes** in bytes/s +7. **bytes** in bytes/s * read * write -7. **number of syscalls** in syscalls/s +8. **number of syscalls** in syscalls/s * read * write -8. **lookups** - number of lookups per second +9. **lookups** - number of lookups per second * path * attr -9. **hits** - number of cache hits +10. **hits** - number of cache hits * hits -10. **attempts** - authorization attemts +11. **attempts** - authorization attemts * success * failure -11. **cache** - cached authorization hits +12. **cache** - cached authorization hits * hit * miss @@ -219,6 +303,67 @@ If no configuration is given, module will attempt to connect to dovecot using un --- +# elasticsearch + +Module monitor elasticsearch performance and health metrics + +It produces: + +1. **Search performance** charts: + * Number of queries, fetches + * Time spent on queries, fetches + * Query and fetch latency + +2. **Indexing performance** charts: + * Number of documents indexed, index refreshes, flushes + * Time spent on indexing, refreshing, flushing + * Indexing and flushing latency + +3. **Memory usage and garbace collection** charts: + * JVM heap currently in use, commited + * Count of garbage collections + * Time spent on garbage collections + +4. **Host metrics** charts: + * Available file descriptors in percent + * Opened HTTP connections + * Cluster communication transport metrics + +5. **Queues and rejections** charts: + * Number of queued/rejected threads in thread pool + +6. **Fielddata cache** charts: + * Fielddata cache size + * Fielddata evictions and circuit breaker tripped count + +7. **Cluster health API** charts: + * Cluster status + * Nodes and tasks statistics + * Shards statistics + +8. **Cluster stats API** charts: + * Nodes statistics + * Query cache statistics + * Docs statistics + * Store statistics + * Indices and shards statistics + +### configuration + +Sample: + +```yaml +local: + host : 'ipaddress' # Server ip address or hostname + port : 'password' # Port on which elasticsearch listed + cluster_health : True/False # Calls to cluster health elasticsearch API. Enabled by default. + cluster_stats : True/False # Calls to cluster stats elasticsearch API. Enabled by default. +``` + +If no configuration is given, module will fail to run. + +--- + # exim Simple module executing `exim -bpc` to grab exim queue. @@ -233,6 +378,151 @@ Configuration is not needed. --- +# fail2ban + +Module monitor fail2ban log file to show all bans for all active jails + +**Requirements:** + * fail2ban.log file MUST BE readable by netdata (A good idea is to add **create 0640 root netdata** to fail2ban conf at logrotate.d) + +It produces one chart with multiple lines (one line per jail) + +### configuration + +Sample: + +```yaml +local: + log_path: '/var/log/fail2ban.log' + conf_path: '/etc/fail2ban/jail.local' + exclude: 'dropbear apache' +``` +If no configuration is given, module will attempt to read log file at `/var/log/fail2ban.log` and conf file at `/etc/fail2ban/jail.local`. +If conf file is not found default jail is `ssh`. + +--- + +# freeradius + +Uses the `radclient` command to provide freeradius statistics. It is not recommended to run it every second. + +It produces: + +1. **Authentication counters:** + * access-accepts + * access-rejects + * auth-dropped-requests + * auth-duplicate-requests + * auth-invalid-requests + * auth-malformed-requests + * auth-unknown-types + +2. **Accounting counters:** [optional] + * accounting-requests + * accounting-responses + * acct-dropped-requests + * acct-duplicate-requests + * acct-invalid-requests + * acct-malformed-requests + * acct-unknown-types + +3. **Proxy authentication counters:** [optional] + * proxy-access-accepts + * proxy-access-rejects + * proxy-auth-dropped-requests + * proxy-auth-duplicate-requests + * proxy-auth-invalid-requests + * proxy-auth-malformed-requests + * proxy-auth-unknown-types + +4. **Proxy accounting counters:** [optional] + * proxy-accounting-requests + * proxy-accounting-responses + * proxy-acct-dropped-requests + * proxy-acct-duplicate-requests + * proxy-acct-invalid-requests + * proxy-acct-malformed-requests + * proxy-acct-unknown-typesa + + +### configuration + +Sample: + +```yaml +local: + host : 'localhost' + port : '18121' + secret : 'adminsecret' + acct : False # Freeradius accounting statistics. + proxy_auth : False # Freeradius proxy authentication statistics. + proxy_acct : False # Freeradius proxy accounting statistics. +``` + +**Freeradius server configuration:** + +The configuration for the status server is automatically created in the sites-available directory. +By default, server is enabled and can be queried from every client. +FreeRADIUS will only respond to status-server messages, if the status-server virtual server has been enabled. + +To do this, create a link from the sites-enabled directory to the status file in the sites-available directory: + * cd sites-enabled + * ln -s ../sites-available/status status + +and restart/reload your FREERADIUS server. + +--- + +# haproxy + +Module monitors frontend and backend metrics such as bytes in, bytes out, sessions current, sessions in queue current. +And health metrics such as backend servers status (server check should be used). + +Plugin can obtain data from url **OR** unix socket. + +**Requirement:** +Socket MUST be readable AND writable by netdata user. + +It produces: + +1. **Frontend** family charts + * Kilobytes in/s + * Kilobytes out/s + * Sessions current + * Sessions in queue current + +2. **Backend** family charts + * Kilobytes in/s + * Kilobytes out/s + * Sessions current + * Sessions in queue current + +3. **Health** chart + * number of failed servers for every backend (in DOWN state) + + +### configuration + +Sample: + +```yaml +via_url: + user : 'username' # ONLY IF stats auth is used + pass : 'password' # # ONLY IF stats auth is used + url : 'http://ip.address:port/url;csv;norefresh' +``` + +OR + +```yaml +via_socket: + socket : 'path/to/haproxy/sock' +``` + +If no configuration is given, module will fail to run. + +--- + # hddtemp Module monitors disk temperatures from one or more hddtemp daemons. @@ -281,6 +571,69 @@ localhost: --- +# isc_dhcpd + +Module monitor leases database to show all active leases for given pools. + +**Requirements:** + * dhcpd leases file MUST BE readable by netdata + * pools MUST BE in CIDR format + +It produces: + +1. **Pools utilization** Aggregate chart for all pools. + * utilization in percent + +2. **Total leases** + * leases (overall number of leases for all pools) + +3. **Active leases** for every pools + * leases (number of active leases in pool) + + +### configuration + +Sample: + +```yaml +local: + leases_path : '/var/lib/dhcp/dhcpd.leases' + pools : '192.168.3.0/24 192.168.4.0/24 192.168.5.0/24' +``` + +In case of python2 you need to install `py2-ipaddress` to make plugin work. +The module will not work If no configuration is given. + +--- + + +# mdstat + +Module monitor /proc/mdstat + +It produces: + +1. **Health** Number of failed disks in every array (aggregate chart). + +2. **Disks stats** + * total (number of devices array ideally would have) + * inuse (number of devices currently are in use) + +3. **Current status** + * resync in percent + * recovery in percent + * reshape in percent + * check in percent + +4. **Operation status** (if resync/recovery/reshape/check is active) + * finish in minutes + * speed in megabytes/s + +### configuration +No configuration is needed. + +--- + # memcached Memcached monitoring module. Data grabbed from [stats interface](https://github.com/memcached/memcached/wiki/Commands#stats). @@ -515,6 +868,39 @@ When no configuration file is found, module tries to parse `/var/log/nginx/acces --- +# ovpn_status_log + +Module monitor openvpn-status log file. + +**Requirements:** + + * If you are running multiple OpenVPN instances out of the same directory, MAKE SURE TO EDIT DIRECTIVES which create output files + so that multiple instances do not overwrite each other's output files. + + * Make sure NETDATA USER CAN READ openvpn-status.log + + * Update_every interval MUST MATCH interval on which OpenVPN writes operational status to log file. + +It produces: + +1. **Users** OpenVPN active users + * users + +2. **Traffic** OpenVPN overall bandwidth usage in kilobit/s + * in + * out + +### configuration + +Sample: + +```yaml +default + log_path : '/var/log/openvpn-status.log' +``` + +--- + # phpfpm This module will monitor one or more php-fpm instances depending on configuration. @@ -699,3 +1085,69 @@ Without configuration, module attempts to connect to `http://localhost:8080/mana So it will probably fail. --- + +# varnish cache + +Module uses the `varnishstat` command to provide varnish cache statistics. + +It produces: + +1. **Client metrics** + * session accepted + * session dropped + * good client requests received + +2. **All history hit rate ratio** + * cache hits in percent + * cache miss in percent + * cache hits for pass percent + +3. **Curent poll hit rate ratio** + * cache hits in percent + * cache miss in percent + * cache hits for pass percent + +4. **Thread-related metrics** (only for varnish version 4+) + * total number of threads + * threads created + * threads creation failed + * threads hit max + * length os session queue + * sessions queued for thread + +5. **Backend health** + * backend conn. success + * backend conn. not attempted + * backend conn. too many + * backend conn. failures + * backend conn. reuses + * backend conn. recycles + * backend conn. retry + * backend requests made + +6. **Memory usage** + * memory available in megabytes + * memory allocated in megabytes + +7. **Problems summary** + * session dropped + * session accept failures + * session pipe overflow + * backend conn. not attempted + * fetch failed (all causes) + * backend conn. too many + * threads hit max + * threads destroyed + * length of session queue + * HTTP header overflows + * ESI parse errors + * ESI parse warnings + +8. **Uptime** + * varnish instance uptime in seconds + +### configuration + +No configuration is needed. + +--- diff --git a/python.d/bind_rndc.chart.py b/python.d/bind_rndc.chart.py new file mode 100644 index 000000000..e11c751f5 --- /dev/null +++ b/python.d/bind_rndc.chart.py @@ -0,0 +1,180 @@ +# -*- coding: utf-8 -*- +# Description: bind rndc netdata python.d module +# Author: l2isbad + +from base import SimpleService +from re import compile, findall +from os.path import getsize, isfile, split +from os import access as is_accessible, R_OK +from subprocess import Popen + +priority = 60000 +retries = 60 +update_every = 30 + +DIRECTORIES = ['/bin/', '/usr/bin/', '/sbin/', '/usr/sbin/'] +NMS = ['requests', 'responses', 'success', 'auth_answer', 'nonauth_answer', 'nxrrset', 'failure', + 'nxdomain', 'recursion', 'duplicate', 'rejections'] +QUERIES = ['RESERVED0', 'A', 'NS', 'CNAME', 'SOA', 'PTR', 'MX', 'TXT', 'X25', 'AAAA', 'SRV', 'NAPTR', + 'A6', 'DS', 'RRSIG', 'DNSKEY', 'SPF', 'ANY', 'DLV'] + + +class Service(SimpleService): + def __init__(self, configuration=None, name=None): + SimpleService.__init__(self, configuration=configuration, name=name) + self.named_stats_path = self.configuration.get('named_stats_path', '/var/log/bind/named.stats') + self.regex_values = compile(r'([0-9]+) ([^\n]+)') + # self.options = ['Incoming Requests', 'Incoming Queries', 'Outgoing Queries', + # 'Name Server Statistics', 'Zone Maintenance Statistics', 'Resolver Statistics', + # 'Cache DB RRsets', 'Socket I/O Statistics'] + self.options = ['Name Server Statistics', 'Incoming Queries', 'Outgoing Queries'] + self.regex_options = [r'(%s(?= \+\+)) \+\+([^\+]+)' % option for option in self.options] + try: + self.rndc = [''.join([directory, 'rndc']) for directory in DIRECTORIES + if isfile(''.join([directory, 'rndc']))][0] + except IndexError: + self.rndc = False + + def check(self): + # We cant start without 'rndc' command + if not self.rndc: + self.error('Command "rndc" not found') + return False + + # We cant start if stats file is not exist or not readable by netdata user + if not is_accessible(self.named_stats_path, R_OK): + self.error('Cannot access file %s' % self.named_stats_path) + return False + + size_before = getsize(self.named_stats_path) + run_rndc = Popen([self.rndc, 'stats'], shell=False) + run_rndc.wait() + size_after = getsize(self.named_stats_path) + + # We cant start if netdata user has no permissions to run 'rndc stats' + if not run_rndc.returncode: + # 'rndc' was found, stats file is exist and readable and we can run 'rndc stats'. Lets go! + self.create_charts() + + # BIND APPEND dump on every run 'rndc stats' + # that is why stats file size can be VERY large if update_interval too small + dump_size_24hr = round(86400 / self.update_every * (int(size_after) - int(size_before)) / 1048576, 3) + + # If update_every too small we should WARN user + if self.update_every < 30: + self.info('Update_every %s is NOT recommended for use. Increase the value to > 30' % self.update_every) + + self.info('With current update_interval it will be + %s MB every 24hr. ' + 'Don\'t forget to create logrotate conf file for %s' % (dump_size_24hr, self.named_stats_path)) + + self.info('Plugin was started successfully.') + + return True + else: + self.error('Not enough permissions to run "%s stats"' % self.rndc) + return False + + def _get_raw_data(self): + """ + Run 'rndc stats' and read last dump from named.stats + :return: tuple( + file.read() obj, + named.stats file size + ) + """ + + try: + current_size = getsize(self.named_stats_path) + except OSError: + return None, None + + run_rndc = Popen([self.rndc, 'stats'], shell=False) + run_rndc.wait() + + if run_rndc.returncode: + return None, None + + try: + with open(self.named_stats_path) as bind_rndc: + bind_rndc.seek(current_size) + result = bind_rndc.read() + except OSError: + return None, None + else: + return result, current_size + + def _get_data(self): + """ + Parse data from _get_raw_data() + :return: dict + """ + + raw_data, size = self._get_raw_data() + + if raw_data is None: + return None + + rndc_stats = dict() + + # Result: dict. + # topic = Cache DB RRsets; body = A 178303 NS 86790 ... ; desc = A; value = 178303 + # {'Cache DB RRsets': [('A', 178303), ('NS', 286790), ...], + # {Incoming Queries': [('RESERVED0', 8), ('A', 4557317680), ...], + # ...... + for regex in self.regex_options: + rndc_stats.update({topic: [(desc, int(value)) for value, desc in self.regex_values.findall(body)] + for topic, body in findall(regex, raw_data)}) + + nms = dict(rndc_stats.get('Name Server Statistics', [])) + + inc_queries = {'i' + k: 0 for k in QUERIES} + inc_queries.update({'i' + k: v for k, v in rndc_stats.get('Incoming Queries', [])}) + out_queries = {'o' + k: 0 for k in QUERIES} + out_queries.update({'o' + k: v for k, v in rndc_stats.get('Outgoing Queries', [])}) + + to_netdata = dict() + to_netdata['requests'] = sum([v for k, v in nms.items() if 'request' in k and 'received' in k]) + to_netdata['responses'] = sum([v for k, v in nms.items() if 'responses' in k and 'sent' in k]) + to_netdata['success'] = nms.get('queries resulted in successful answer', 0) + to_netdata['auth_answer'] = nms.get('queries resulted in authoritative answer', 0) + to_netdata['nonauth_answer'] = nms.get('queries resulted in non authoritative answer', 0) + to_netdata['nxrrset'] = nms.get('queries resulted in nxrrset', 0) + to_netdata['failure'] = sum([nms.get('queries resulted in SERVFAIL', 0), nms.get('other query failures', 0)]) + to_netdata['nxdomain'] = nms.get('queries resulted in NXDOMAIN', 0) + to_netdata['recursion'] = nms.get('queries caused recursion', 0) + to_netdata['duplicate'] = nms.get('duplicate queries received', 0) + to_netdata['rejections'] = nms.get('recursive queries rejected', 0) + to_netdata['stats_size'] = size + + to_netdata.update(inc_queries) + to_netdata.update(out_queries) + return to_netdata + + def create_charts(self): + self.order = ['stats_size', 'bind_stats', 'incoming_q', 'outgoing_q'] + self.definitions = { + 'bind_stats': { + 'options': [None, 'Name Server Statistics', 'stats', 'Name Server Statistics', 'bind_rndc.stats', 'line'], + 'lines': [ + ]}, + 'incoming_q': { + 'options': [None, 'Incoming queries', 'queries','Incoming queries', 'bind_rndc.incq', 'line'], + 'lines': [ + ]}, + 'outgoing_q': { + 'options': [None, 'Outgoing queries', 'queries','Outgoing queries', 'bind_rndc.outq', 'line'], + 'lines': [ + ]}, + 'stats_size': { + 'options': [None, '%s file size' % split(self.named_stats_path)[1].capitalize(), 'megabytes', + '%s size' % split(self.named_stats_path)[1].capitalize(), 'bind_rndc.size', 'line'], + 'lines': [ + ["stats_size", None, "absolute", 1, 1048576] + ]} + } + for elem in QUERIES: + self.definitions['incoming_q']['lines'].append(['i' + elem, elem, 'incremental']) + self.definitions['outgoing_q']['lines'].append(['o' + elem, elem, 'incremental']) + + for elem in NMS: + self.definitions['bind_stats']['lines'].append([elem, None, 'incremental']) diff --git a/python.d/cpufreq.chart.py b/python.d/cpufreq.chart.py index a9de5cedd..c761aae88 100644 --- a/python.d/cpufreq.chart.py +++ b/python.d/cpufreq.chart.py @@ -1,8 +1,10 @@ # -*- coding: utf-8 -*- # Description: cpufreq netdata python.d module -# Author: Pawel Krupa (paulfantom) +# Author: Pawel Krupa (paulfantom) and Steven Noonan (tycho) +import glob import os +import time from base import SimpleService # default module values (can be overridden per job in `config`) @@ -18,29 +20,53 @@ CHARTS = { ]} } - class Service(SimpleService): def __init__(self, configuration=None, name=None): prefix = os.getenv('NETDATA_HOST_PREFIX', "") if prefix.endswith('/'): prefix = prefix[:-1] self.sys_dir = prefix + "/sys/devices" - self.filename = "scaling_cur_freq" SimpleService.__init__(self, configuration=configuration, name=name) self.order = ORDER self.definitions = CHARTS self._orig_name = "" self.assignment = {} - self.paths = [] + self.accurate_exists = True + self.accurate_last = {} def _get_data(self): - raw = {} - for path in self.paths: - with open(path, 'r') as f: - raw[path] = f.read() data = {} - for path in self.paths: - data[self.assignment[path]] = raw[path] + + if self.accurate_exists: + elapsed = time.time() - self.timetable['last'] + + accurate_ok = True + + for name, paths in self.assignment.items(): + last = self.accurate_last[name] + current = 0 + for line in open(paths['accurate'], 'r'): + line = list(map(int, line.split())) + current += (line[0] * line[1]) / 100 + delta = current - last + data[name] = delta + self.accurate_last[name] = current + if delta == 0 or abs(delta) > 1e7: + # Delta is either too large or nonexistent, fall back to + # less accurate reading. This can happen if we switch + # to/from the 'schedutil' governor, which doesn't report + # stats. + accurate_ok = False + + if accurate_ok: + return data + else: + self.alert("accurate method failed, falling back") + + + for name, paths in self.assignment.items(): + data[name] = open(paths['inaccurate'], 'r').read() + return data def check(self): @@ -51,23 +77,30 @@ class Service(SimpleService): self._orig_name = self.chart_name - for dirpath, _, filenames in os.walk(self.sys_dir): - if self.filename in filenames: - self.paths.append(dirpath + "/" + self.filename) - - if len(self.paths) == 0: - self.error("cannot find", self.filename) + for path in glob.glob(self.sys_dir + '/system/cpu/cpu*/cpufreq/stats/time_in_state'): + path_elem = path.split('/') + cpu = path_elem[-4] + if cpu not in self.assignment: + self.assignment[cpu] = {} + self.assignment[cpu]['accurate'] = path + self.accurate_last[cpu] = 0 + + if len(self.assignment) == 0: + self.accurate_exists = False + + for path in glob.glob(self.sys_dir + '/system/cpu/cpu*/cpufreq/scaling_cur_freq'): + path_elem = path.split('/') + cpu = path_elem[-3] + if cpu not in self.assignment: + self.assignment[cpu] = {} + self.assignment[cpu]['inaccurate'] = path + + if len(self.assignment) == 0: + self.error("couldn't find a method to read cpufreq statistics") return False - self.paths.sort() - i = 0 - for path in self.paths: - self.assignment[path] = "cpu" + str(i) - i += 1 - - for name in self.assignment: - dim = self.assignment[name] - self.definitions[ORDER[0]]['lines'].append([dim, dim, 'absolute', 1, 1000]) + for name in self.assignment.keys(): + self.definitions[ORDER[0]]['lines'].append([name, name, 'absolute', 1, 1000]) return True diff --git a/python.d/cpuidle.chart.py b/python.d/cpuidle.chart.py new file mode 100644 index 000000000..f7199aebd --- /dev/null +++ b/python.d/cpuidle.chart.py @@ -0,0 +1,143 @@ +# -*- coding: utf-8 -*- +# Description: cpuidle netdata python.d module +# Author: Steven Noonan (tycho) + +import glob +import os +import platform +import time +from base import SimpleService + +import ctypes +syscall = ctypes.CDLL('libc.so.6').syscall + +# default module values (can be overridden per job in `config`) +# update_every = 2 + +class Service(SimpleService): + def __init__(self, configuration=None, name=None): + prefix = os.getenv('NETDATA_HOST_PREFIX', "") + if prefix.endswith('/'): + prefix = prefix[:-1] + self.sys_dir = prefix + "/sys/devices/system/cpu" + self.schedstat_path = prefix + "/proc/schedstat" + SimpleService.__init__(self, configuration=configuration, name=name) + self.order = [] + self.definitions = {} + self._orig_name = "" + self.assignment = {} + + def __gettid(self): + # This is horrendous. We need the *thread id* (not the *process id*), + # but there's no Python standard library way of doing that. If you need + # to enable this module on a non-x86 machine type, you'll have to find + # the Linux syscall number for gettid() and add it to the dictionary + # below. + syscalls = { + 'i386': 224, + 'x86_64': 186, + } + if platform.machine() not in syscalls: + return None + tid = syscall(syscalls[platform.machine()]) + return tid + + def __wake_cpus(self): + # Requires Python 3.3+. This will "tickle" each CPU to force it to + # update its idle counters. + if hasattr(os, 'sched_setaffinity'): + pid = self.__gettid() + save_affinity = os.sched_getaffinity(pid) + for idx in range(0, len(self.assignment)): + os.sched_setaffinity(pid, [idx]) + os.sched_getaffinity(pid) + os.sched_setaffinity(pid, save_affinity) + + def __read_schedstat(self): + cpus = {} + for line in open(self.schedstat_path, 'r'): + if not line.startswith('cpu'): + continue + line = line.rstrip().split() + cpu = line[0] + active_time = line[7] + cpus[cpu] = int(active_time) // 1000 + return cpus + + def _get_data(self): + results = {} + + # This line is critical for the stats to update. If we don't "tickle" + # all the CPUs, then all the counters stop counting. + self.__wake_cpus() + + # Use the kernel scheduler stats to determine how much time was spent + # in C0 (active). + schedstat = self.__read_schedstat() + + for cpu, metrics in self.assignment.items(): + update_time = schedstat[cpu] + results[cpu + '_active_time'] = update_time + + for metric, path in metrics.items(): + residency = int(open(path, 'r').read()) + results[metric] = residency + + return results + + def check(self): + if self.__gettid() is None: + self.error("Cannot get thread ID. Stats would be completely broken.") + return False + + self._orig_name = self.chart_name + + for path in sorted(glob.glob(self.sys_dir + '/cpu*/cpuidle/state*/name')): + # ['', 'sys', 'devices', 'system', 'cpu', 'cpu0', 'cpuidle', 'state3', 'name'] + path_elem = path.split('/') + cpu = path_elem[-4] + state = path_elem[-2] + statename = open(path, 'rt').read().rstrip() + + orderid = '%s_cpuidle' % (cpu,) + if orderid not in self.definitions: + self.order.append(orderid) + active_name = '%s_active_time' % (cpu,) + self.definitions[orderid] = { + 'options': [None, 'C-state residency', 'time%', 'cpuidle', None, 'stacked'], + 'lines': [ + [active_name, 'C0 (active)', 'percentage-of-incremental-row', 1, 1], + ], + } + self.assignment[cpu] = {} + + defid = '%s_%s_time' % (orderid, state) + + self.definitions[orderid]['lines'].append( + [defid, statename, 'percentage-of-incremental-row', 1, 1] + ) + + self.assignment[cpu][defid] = '/'.join(path_elem[:-1] + ['time']) + + # Sort order by kernel-specified CPU index + self.order.sort(key=lambda x: int(x.split('_')[0][3:])) + + if len(self.definitions) == 0: + self.error("couldn't find cstate stats") + return False + + return True + + def create(self): + self.chart_name = "cpu" + status = SimpleService.create(self) + self.chart_name = self._orig_name + return status + + def update(self, interval): + self.chart_name = "cpu" + status = SimpleService.update(self, interval=interval) + self.chart_name = self._orig_name + return status + +# vim: set ts=4 sts=4 sw=4 et: diff --git a/python.d/dovecot.chart.py b/python.d/dovecot.chart.py index 586478cda..60e8bf6ef 100644 --- a/python.d/dovecot.chart.py +++ b/python.d/dovecot.chart.py @@ -10,74 +10,78 @@ priority = 60000 retries = 60 # charts order (can be overridden if you want less charts, or different order) -ORDER = ['sessions', 'commands', +ORDER = ['sessions', 'logins', 'commands', 'faults', 'context_switches', - 'disk', 'bytes', 'syscalls', + 'io', 'net', 'syscalls', 'lookup', 'cache', 'auth', 'auth_cache'] CHARTS = { 'sessions': { - 'options': [None, "logins and sessions", 'number', 'IMAP', 'dovecot.sessions', 'line'], + 'options': [None, "Dovecot Active Sessions", 'number', 'sessions', 'dovecot.sessions', 'line'], 'lines': [ - ['num_logins', 'logins', 'absolute'], ['num_connected_sessions', 'active sessions', 'absolute'] ]}, + 'logins': { + 'options': [None, "Dovecot Logins", 'number', 'logins', 'dovecot.logins', 'line'], + 'lines': [ + ['num_logins', 'logins', 'absolute'] + ]}, 'commands': { - 'options': [None, "commands", "commands", 'IMAP', 'dovecot.commands', 'line'], + 'options': [None, "Dovecot Commands", "commands", 'commands', 'dovecot.commands', 'line'], 'lines': [ ['num_cmds', 'commands', 'absolute'] ]}, 'faults': { - 'options': [None, "faults", "faults", 'Faults', 'dovecot.faults', 'line'], + 'options': [None, "Dovecot Page Faults", "faults", 'page 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'], + 'options': [None, "Dovecot 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.disk', 'line'], + 'io': { + 'options': [None, "Dovecot Disk I/O", 'kilobytes/s', 'disk', 'dovecot.io', 'area'], 'lines': [ - ['disk_input', 'read', 'incremental'], - ['disk_output', 'write', 'incremental'] + ['disk_input', 'read', 'incremental', 1, 1024], + ['disk_output', 'write', 'incremental', -1, 1024] ]}, - 'bytes': { - 'options': [None, "bytes", 'bytes/s', 'Reads and Writes', 'dovecot.bytes', 'line'], + 'net': { + 'options': [None, "Dovecot Network Bandwidth", 'kilobits/s', 'network', 'dovecot.net', 'area'], 'lines': [ - ['read_bytes', 'read', 'incremental'], - ['write_bytes', 'write', 'incremental'] + ['read_bytes', 'read', 'incremental', 8, 1024], + ['write_bytes', 'write', 'incremental', -8, 1024] ]}, 'syscalls': { - 'options': [None, "number of syscalls", 'syscalls/s', 'Reads and Writes', 'dovecot.syscalls', 'line'], + 'options': [None, "Dovecot Number of SysCalls", 'syscalls/s', 'system', 'dovecot.syscalls', 'line'], 'lines': [ ['read_count', 'read', 'incremental'], ['write_count', 'write', 'incremental'] ]}, 'lookup': { - 'options': [None, "lookups", 'number/s', 'Mail', 'dovecot.lookup', 'line'], + 'options': [None, "Dovecot Lookups", 'number/s', 'lookups', 'dovecot.lookup', 'stacked'], 'lines': [ ['mail_lookup_path', 'path', 'incremental'], ['mail_lookup_attr', 'attr', 'incremental'] ]}, 'cache': { - 'options': [None, "hits", 'hits/s', 'Mail', 'dovecot.cache', 'line'], + 'options': [None, "Dovecot Cache Hits", 'hits/s', 'cache', 'dovecot.cache', 'line'], 'lines': [ ['mail_cache_hits', 'hits', 'incremental'] ]}, 'auth': { - 'options': [None, "attempts", 'attempts', 'Authentication', 'dovecot.auth', 'stacked'], + 'options': [None, "Dovecot Authentications", 'attempts', 'logins', 'dovecot.auth', 'stacked'], 'lines': [ - ['auth_successes', 'success', 'absolute'], - ['auth_failures', 'failure', 'absolute'] + ['auth_successes', 'ok', 'absolute'], + ['auth_failures', 'failed', 'absolute'] ]}, 'auth_cache': { - 'options': [None, "cache", 'number', 'Authentication', 'dovecot.auth_cache', 'stacked'], + 'options': [None, "Dovecot Authentication Cache", 'number', 'cache', 'dovecot.auth_cache', 'stacked'], 'lines': [ ['auth_cache_hits', 'hit', 'absolute'], ['auth_cache_misses', 'miss', 'absolute'] @@ -106,6 +110,10 @@ class Service(SocketService): except (ValueError, AttributeError): return None + if raw is None: + self.debug("dovecot returned no data") + return None + data = raw.split('\n')[:2] desc = data[0].split('\t') vals = data[1].split('\t') diff --git a/python.d/elasticsearch.chart.py b/python.d/elasticsearch.chart.py new file mode 100644 index 000000000..ff841f17c --- /dev/null +++ b/python.d/elasticsearch.chart.py @@ -0,0 +1,402 @@ +# -*- coding: utf-8 -*- +# Description: elastic search node stats netdata python.d module +# Author: l2isbad + +from base import UrlService +from requests import get +from socket import gethostbyname +try: + from queue import Queue +except ImportError: + from Queue import Queue +from threading import Thread + +# default module values (can be overridden per job in `config`) +# update_every = 2 +update_every = 5 +priority = 60000 +retries = 60 + +# charts order (can be overridden if you want less charts, or different order) +ORDER = ['search_perf_total', 'search_perf_time', 'search_latency', 'index_perf_total', 'index_perf_time', + 'index_latency', 'jvm_mem_heap', 'jvm_gc_count', 'jvm_gc_time', 'host_metrics_file_descriptors', + 'host_metrics_http', 'host_metrics_transport', 'thread_pool_qr', 'fdata_cache', 'fdata_ev_tr', + 'cluster_health_status', 'cluster_health_nodes', 'cluster_health_shards', 'cluster_stats_nodes', + 'cluster_stats_query_cache', 'cluster_stats_docs', 'cluster_stats_store', 'cluster_stats_indices_shards'] + +CHARTS = { + 'search_perf_total': { + 'options': [None, 'Number of queries, fetches', 'queries', 'Search performance', 'es.search_query', 'stacked'], + 'lines': [ + ['query_total', 'search_total', 'incremental'], + ['fetch_total', 'fetch_total', 'incremental'], + ['query_current', 'search_current', 'absolute'], + ['fetch_current', 'fetch_current', 'absolute'] + ]}, + 'search_perf_time': { + 'options': [None, 'Time spent on queries, fetches', 'seconds', 'Search performance', 'es.search_time', 'stacked'], + 'lines': [ + ['query_time_in_millis', 'query', 'incremental', 1, 1000], + ['fetch_time_in_millis', 'fetch', 'incremental', 1, 1000] + ]}, + 'search_latency': { + 'options': [None, 'Query and fetch latency', 'ms', 'Search performance', 'es.search_latency', 'stacked'], + 'lines': [ + ['query_latency', 'query', 'absolute', 1, 1000], + ['fetch_latency', 'fetch', 'absolute', 1, 1000] + ]}, + 'index_perf_total': { + 'options': [None, 'Number of documents indexed, index refreshes, flushes', 'documents/indexes', + 'Indexing performance', 'es.index_doc', 'stacked'], + 'lines': [ + ['indexing_index_total', 'indexed', 'incremental'], + ['refresh_total', 'refreshes', 'incremental'], + ['flush_total', 'flushes', 'incremental'], + ['indexing_index_current', 'indexed_current', 'absolute'], + ]}, + 'index_perf_time': { + 'options': [None, 'Time spent on indexing, refreshing, flushing', 'seconds', 'Indexing performance', + 'es.search_time', 'stacked'], + 'lines': [ + ['indexing_index_time_in_millis', 'indexing', 'incremental', 1, 1000], + ['refresh_total_time_in_millis', 'refreshing', 'incremental', 1, 1000], + ['flush_total_time_in_millis', 'flushing', 'incremental', 1, 1000] + ]}, + 'index_latency': { + 'options': [None, 'Indexing and flushing latency', 'ms', 'Indexing performance', + 'es.index_latency', 'stacked'], + 'lines': [ + ['indexing_latency', 'indexing', 'absolute', 1, 1000], + ['flushing_latency', 'flushing', 'absolute', 1, 1000] + ]}, + 'jvm_mem_heap': { + 'options': [None, 'JVM heap currently in use/committed', 'percent/MB', 'Memory usage and gc', + 'es.jvm_heap', 'area'], + 'lines': [ + ['jvm_heap_percent', 'inuse', 'absolute'], + ['jvm_heap_commit', 'commit', 'absolute', -1, 1048576] + ]}, + 'jvm_gc_count': { + 'options': [None, 'Count of garbage collections', 'counts', 'Memory usage and gc', 'es.gc_count', 'stacked'], + 'lines': [ + ['young_collection_count', 'young', 'incremental'], + ['old_collection_count', 'old', 'incremental'] + ]}, + 'jvm_gc_time': { + 'options': [None, 'Time spent on garbage collections', 'ms', 'Memory usage and gc', 'es.gc_time', 'stacked'], + 'lines': [ + ['young_collection_time_in_millis', 'young', 'incremental'], + ['old_collection_time_in_millis', 'old', 'incremental'] + ]}, + 'thread_pool_qr': { + 'options': [None, 'Number of queued/rejected threads in thread pool', 'threads', 'Queues and rejections', + 'es.qr', 'stacked'], + 'lines': [ + ['bulk_queue', 'bulk_queue', 'absolute'], + ['index_queue', 'index_queue', 'absolute'], + ['search_queue', 'search_queue', 'absolute'], + ['merge_queue', 'merge_queue', 'absolute'], + ['bulk_rejected', 'bulk_rej', 'absolute'], + ['index_rejected', 'index_rej', 'absolute'], + ['search_rejected', 'search_rej', 'absolute'], + ['merge_rejected', 'merge_rej', 'absolute'] + ]}, + 'fdata_cache': { + 'options': [None, 'Fielddata cache size', 'MB', 'Fielddata cache', 'es.fdata_cache', 'line'], + 'lines': [ + ['index_fdata_mem', 'mem_size', 'absolute', 1, 1048576] + ]}, + 'fdata_ev_tr': { + 'options': [None, 'Fielddata evictions and circuit breaker tripped count', 'number of events', + 'Fielddata cache', 'es.fdata_ev_tr', 'line'], + 'lines': [ + ['index_fdata_evic', 'evictions', 'incremental'], + ['breakers_fdata_trip', 'tripped', 'incremental'] + ]}, + 'cluster_health_nodes': { + 'options': [None, 'Nodes and tasks statistics', 'units', 'Cluster health API', + 'es.cluster_health', 'stacked'], + 'lines': [ + ['health_number_of_nodes', 'nodes', 'absolute'], + ['health_number_of_data_nodes', 'data_nodes', 'absolute'], + ['health_number_of_pending_tasks', 'pending_tasks', 'absolute'], + ['health_number_of_in_flight_fetch', 'inflight_fetch', 'absolute'] + ]}, + 'cluster_health_status': { + 'options': [None, 'Cluster status', 'status', 'Cluster health API', + 'es.cluster_health_status', 'area'], + 'lines': [ + ['status_green', 'green', 'absolute'], + ['status_red', 'red', 'absolute'], + ['status_foo1', None, 'absolute'], + ['status_foo2', None, 'absolute'], + ['status_foo3', None, 'absolute'], + ['status_yellow', 'yellow', 'absolute'] + ]}, + 'cluster_health_shards': { + 'options': [None, 'Shards statistics', 'shards', 'Cluster health API', + 'es.cluster_health_sharts', 'stacked'], + 'lines': [ + ['health_active_shards', 'active_shards', 'absolute'], + ['health_relocating_shards', 'relocating_shards', 'absolute'], + ['health_unassigned_shards', 'unassigned', 'absolute'], + ['health_delayed_unassigned_shards', 'delayed_unassigned', 'absolute'], + ['health_initializing_shards', 'initializing', 'absolute'], + ['health_active_shards_percent_as_number', 'active_percent', 'absolute'] + ]}, + 'cluster_stats_nodes': { + 'options': [None, 'Nodes statistics', 'nodes', 'Cluster stats API', + 'es.cluster_stats_nodes', 'stacked'], + 'lines': [ + ['count_data_only', 'data_only', 'absolute'], + ['count_master_data', 'master_data', 'absolute'], + ['count_total', 'total', 'absolute'], + ['count_master_only', 'master_only', 'absolute'], + ['count_client', 'client', 'absolute'] + ]}, + 'cluster_stats_query_cache': { + 'options': [None, 'Query cache statistics', 'queries', 'Cluster stats API', + 'es.cluster_stats_query_cache', 'stacked'], + 'lines': [ + ['query_cache_hit_count', 'hit', 'incremental'], + ['query_cache_miss_count', 'miss', 'incremental'] + ]}, + 'cluster_stats_docs': { + 'options': [None, 'Docs statistics', 'count', 'Cluster stats API', + 'es.cluster_stats_docs', 'line'], + 'lines': [ + ['docs_count', 'docs', 'absolute'] + ]}, + 'cluster_stats_store': { + 'options': [None, 'Store statistics', 'MB', 'Cluster stats API', + 'es.cluster_stats_store', 'line'], + 'lines': [ + ['store_size_in_bytes', 'size', 'absolute', 1, 1048567] + ]}, + 'cluster_stats_indices_shards': { + 'options': [None, 'Indices and shards statistics', 'count', 'Cluster stats API', + 'es.cluster_stats_ind_sha', 'stacked'], + 'lines': [ + ['indices_count', 'indices', 'absolute'], + ['shards_total', 'shards', 'absolute'] + ]}, + 'host_metrics_transport': { + 'options': [None, 'Cluster communication transport metrics', 'kbit/s', 'Host metrics', + 'es.host_metrics_transport', 'area'], + 'lines': [ + ['transport_rx_size_in_bytes', 'in', 'incremental', 8, 1000], + ['transport_tx_size_in_bytes', 'out', 'incremental', -8, 1000] + ]}, + 'host_metrics_file_descriptors': { + 'options': [None, 'Available file descriptors in percent', 'percent', 'Host metrics', + 'es.host_metrics_descriptors', 'area'], + 'lines': [ + ['file_descriptors_used', 'used', 'absolute', 1, 10] + ]}, + 'host_metrics_http': { + 'options': [None, 'Opened HTTP connections', 'connections', 'Host metrics', + 'es.host_metrics_http', 'line'], + 'lines': [ + ['http_current_open', 'opened', 'absolute', 1, 1] + ]} +} + + +class Service(UrlService): + def __init__(self, configuration=None, name=None): + UrlService.__init__(self, configuration=configuration, name=name) + self.order = ORDER + self.definitions = CHARTS + self.host = self.configuration.get('host') + self.port = self.configuration.get('port') + self.user = self.configuration.get('user') + self.password = self.configuration.get('pass') + self.latency = dict() + + def check(self): + # We can't start if <host> AND <port> not specified + if not all([self.host, self.port]): + return False + + # It as a bad idea to use hostname. + # Hostname -> ipaddress + try: + self.host = gethostbyname(self.host) + except Exception as e: + self.error(str(e)) + return False + + # HTTP Auth? NOT TESTED + self.auth = self.user and self.password + + # Create URL for every Elasticsearch API + url_node_stats = 'http://%s:%s/_nodes/_local/stats' % (self.host, self.port) + url_cluster_health = 'http://%s:%s/_cluster/health' % (self.host, self.port) + url_cluster_stats = 'http://%s:%s/_cluster/stats' % (self.host, self.port) + + # Create list of enabled API calls + user_choice = [bool(self.configuration.get('node_stats', True)), + bool(self.configuration.get('cluster_health', True)), + bool(self.configuration.get('cluster_stats', True))] + + avail_methods = [(self._get_node_stats, url_node_stats), + (self._get_cluster_health, url_cluster_health), + (self._get_cluster_stats, url_cluster_stats)] + + # Remove disabled API calls from 'avail methods' + self.methods = [avail_methods[_] for _ in range(len(avail_methods)) if user_choice[_]] + + # Run _get_data for ALL active API calls. + api_result = {} + for method in self.methods: + api_result[method[1]] = (bool(self._get_raw_data(method[1]))) + + # We can start ONLY if all active API calls returned NOT None + if not all(api_result.values()): + self.error('Plugin could not get data from all APIs') + self.error('%s' % api_result) + return False + else: + self.info('%s' % api_result) + self.info('Plugin was started successfully') + + return True + + def _get_raw_data(self, url): + try: + if not self.auth: + raw_data = get(url) + else: + raw_data = get(url, auth=(self.user, self.password)) + except Exception: + return None + + return raw_data + + def _get_data(self): + threads = list() + queue = Queue() + result = dict() + + for method in self.methods: + th = Thread(target=method[0], args=(queue, method[1])) + th.start() + threads.append(th) + + for thread in threads: + thread.join() + result.update(queue.get()) + + return result or None + + def _get_cluster_health(self, queue, url): + """ + Format data received from http request + :return: dict + """ + + data = self._get_raw_data(url) + + if not data: + queue.put({}) + else: + data = data.json() + + to_netdata = dict() + to_netdata.update(update_key('health', data)) + to_netdata.update({'status_green': 0, 'status_red': 0, 'status_yellow': 0, + 'status_foo1': 0, 'status_foo2': 0, 'status_foo3': 0}) + to_netdata[''.join(['status_', to_netdata.get('health_status', '')])] = 1 + + queue.put(to_netdata) + + def _get_cluster_stats(self, queue, url): + """ + Format data received from http request + :return: dict + """ + + data = self._get_raw_data(url) + + if not data: + queue.put({}) + else: + data = data.json() + + to_netdata = dict() + to_netdata.update(update_key('count', data['nodes']['count'])) + to_netdata.update(update_key('query_cache', data['indices']['query_cache'])) + to_netdata.update(update_key('docs', data['indices']['docs'])) + to_netdata.update(update_key('store', data['indices']['store'])) + to_netdata['indices_count'] = data['indices']['count'] + to_netdata['shards_total'] = data['indices']['shards']['total'] + + queue.put(to_netdata) + + def _get_node_stats(self, queue, url): + """ + Format data received from http request + :return: dict + """ + + data = self._get_raw_data(url) + + if not data: + queue.put({}) + else: + data = data.json() + node = list(data['nodes'].keys())[0] + to_netdata = dict() + # Search performance metrics + to_netdata.update(data['nodes'][node]['indices']['search']) + to_netdata['query_latency'] = self.find_avg(to_netdata['query_total'], + to_netdata['query_time_in_millis'], 'query_latency') + to_netdata['fetch_latency'] = self.find_avg(to_netdata['fetch_total'], + to_netdata['fetch_time_in_millis'], 'fetch_latency') + + # Indexing performance metrics + for key in ['indexing', 'refresh', 'flush']: + to_netdata.update(update_key(key, data['nodes'][node]['indices'].get(key, {}))) + to_netdata['indexing_latency'] = self.find_avg(to_netdata['indexing_index_total'], + to_netdata['indexing_index_time_in_millis'], 'index_latency') + to_netdata['flushing_latency'] = self.find_avg(to_netdata['flush_total'], + to_netdata['flush_total_time_in_millis'], 'flush_latency') + # Memory usage and garbage collection + to_netdata.update(update_key('young', data['nodes'][node]['jvm']['gc']['collectors']['young'])) + to_netdata.update(update_key('old', data['nodes'][node]['jvm']['gc']['collectors']['old'])) + to_netdata['jvm_heap_percent'] = data['nodes'][node]['jvm']['mem']['heap_used_percent'] + to_netdata['jvm_heap_commit'] = data['nodes'][node]['jvm']['mem']['heap_committed_in_bytes'] + + # Thread pool queues and rejections + for key in ['bulk', 'index', 'search', 'merge']: + to_netdata.update(update_key(key, data['nodes'][node]['thread_pool'].get(key, {}))) + + # Fielddata cache + to_netdata['index_fdata_mem'] = data['nodes'][node]['indices']['fielddata']['memory_size_in_bytes'] + to_netdata['index_fdata_evic'] = data['nodes'][node]['indices']['fielddata']['evictions'] + to_netdata['breakers_fdata_trip'] = data['nodes'][node]['breakers']['fielddata']['tripped'] + + # Host metrics + to_netdata.update(update_key('http', data['nodes'][node]['http'])) + to_netdata.update(update_key('transport', data['nodes'][node]['transport'])) + to_netdata['file_descriptors_used'] = round(float(data['nodes'][node]['process']['open_file_descriptors']) + / data['nodes'][node]['process']['max_file_descriptors'] * 1000) + + queue.put(to_netdata) + + def find_avg(self, value1, value2, key): + if key not in self.latency: + self.latency.update({key: [value1, value2]}) + return 0 + else: + if not self.latency[key][0] == value1: + latency = round(float(value2 - self.latency[key][1]) / float(value1 - self.latency[key][0]) * 1000) + self.latency.update({key: [value1, value2]}) + return latency + else: + self.latency.update({key: [value1, value2]}) + return 0 + + +def update_key(string, dictionary): + return {'_'.join([string, k]): v for k, v in dictionary.items()} diff --git a/python.d/fail2ban.chart.py b/python.d/fail2ban.chart.py new file mode 100644 index 000000000..2d80282c6 --- /dev/null +++ b/python.d/fail2ban.chart.py @@ -0,0 +1,91 @@ +# -*- coding: utf-8 -*- +# Description: fail2ban log netdata python.d module +# Author: l2isbad + +from base import LogService +from re import compile +try: + from itertools import filterfalse +except ImportError: + from itertools import ifilterfalse as filterfalse +from os import access as is_accessible, R_OK + +priority = 60000 +retries = 60 +regex = compile(r'([A-Za-z-]+\]) enabled = ([a-z]+)') + +ORDER = ['jails_group'] + + +class Service(LogService): + def __init__(self, configuration=None, name=None): + LogService.__init__(self, configuration=configuration, name=name) + self.order = ORDER + self.log_path = self.configuration.get('log_path', '/var/log/fail2ban.log') + self.conf_path = self.configuration.get('conf_path', '/etc/fail2ban/jail.local') + self.default_jails = ['ssh'] + try: + self.exclude = self.configuration['exclude'].split() + except (KeyError, AttributeError): + self.exclude = [] + + + def _get_data(self): + """ + Parse new log lines + :return: dict + """ + + # If _get_raw_data returns empty list (no new lines in log file) we will send to Netdata this + self.data = {jail: 0 for jail in self.jails_list} + + try: + raw = self._get_raw_data() + if raw is None: + return None + elif not raw: + return self.data + except (ValueError, AttributeError): + return None + + # Fail2ban logs looks like + # 2016-12-25 12:36:04,711 fail2ban.actions[2455]: WARNING [ssh] Ban 178.156.32.231 + self.data = dict( + zip( + self.jails_list, + [len(list(filterfalse(lambda line: (jail + '] Ban') not in line, raw))) for jail in self.jails_list] + )) + + return self.data + + def check(self): + + # Check "log_path" is accessible. + # If NOT STOP plugin + if not is_accessible(self.log_path, R_OK): + self.error('Cannot access file %s' % (self.log_path)) + return False + + # Check "conf_path" is accessible. + # If "conf_path" is accesible try to parse it to find enabled jails + if is_accessible(self.conf_path, R_OK): + with open(self.conf_path, 'rt') as jails_conf: + jails_list = regex.findall(' '.join(jails_conf.read().split())) + self.jails_list = [jail[:-1] for jail, status in jails_list if status == 'true'] + else: + self.jails_list = [] + self.error('Cannot access jail.local file %s.' % (self.conf_path)) + + # If for some reason parse failed we still can START with default jails_list. + self.jails_list = [jail for jail in self.jails_list if jail not in self.exclude]\ + if self.jails_list else self.default_jails + self.create_dimensions() + self.info('Plugin succefully started. Jails: %s' % (self.jails_list)) + return True + + def create_dimensions(self): + self.definitions = {'jails_group': + {'options': + [None, "Jails ban statistics", "bans/s", 'Jails', 'jail.ban', 'line'], 'lines': []}} + for jail in self.jails_list: + self.definitions['jails_group']['lines'].append([jail, jail, 'absolute']) diff --git a/python.d/freeradius.chart.py b/python.d/freeradius.chart.py new file mode 100644 index 000000000..2ac280f0e --- /dev/null +++ b/python.d/freeradius.chart.py @@ -0,0 +1,114 @@ +# -*- coding: utf-8 -*- +# Description: freeradius netdata python.d module +# Author: l2isbad + +from base import SimpleService +from os.path import isfile +from re import findall +from subprocess import Popen, PIPE + +# default module values (can be overridden per job in `config`) +priority = 60000 +retries = 60 +update_every = 15 +directories = ['/bin/', '/usr/bin/', '/sbin/', '/usr/sbin/'] + +# charts order (can be overridden if you want less charts, or different order) +ORDER = ['authentication', 'accounting', 'proxy-auth', 'proxy-acct'] + +CHARTS = { + 'authentication': { + 'options': [None, "Authentication", "packets/s", 'Authentication', 'freerad.auth', 'line'], + 'lines': [ + ['access-accepts', None, 'incremental'], ['access-rejects', None, 'incremental'], + ['auth-dropped-requests', None, 'incremental'], ['auth-duplicate-requests', None, 'incremental'], + ['auth-invalid-requests', None, 'incremental'], ['auth-malformed-requests', None, 'incremental'], + ['auth-unknown-types', None, 'incremental'] + ]}, + 'accounting': { + 'options': [None, "Accounting", "packets/s", 'Accounting', 'freerad.acct', 'line'], + 'lines': [ + ['accounting-requests', None, 'incremental'], ['accounting-responses', None, 'incremental'], + ['acct-dropped-requests', None, 'incremental'], ['acct-duplicate-requests', None, 'incremental'], + ['acct-invalid-requests', None, 'incremental'], ['acct-malformed-requests', None, 'incremental'], + ['acct-unknown-types', None, 'incremental'] + ]}, + 'proxy-auth': { + 'options': [None, "Proxy Authentication", "packets/s", 'Authentication', 'freerad.proxy.auth', 'line'], + 'lines': [ + ['proxy-access-accepts', None, 'incremental'], ['proxy-access-rejects', None, 'incremental'], + ['proxy-auth-dropped-requests', None, 'incremental'], ['proxy-auth-duplicate-requests', None, 'incremental'], + ['proxy-auth-invalid-requests', None, 'incremental'], ['proxy-auth-malformed-requests', None, 'incremental'], + ['proxy-auth-unknown-types', None, 'incremental'] + ]}, + 'proxy-acct': { + 'options': [None, "Proxy Accounting", "packets/s", 'Accounting', 'freerad.proxy.acct', 'line'], + 'lines': [ + ['proxy-accounting-requests', None, 'incremental'], ['proxy-accounting-responses', None, 'incremental'], + ['proxy-acct-dropped-requests', None, 'incremental'], ['proxy-acct-duplicate-requests', None, 'incremental'], + ['proxy-acct-invalid-requests', None, 'incremental'], ['proxy-acct-malformed-requests', None, 'incremental'], + ['proxy-acct-unknown-types', None, 'incremental'] + ]} + +} + + +class Service(SimpleService): + def __init__(self, configuration=None, name=None): + SimpleService.__init__(self, configuration=configuration, name=name) + self.host = self.configuration.get('host', 'localhost') + self.port = self.configuration.get('port', '18121') + self.secret = self.configuration.get('secret', 'adminsecret') + self.acct = self.configuration.get('acct', False) + self.proxy_auth = self.configuration.get('proxy_auth', False) + self.proxy_acct = self.configuration.get('proxy_acct', False) + try: + self.echo = [''.join([directory, 'echo']) for directory in directories if isfile(''.join([directory, 'echo']))][0] + self.radclient = [''.join([directory, 'radclient']) for directory in directories if isfile(''.join([directory, 'radclient']))][0] + except IndexError: + self.echo = [] + self.radclient = [] + self.sub_echo = [self.echo, 'Message-Authenticator = 0x00, FreeRADIUS-Statistics-Type = 15, Response-Packet-Type = Access-Accept'] + self.sub_radclient = [self.radclient, '-r', '1', '-t', '1', ':'.join([self.host, self.port]), 'status', self.secret] + + def check(self): + if not all([self.echo, self.radclient]): + self.error('Command radclient not found') + return False + if self._get_raw_data(): + chart_choice = [True, bool(self.acct), bool(self.proxy_auth), bool(self.proxy_acct)] + self.order = [chart for chart, choice in zip(ORDER, chart_choice) if choice] + self.definitions = {k:v for k, v in CHARTS.items() if k in self.order} + self.info('Plugin was started succesfully') + return True + else: + self.error('Request returned no data. Is server alive? Used options: host {}, port {}, secret {}'.format(self.host, self.port, self.secret)) + return False + + + def _get_data(self): + """ + Format data received from shell command + :return: dict + """ + result = self._get_raw_data() + return {k.lower():int(v) for k, v in findall(r'((?<=-)[AP][a-zA-Z-]+) = (\d+)', result)} + + def _get_raw_data(self): + """ + The following code is equivalent to + 'echo "Message-Authenticator = 0x00, FreeRADIUS-Statistics-Type = 15, Response-Packet-Type = Access-Accept" | radclient -t 1 -r 1 host:port status secret' + :return: str + """ + try: + process_echo = Popen(self.sub_echo, stdout=PIPE, stderr=PIPE, shell=False) + process_rad = Popen(self.sub_radclient, stdin=process_echo.stdout, stdout=PIPE, stderr=PIPE, shell=False) + process_echo.stdout.close() + raw_result = process_rad.communicate()[0] + except Exception: + return None + else: + if process_rad.returncode is 0: + return raw_result.decode() + else: + return None diff --git a/python.d/gunicorn_log.chart.py b/python.d/gunicorn_log.chart.py new file mode 100644 index 000000000..945963635 --- /dev/null +++ b/python.d/gunicorn_log.chart.py @@ -0,0 +1,72 @@ +# -*- coding: utf-8 -*- +# Description: nginx log netdata python.d module +# Author: Pawel Krupa (paulfantom) +# Modified for Gunicorn by: Jeff Willette (deltaskelta) + +from base import LogService +import re + +priority = 60000 +retries = 60 +# update_every = 3 + +ORDER = ['codes'] +CHARTS = { + 'codes': { + 'options': [None, 'gunicorn status codes', 'requests/s', 'requests', 'gunicorn_log.codes', 'stacked'], + 'lines': [ + ["2xx", None, "incremental"], + ["3xx", None, "incremental"], + ["4xx", None, "incremental"], + ["5xx", None, "incremental"] + ]} +} + + +class Service(LogService): + def __init__(self, configuration=None, name=None): + LogService.__init__(self, configuration=configuration, name=name) + if len(self.log_path) == 0: + self.log_path = "/var/log/gunicorn/access.log" + self.order = ORDER + self.definitions = CHARTS + pattern = r'" ([0-9]{3}) ' + #pattern = r'(?:" )([0-9][0-9][0-9]) ?' + self.regex = re.compile(pattern) + + def _get_data(self): + """ + Parse new log lines + :return: dict + """ + data = {'2xx': 0, + '3xx': 0, + '4xx': 0, + '5xx': 0} + try: + raw = self._get_raw_data() + if raw is None: + return None + elif not raw: + return data + except (ValueError, AttributeError): + return None + + regex = self.regex + for line in raw: + code = regex.search(line) + try: + beginning = code.group(1)[0] + except AttributeError: + continue + + if beginning == '2': + data["2xx"] += 1 + elif beginning == '3': + data["3xx"] += 1 + elif beginning == '4': + data["4xx"] += 1 + elif beginning == '5': + data["5xx"] += 1 + + return data diff --git a/python.d/haproxy.chart.py b/python.d/haproxy.chart.py new file mode 100644 index 000000000..2fb97d755 --- /dev/null +++ b/python.d/haproxy.chart.py @@ -0,0 +1,177 @@ +# -*- coding: utf-8 -*- +# Description: haproxy netdata python.d module +# Author: l2isbad + +from base import UrlService, 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 = ['fbin', 'fbout', 'fscur', 'fqcur', 'bbin', 'bbout', 'bscur', 'bqcur', 'health_sdown', 'health_bdown'] +CHARTS = { + 'fbin': { + 'options': [None, "Kilobytes in", "kilobytes in/s", 'Frontend', 'haproxy_f.bin', 'line'], + 'lines': [ + ]}, + 'fbout': { + 'options': [None, "Kilobytes out", "kilobytes out/s", 'Frontend', 'haproxy_f.bout', 'line'], + 'lines': [ + ]}, + 'fscur': { + 'options': [None, "Sessions active", "sessions", 'Frontend', 'haproxy_f.scur', 'line'], + 'lines': [ + ]}, + 'fqcur': { + 'options': [None, "Session in queue", "sessions", 'Frontend', 'haproxy_f.qcur', 'line'], + 'lines': [ + ]}, + 'bbin': { + 'options': [None, "Kilobytes in", "kilobytes in/s", 'Backend', 'haproxy_b.bin', 'line'], + 'lines': [ + ]}, + 'bbout': { + 'options': [None, "Kilobytes out", "kilobytes out/s", 'Backend', 'haproxy_b.bout', 'line'], + 'lines': [ + ]}, + 'bscur': { + 'options': [None, "Sessions active", "sessions", 'Backend', 'haproxy_b.scur', 'line'], + 'lines': [ + ]}, + 'bqcur': { + 'options': [None, "Sessions in queue", "sessions", 'Backend', 'haproxy_b.qcur', 'line'], + 'lines': [ + ]}, + 'health_sdown': { + 'options': [None, "Number of servers in backend in DOWN state", "failed servers", 'Health', 'haproxy_hs.down', 'line'], + 'lines': [ + ]}, + 'health_bdown': { + 'options': [None, "Is backend alive? 1 = DOWN", "failed backend", 'Health', 'haproxy_hb.down', 'line'], + 'lines': [ + ]} +} + + +class Service(UrlService, SocketService): + def __init__(self, configuration=None, name=None): + SocketService.__init__(self, configuration=configuration, name=name) + self.user = self.configuration.get('user') + self.password = self.configuration.get('pass') + self.request = 'show stat\n' + self.poll_method = (UrlService, SocketService) + self.order = ORDER + self.order_front = [_ for _ in ORDER if _.startswith('f')] + self.order_back = [_ for _ in ORDER if _.startswith('b')] + self.definitions = CHARTS + self.charts = True + + def check(self): + if self.configuration.get('url'): + self.poll_method = self.poll_method[0] + url = self.configuration.get('url') + if not url.endswith(';csv;norefresh'): + self.error('Bad url(%s). Must be http://<ip.address>:<port>/<url>;csv;norefresh' % url) + return False + elif self.configuration.get('socket'): + self.poll_method = self.poll_method[1] + else: + self.error('No configuration is specified') + return False + + if self.poll_method.check(self): + self.info('Plugin was started succesfully. We are using %s.' % self.poll_method.__name__) + return True + + def create_charts(self, front_ends, back_ends): + for _ in range(len(front_ends)): + self.definitions['fbin']['lines'].append(['_'.join(['fbin', front_ends[_]['# pxname']]), front_ends[_]['# pxname'], 'incremental', 1, 1024]) + self.definitions['fbout']['lines'].append(['_'.join(['fbout', front_ends[_]['# pxname']]), front_ends[_]['# pxname'], 'incremental', 1, 1024]) + self.definitions['fscur']['lines'].append(['_'.join(['fscur', front_ends[_]['# pxname']]), front_ends[_]['# pxname'], 'absolute']) + self.definitions['fqcur']['lines'].append(['_'.join(['fqcur', front_ends[_]['# pxname']]), front_ends[_]['# pxname'], 'absolute']) + + for _ in range(len(back_ends)): + self.definitions['bbin']['lines'].append(['_'.join(['bbin', back_ends[_]['# pxname']]), back_ends[_]['# pxname'], 'incremental', 1, 1024]) + self.definitions['bbout']['lines'].append(['_'.join(['bbout', back_ends[_]['# pxname']]), back_ends[_]['# pxname'], 'incremental', 1, 1024]) + self.definitions['bscur']['lines'].append(['_'.join(['bscur', back_ends[_]['# pxname']]), back_ends[_]['# pxname'], 'absolute']) + self.definitions['bqcur']['lines'].append(['_'.join(['bqcur', back_ends[_]['# pxname']]), back_ends[_]['# pxname'], 'absolute']) + self.definitions['health_sdown']['lines'].append(['_'.join(['hsdown', back_ends[_]['# pxname']]), back_ends[_]['# pxname'], 'absolute']) + self.definitions['health_bdown']['lines'].append(['_'.join(['hbdown', back_ends[_]['# pxname']]), back_ends[_]['# pxname'], 'absolute']) + + def _get_data(self): + """ + Format data received from http request + :return: dict + """ + try: + raw_data = self.poll_method._get_raw_data(self).splitlines() + except Exception as e: + self.error(str(e)) + return None + + all_instances = [dict(zip(raw_data[0].split(','), raw_data[_].split(','))) for _ in range(1, len(raw_data))] + + back_ends = list(filter(is_backend, all_instances)) + front_ends = list(filter(is_frontend, all_instances)) + servers = list(filter(is_server, all_instances)) + + if self.charts: + self.create_charts(front_ends, back_ends) + self.charts = False + + to_netdata = dict() + + for frontend in front_ends: + for _ in self.order_front: + to_netdata.update({'_'.join([_, frontend['# pxname']]): int(frontend[_[1:]]) if frontend.get(_[1:]) else 0}) + + for backend in back_ends: + for _ in self.order_back: + to_netdata.update({'_'.join([_, backend['# pxname']]): int(backend[_[1:]]) if backend.get(_[1:]) else 0}) + + for _ in range(len(back_ends)): + to_netdata.update({'_'.join(['hsdown', back_ends[_]['# pxname']]): + len([server for server in servers if is_server_down(server, back_ends, _)])}) + to_netdata.update({'_'.join(['hbdown', back_ends[_]['# pxname']]): 1 if is_backend_down(back_ends, _) else 0}) + + return to_netdata + + def _check_raw_data(self, data): + """ + Check if all data has been gathered from socket + :param data: str + :return: boolean + """ + return not bool(data) + +def is_backend(backend): + try: + return backend['svname'] == 'BACKEND' and backend['# pxname'] != 'stats' + except Exception: + return False + +def is_frontend(frontend): + try: + return frontend['svname'] == 'FRONTEND' and frontend['# pxname'] != 'stats' + except Exception: + return False + +def is_server(server): + try: + return not server['svname'].startswith(('FRONTEND', 'BACKEND')) + except Exception: + return False + +def is_server_down(server, back_ends, _): + try: + return server['# pxname'] == back_ends[_]['# pxname'] and server['status'] == 'DOWN' + except Exception: + return False + +def is_backend_down(back_ends, _): + try: + return back_ends[_]['status'] == 'DOWN' + except Exception: + return False diff --git a/python.d/hddtemp.chart.py b/python.d/hddtemp.chart.py index 465bfdfa2..8a98995be 100644 --- a/python.d/hddtemp.chart.py +++ b/python.d/hddtemp.chart.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- # Description: hddtemp netdata python.d module # Author: Pawel Krupa (paulfantom) +# Modified by l2isbad import os from base import SocketService @@ -21,15 +22,6 @@ retries = 60 ORDER = ['temperatures'] -CHARTS = { - 'temperatures': { - 'options': ['disks_temp', 'temperature', 'Celsius', 'Disks temperature', 'hddtemp.temperatures', 'line'], - 'lines': [ - # lines are created dynamically in `check()` method - ]} -} - - class Service(SocketService): def __init__(self, configuration=None, name=None): SocketService.__init__(self, configuration=configuration, name=name) @@ -38,7 +30,10 @@ class Service(SocketService): self.host = "127.0.0.1" self.port = 7634 self.order = ORDER - self.definitions = CHARTS + self.fahrenheit = ('Fahrenheit', lambda x: x * 9 / 5 + 32) if self.configuration.get('fahrenheit') else False + self.whatever = ('Whatever', lambda x: x * 33 / 22 + 11) if self.configuration.get('whatever') else False + self.choice = (choice for choice in [self.fahrenheit, self.whatever] if choice) + self.calc = lambda x: x self.disks = [] def _get_disks(self): @@ -82,7 +77,7 @@ class Service(SocketService): if not raw[i*5+1] in self.disks: continue try: - val = int(raw[i*5+3]) + val = self.calc(int(raw[i*5+3])) except ValueError: val = 0 data[raw[i*5+1].replace("/dev/", "")] = val @@ -105,9 +100,21 @@ class Service(SocketService): if data is None: return False + self.definitions = { + 'temperatures': { + 'options': ['disks_temp', 'Disks Temperatures', 'temperatures', 'hddtemp.temperatures', 'line'], + 'lines': [ + # lines are created dynamically in `check()` method + ]} + } + try: + self.choice = next(self.choice) + except StopIteration: + self.definitions[ORDER[0]]['options'].insert(2, 'Celsius') + else: + self.calc = self.choice[1] + self.definitions[ORDER[0]]['options'].insert(2, self.choice[0]) + for name in data: self.definitions[ORDER[0]]['lines'].append([name]) - return True - - diff --git a/python.d/ipfs.chart.py b/python.d/ipfs.chart.py index b0b2a9659..eaea3c5ee 100644 --- a/python.d/ipfs.chart.py +++ b/python.d/ipfs.chart.py @@ -19,7 +19,7 @@ retries = 60 # }} # charts order (can be overridden if you want less charts, or different order) -ORDER = ['bandwidth', 'peers'] +ORDER = ['bandwidth', 'peers', 'repo_size', 'repo_objects'] CHARTS = { 'bandwidth': { @@ -32,9 +32,25 @@ CHARTS = { 'options': [None, 'IPFS Peers', 'peers', 'Peers', 'ipfs.peers', 'line'], 'lines': [ ["peers", None, 'absolute'] - ]} + ]}, + 'repo_size': { + 'options': [None, 'IPFS Repo Size', 'GB', 'Size', 'ipfs.repo_size', 'area'], + 'lines': [ + ["avail", None, "absolute", 1, 1e9], + ["size", None, "absolute", 1, 1e9], + ]}, + 'repo_objects': { + 'options': [None, 'IPFS Repo Objects', 'objects', 'Objects', 'ipfs.repo_objects', 'line'], + 'lines': [ + ["objects", None, "absolute", 1, 1], + ["pinned", None, "absolute", 1, 1], + ["recursive_pins", None, "absolute", 1, 1] + ]}, } +SI_zeroes = {'k': 3, 'm': 6, 'g': 9, 't': 12, + 'p': 15, 'e': 18, 'z': 21, 'y': 24 } + class Service(UrlService): def __init__(self, configuration=None, name=None): @@ -45,63 +61,63 @@ class Service(UrlService): self.baseurl = "http://localhost:5001" self.order = ORDER self.definitions = CHARTS + self.__storagemax = None - def _get_bandwidth(self): + def _get_json(self, suburl): """ - Format data received from http request - :return: int, int + :return: json decoding of the specified url """ - self.url = self.baseurl + "/api/v0/stats/bw" - try: - raw = self._get_raw_data() - except AttributeError: - return None - + self.url = self.baseurl + suburl try: - parsed = json.loads(raw) - bw_in = int(parsed['RateIn']) - bw_out = int(parsed['RateOut']) + return json.loads(self._get_raw_data()) except: - return None + return {} - return bw_in, bw_out + def _recursive_pins(self, keys): + return len([k for k in keys if keys[k]["Type"] == b"recursive"]) - 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 + def _dehumanize(self, storemax): + # convert from '10Gb' to 10000000000 + if type(storemax) != int: + storemax = storemax.lower() + if storemax.endswith('b'): + val, units = storemax[:-2], storemax[-2] + if units in SI_zeroes: + val += '0'*SI_zeroes[units] + storemax = val + try: + storemax = int(storemax) + except: + storemax = None + return storemax - return peers + def _storagemax(self, storecfg): + if self.__storagemax is None: + self.__storagemax = self._dehumanize(storecfg['StorageMax']) + return self.__storagemax 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 + cfg = { # suburl : List of (result-key, original-key, transform-func) + '/api/v0/stats/bw' :[('in', 'RateIn', int ), + ('out', 'RateOut', int )], + '/api/v0/swarm/peers':[('peers', 'Strings', len )], + '/api/v0/stats/repo' :[('size', 'RepoSize', int), + ('objects', 'NumObjects', int)], + '/api/v0/pin/ls': [('pinned', 'Keys', len), + ('recursive_pins', 'Keys', self._recursive_pins)], + '/api/v0/config/show': [('avail', 'Datastore', self._storagemax)] + } + r = {} + for suburl in cfg: + json = self._get_json(suburl) + for newkey, origkey, xmute in cfg[suburl]: + try: + r[newkey] = xmute(json[origkey]) + except: pass + return r or None + - if len(data) == 0: - return None - return data diff --git a/python.d/isc_dhcpd.chart.py b/python.d/isc_dhcpd.chart.py new file mode 100644 index 000000000..54fbfb1f5 --- /dev/null +++ b/python.d/isc_dhcpd.chart.py @@ -0,0 +1,167 @@ +# -*- coding: utf-8 -*- +# Description: isc dhcpd lease netdata python.d module +# Author: l2isbad + +from base import SimpleService +from time import mktime, strptime, gmtime, time +from os import stat +try: + from ipaddress import IPv4Address as ipaddress + from ipaddress import ip_network + have_ipaddress = True +except ImportError: + have_ipaddress = False +try: + from itertools import filterfalse as filterfalse +except ImportError: + from itertools import ifilterfalse as filterfalse + + +priority = 60000 +retries = 60 +update_every = 60 + +class Service(SimpleService): + def __init__(self, configuration=None, name=None): + SimpleService.__init__(self, configuration=configuration, name=name) + self.leases_path = self.configuration.get('leases_path', '/var/lib/dhcp/dhcpd.leases') + self.pools = self.configuration.get('pools') + + # Will work only with 'default' db-time-format (weekday year/month/day hour:minute:second) + # TODO: update algorithm to parse correctly 'local' db-time-format + # (epoch <seconds-since-epoch>; # <day-name> <month-name> <day-number> <hours>:<minutes>:<seconds> <year>) + # Also only ipv4 supported + + def check(self): + if not self._get_raw_data(): + self.error('Make sure leases_path is correct and leases log file is readable by netdata') + return False + elif not have_ipaddress: + self.error('No ipaddress module. Please install (py2-ipaddress in case of python2)') + return False + else: + try: + self.pools = self.pools.split() + if not [ip_network(return_utf(pool)) for pool in self.pools]: + self.error('Pools list is empty') + return False + except (ValueError, IndexError, AttributeError, SyntaxError) as e: + self.error('Pools configurations is incorrect', str(e)) + return False + + # Creating static charts + self.order = ['parse_time', 'leases_size', 'utilization', 'total'] + self.definitions = {'utilization': + {'options': + [None, 'Pools utilization', 'used %', 'Utilization', 'isc_dhcpd.util', 'line'], + 'lines': []}, + 'total': + {'options': + [None, 'Total all pools', 'leases', 'Utilization', 'isc_dhcpd.total', 'line'], + 'lines': [['total', 'leases', 'absolute']]}, + 'parse_time': + {'options': + [None, 'Parse time', 'ms', 'Parse stats', 'isc_dhcpd.parse', 'line'], + 'lines': [['ptime', 'time', 'absolute']]}, + 'leases_size': + {'options': + [None, 'dhcpd.leases file size', 'kilobytes', 'Parse stats', 'isc_dhcpd.lsize', 'line'], + 'lines': [['lsize', 'size', 'absolute']]}} + # Creating dynamic charts + for pool in self.pools: + self.definitions['utilization']['lines'].append([''.join(['ut_', pool]), pool, 'absolute']) + self.order.append(''.join(['leases_', pool])) + self.definitions[''.join(['leases_', pool])] = \ + {'options': [None, 'Active leases', 'leases', 'Pools', 'isc_dhcpd.lease', 'area'], + 'lines': [[''.join(['le_', pool]), pool, 'absolute']]} + + self.info('Plugin was started succesfully') + return True + + def _get_raw_data(self): + """ + Parses log file + :return: tuple( + [ipaddress, lease end time, ...], + time to parse leases file + ) + """ + try: + with open(self.leases_path, 'rt') as dhcp_leases: + time_start = time() + part1 = filterfalse(find_lease, dhcp_leases) + part2 = filterfalse(find_ends, dhcp_leases) + raw_result = dict(zip(part1, part2)) + time_end = time() + + file_parse_time = round((time_end - time_start) * 1000) + + except Exception as e: + self.error("Failed to parse leases file:", str(e)) + return None + + else: + result = (raw_result, file_parse_time) + return result + + def _get_data(self): + """ + :return: dict + """ + raw_leases = self._get_raw_data() + + if not raw_leases: + return None + + # Result: {ipaddress: end lease time, ...} + all_leases = {k[6:len(k)-3]:v[7:len(v)-2] for k, v in raw_leases[0].items()} + + # Result: [active binding, active binding....]. (Expire time (ends date;) - current time > 0) + active_leases = [k for k, v in all_leases.items() if is_binding_active(all_leases[k])] + + # Result: {pool: number of active bindings in pool, ...} + pools_count = {pool: len([lease for lease in active_leases if is_address_in(lease, pool)]) + for pool in self.pools} + + # Result: {pool: number of host ip addresses in pool, ...} + pools_max = {pool: (2 ** (32 - int(pool.split('/')[1])) - 2) + for pool in self.pools} + + # Result: {pool: % utilization, ....} (percent) + pools_util = {pool:int(round(float(pools_count[pool]) / pools_max[pool] * 100, 0)) + for pool in self.pools} + + # Bulding dicts to send to netdata + final_count = {''.join(['le_', k]): v for k, v in pools_count.items()} + final_util = {''.join(['ut_', k]): v for k, v in pools_util.items()} + + to_netdata = {'total': len(active_leases)} + to_netdata.update({'lsize': int(stat(self.leases_path)[6] / 1024)}) + to_netdata.update({'ptime': int(raw_leases[1])}) + to_netdata.update(final_util) + to_netdata.update(final_count) + + return to_netdata + + +def is_binding_active(binding): + return mktime(strptime(binding, '%w %Y/%m/%d %H:%M:%S')) - mktime(gmtime()) > 0 + + +def is_address_in(address, pool): + return ipaddress(return_utf(address)) in ip_network(return_utf(pool)) + + +def find_lease(value): + return value[0:3] != 'lea' + + +def find_ends(value): + return value[2:6] != 'ends' + +def return_utf(s): + # python2 returns "<type 'str'>" for simple strings + # python3 returns "<class 'str'>" for unicode strings + if str(type(s)) == "<type 'str'>": + return unicode(s, 'utf-8') + return s diff --git a/python.d/mdstat.chart.py b/python.d/mdstat.chart.py new file mode 100644 index 000000000..0f7d2b444 --- /dev/null +++ b/python.d/mdstat.chart.py @@ -0,0 +1,99 @@ +# -*- coding: utf-8 -*- +# Description: mdstat netdata python.d module +# Author: l2isbad + +from base import SimpleService +from re import compile +priority = 60000 +retries = 60 +update_every = 1 + + +class Service(SimpleService): + def __init__(self, configuration=None, name=None): + SimpleService.__init__(self, configuration=configuration, name=name) + self.order = ['agr_health'] + self.definitions = {'agr_health': + {'options': + [None, 'Faulty devices in MD', 'failed disks', 'health', 'md.health', 'line'], + 'lines': []}} + self.proc_mdstat = '/proc/mdstat' + self.regex_disks = compile(r'((?<=\ )[a-zA-Z_0-9]+(?= : active)).*?((?<= \[)[0-9]+)/([0-9]+(?=\] ))') + self.regex_status = compile(r'([a-zA-Z_0-9]+)( : active)[^:]*?([a-z]+) = ([0-9.]+(?=%)).*?((?<=finish=)[0-9.]+)min speed=([0-9]+)') + + def check(self): + raw_data = self._get_raw_data() + if not raw_data: + self.error('Cant read mdstat data from %s' % (self.proc_mdstat)) + return False + + md_list = [md[0] for md in self.regex_disks.findall(raw_data)] + + if not md_list: + self.error('No active arrays in %s' % (self.proc_mdstat)) + return False + else: + for md in md_list: + self.order.append(md) + self.order.append(''.join([md, '_status'])) + self.order.append(''.join([md, '_rate'])) + self.definitions['agr_health']['lines'].append([''.join([md, '_health']), md, 'absolute']) + self.definitions[md] = {'options': + [None, '%s disks stats' % md, 'disks', md, 'md.disks', 'stacked'], + 'lines': [[''.join([md, '_total']), 'total', 'absolute'], + [''.join([md, '_inuse']), 'inuse', 'absolute']]} + self.definitions[''.join([md, '_status'])] = {'options': + [None, '%s current status' % md, 'percent', md, 'md.status', 'line'], + 'lines': [[''.join([md, '_resync']), 'resync', 'absolute', 1, 100], + [''.join([md, '_recovery']), 'recovery', 'absolute', 1, 100], + [''.join([md, '_reshape']), 'reshape', 'absolute', 1, 100], + [''.join([md, '_check']), 'check', 'absolute', 1, 100]]} + self.definitions[''.join([md, '_rate'])] = {'options': + [None, '%s operation status' % md, 'rate', md, 'md.rate', 'line'], + 'lines': [[''.join([md, '_finishin']), 'finish min', 'absolute', 1, 100], + [''.join([md, '_rate']), 'megabyte/s', 'absolute', -1, 100]]} + self.info('Plugin was started successfully. MDs to monitor %s' % (md_list)) + + return True + + def _get_raw_data(self): + """ + Read data from /proc/mdstat + :return: str + """ + try: + with open(self.proc_mdstat, 'rt') as proc_mdstat: + raw_result = proc_mdstat.read() + except Exception: + return None + else: + raw_result = ' '.join(raw_result.split()) + return raw_result + + def _get_data(self): + """ + Parse data from _get_raw_data() + :return: dict + """ + raw_mdstat = self._get_raw_data() + mdstat_disks = self.regex_disks.findall(raw_mdstat) + mdstat_status = self.regex_status.findall(raw_mdstat) + to_netdata = {} + + for md in mdstat_disks: + to_netdata[''.join([md[0], '_total'])] = int(md[1]) + to_netdata[''.join([md[0], '_inuse'])] = int(md[2]) + to_netdata[''.join([md[0], '_health'])] = int(md[1]) - int(md[2]) + to_netdata[''.join([md[0], '_check'])] = 0 + to_netdata[''.join([md[0], '_resync'])] = 0 + to_netdata[''.join([md[0], '_reshape'])] = 0 + to_netdata[''.join([md[0], '_recovery'])] = 0 + to_netdata[''.join([md[0], '_finishin'])] = 0 + to_netdata[''.join([md[0], '_rate'])] = 0 + + for md in mdstat_status: + to_netdata[''.join([md[0], '_' + md[2]])] = round(float(md[3]) * 100) + to_netdata[''.join([md[0], '_finishin'])] = round(float(md[4]) * 100) + to_netdata[''.join([md[0], '_rate'])] = round(float(md[5]) / 1000 * 100) + + return to_netdata diff --git a/python.d/memcached.chart.py b/python.d/memcached.chart.py index 5a1400c99..0d6807ba7 100644 --- a/python.d/memcached.chart.py +++ b/python.d/memcached.chart.py @@ -20,89 +20,89 @@ retries = 60 # }} ORDER = ['cache', 'net', 'connections', 'items', 'evicted_reclaimed', - 'get', 'get_rate', 'set_rate', 'delete', 'cas', 'increment', 'decrement', 'touch', 'touch_rate'] + 'get', 'get_rate', 'set_rate', 'cas', 'delete', 'increment', 'decrement', 'touch', 'touch_rate'] CHARTS = { 'cache': { - 'options': [None, 'Cache Size', 'megabytes', 'Cache', 'memcached.cache', 'stacked'], + 'options': [None, 'Cache Size', 'megabytes', 'cache', 'memcached.cache', 'stacked'], 'lines': [ - ['used', 'used', 'absolute', 1, 1048576], - ['avail', 'available', 'absolute', 1, 1048576] + ['avail', 'available', 'absolute', 1, 1048576], + ['used', 'used', 'absolute', 1, 1048576] ]}, 'net': { - 'options': [None, 'Network', 'kilobytes/s', 'Network', 'memcached.net', 'line'], + 'options': [None, 'Network', 'kilobits/s', 'network', 'memcached.net', 'area'], 'lines': [ - ['bytes_read', 'read', 'incremental', 1, 1024], - ['bytes_written', 'written', 'incremental', 1, 1024] + ['bytes_read', 'in', 'incremental', 8, 1024], + ['bytes_written', 'out', 'incremental', -8, 1024] ]}, 'connections': { - 'options': [None, 'Connections', 'connections/s', 'Cluster', 'memcached.connections', 'line'], + 'options': [None, 'Connections', 'connections/s', 'connections', 'memcached.connections', 'line'], 'lines': [ ['curr_connections', 'current', 'incremental'], ['rejected_connections', 'rejected', 'incremental'], ['total_connections', 'total', 'incremental'] ]}, 'items': { - 'options': [None, 'Items', 'items', 'Cluster', 'memcached.items', 'line'], + 'options': [None, 'Items', 'items', 'items', 'memcached.items', 'line'], 'lines': [ ['curr_items', 'current', 'absolute'], ['total_items', 'total', 'absolute'] ]}, 'evicted_reclaimed': { - 'options': [None, 'Items', 'items', 'Evicted and Reclaimed', 'memcached.evicted_reclaimed', 'line'], + 'options': [None, 'Items', 'items', 'items', 'memcached.evicted_reclaimed', 'line'], 'lines': [ - ['evictions', 'evicted', 'absolute'], - ['reclaimed', 'reclaimed', 'absolute'] + ['reclaimed', 'reclaimed', 'absolute'], + ['evictions', 'evicted', 'absolute'] ]}, 'get': { - 'options': [None, 'Requests', 'requests', 'GET', 'memcached.get', 'stacked'], + 'options': [None, 'Requests', 'requests', 'get ops', 'memcached.get', 'stacked'], 'lines': [ ['get_hits', 'hits', 'percent-of-absolute-row'], ['get_misses', 'misses', 'percent-of-absolute-row'] ]}, 'get_rate': { - 'options': [None, 'Rate', 'requests/s', 'GET', 'memcached.get_rate', 'line'], + 'options': [None, 'Rate', 'requests/s', 'get ops', 'memcached.get_rate', 'line'], 'lines': [ ['cmd_get', 'rate', 'incremental'] ]}, 'set_rate': { - 'options': [None, 'Rate', 'requests/s', 'SET', 'memcached.set_rate', 'line'], + 'options': [None, 'Rate', 'requests/s', 'set ops', 'memcached.set_rate', 'line'], 'lines': [ ['cmd_set', 'rate', 'incremental'] ]}, 'delete': { - 'options': [None, 'Requests', 'requests', 'DELETE', 'memcached.delete', 'stacked'], + 'options': [None, 'Requests', 'requests', 'delete ops', 'memcached.delete', 'stacked'], 'lines': [ ['delete_hits', 'hits', 'percent-of-absolute-row'], ['delete_misses', 'misses', 'percent-of-absolute-row'], ]}, 'cas': { - 'options': [None, 'Requests', 'requests', 'CAS', 'memcached.cas', 'stacked'], + 'options': [None, 'Requests', 'requests', 'check and set ops', 'memcached.cas', 'stacked'], 'lines': [ ['cas_hits', 'hits', 'percent-of-absolute-row'], ['cas_misses', 'misses', 'percent-of-absolute-row'], ['cas_badval', 'bad value', 'percent-of-absolute-row'] ]}, 'increment': { - 'options': [None, 'Requests', 'requests', 'Increment', 'memcached.increment', 'stacked'], + 'options': [None, 'Requests', 'requests', 'increment ops', 'memcached.increment', 'stacked'], 'lines': [ ['incr_hits', 'hits', 'percent-of-absolute-row'], ['incr_misses', 'misses', 'percent-of-absolute-row'] ]}, 'decrement': { - 'options': [None, 'Requests', 'requests', 'Decrement', 'memcached.decrement', 'stacked'], + 'options': [None, 'Requests', 'requests', 'decrement ops', 'memcached.decrement', 'stacked'], 'lines': [ ['decr_hits', 'hits', 'percent-of-absolute-row'], ['decr_misses', 'misses', 'percent-of-absolute-row'] ]}, 'touch': { - 'options': [None, 'Requests', 'requests', 'Touch', 'memcached.touch', 'stacked'], + 'options': [None, 'Requests', 'requests', 'touch ops', 'memcached.touch', 'stacked'], 'lines': [ ['touch_hits', 'hits', 'percent-of-absolute-row'], ['touch_misses', 'misses', 'percent-of-absolute-row'] ]}, 'touch_rate': { - 'options': [None, 'Rate', 'requests/s', 'Touch', 'memcached.touch_rate', 'line'], + 'options': [None, 'Rate', 'requests/s', 'touch ops', 'memcached.touch_rate', 'line'], 'lines': [ ['cmd_touch', 'rate', 'incremental'] ]} @@ -125,44 +125,52 @@ class Service(SocketService): Get data from socket :return: dict """ + response = self._get_raw_data() + if response is None: + # error has already been logged + return None + + if response.startswith('ERROR'): + self.error("received ERROR") + return None + try: - raw = self._get_raw_data().split("\n") + parsed = response.split("\n") except AttributeError: - self.error("no data received") - return None - if raw[0].startswith('ERROR'): - self.error("Memcached returned ERROR") + self.error("response is invalid/empty") return None + + # split the response data = {} - for line in raw: + for line in parsed: if line.startswith('STAT'): try: t = line[5:].split(' ') - data[t[0]] = int(t[1]) + data[t[0]] = t[1] except (IndexError, ValueError): + self.debug("invalid line received: " + str(line)) pass - try: - data['hit_rate'] = int((data['keyspace_hits'] / float(data['keyspace_hits'] + data['keyspace_misses'])) * 100) - except: - data['hit_rate'] = 0 + if len(data) == 0: + self.error("received data doesn't have any records") + return None + + # custom calculations try: data['avail'] = int(data['limit_maxbytes']) - int(data['bytes']) - data['used'] = data['bytes'] + data['used'] = int(data['bytes']) except: pass - if len(data) == 0: - self.error("received data doesn't have needed records") - return None - else: - return data + return data def _check_raw_data(self, data): if data.endswith('END\r\n'): + self.debug("received full response from memcached") return True - else: - return False + + self.debug("waiting more data from memcached") + return False def check(self): """ diff --git a/python.d/mysql.chart.py b/python.d/mysql.chart.py index dab6fad3b..0e3a03299 100644 --- a/python.d/mysql.chart.py +++ b/python.d/mysql.chart.py @@ -40,6 +40,7 @@ retries = 60 # query executed on MySQL server QUERY = "SHOW GLOBAL STATUS;" +QUERY_SLAVE = "SHOW SLAVE STATUS;" ORDER = ['net', 'queries', @@ -55,7 +56,7 @@ ORDER = ['net', 'innodb_buffer_pool_read_ahead', 'innodb_buffer_pool_reqs', 'innodb_buffer_pool_ops', 'qcache_ops', 'qcache', 'qcache_freemem', 'qcache_memblocks', 'key_blocks', 'key_requests', 'key_disk_ops', - 'files', 'files_rate'] + 'files', 'files_rate', 'slave_behind', 'slave_status'] CHARTS = { 'net': { @@ -298,8 +299,18 @@ CHARTS = { ["Connection_errors_peer_address", "peer_addr", "incremental"], ["Connection_errors_select", "select", "incremental"], ["Connection_errors_tcpwrap", "tcpwrap", "incremental"] + ]}, + 'slave_behind': { + 'options': [None, 'Slave Behind Seconds', 'seconds', 'slave', 'mysql.slave_behind', 'line'], + 'lines': [ + ["slave_behind", "seconds", "absolute"] + ]}, + 'slave_status': { + 'options': [None, 'Slave Status', 'status', 'slave', 'mysql.slave_status', 'line'], + 'lines': [ + ["slave_sql", "sql_running", "absolute"], + ["slave_io", "io_running", "absolute"] ]} - } @@ -310,6 +321,7 @@ class Service(SimpleService): self.order = ORDER self.definitions = CHARTS self.connection = None + self.do_slave = -1 def _parse_config(self, configuration): """ @@ -348,6 +360,67 @@ class Service(SimpleService): self.error("problem connecting to server:", e) raise RuntimeError + def _get_data_slave(self): + """ + Get slave raw data from MySQL server + :return: dict + """ + if self.connection is None: + try: + self._connect() + except RuntimeError: + return None + + slave_data = None + slave_raw_data = None + try: + cursor = self.connection.cursor() + if cursor.execute(QUERY_SLAVE): + slave_raw_data = dict(list(zip([elem[0] for elem in cursor.description], cursor.fetchone()))) + + except MySQLdb.OperationalError as e: + self.debug("Reconnecting for query", QUERY_SLAVE, ":", str(e)) + try: + self._connect() + cursor = self.connection.cursor() + if cursor.execute(QUERY_SLAVE): + slave_raw_data = dict(list(zip([elem[0] for elem in cursor.description], cursor.fetchone()))) + except Exception as e: + self.error("retried, but cannot execute query", QUERY_SLAVE, ":", str(e)) + self.connection.close() + self.connection = None + return None + + except Exception as e: + self.error("cannot execute query", QUERY_SLAVE, ":", str(e)) + self.connection.close() + self.connection = None + return None + + if slave_raw_data is not None: + slave_data = { + 'slave_behind': None, + 'slave_sql': None, + 'slave_io': None + } + + try: + slave_data['slave_behind'] = int(slave_raw_data.setdefault('Seconds_Behind_Master', -1)) + except: + slave_data['slave_behind'] = None + + try: + slave_data['slave_sql'] = 1 if slave_raw_data.get('Slave_SQL_Running') == 'Yes' else -1 + except: + slave_data['slave_sql'] = None + + try: + slave_data['slave_io'] = 1 if slave_raw_data.get('Slave_IO_Running') == 'Yes' else -1 + except: + slave_data['slave_io'] = None + + return slave_data + def _get_data(self): """ Get raw data from MySQL server @@ -362,23 +435,47 @@ class Service(SimpleService): 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() + self.debug("Reconnecting for query", QUERY, ":", str(e)) + try: + self._connect() + cursor = self.connection.cursor() + cursor.execute(QUERY) + raw_data = cursor.fetchall() + except Exception as e: + self.error("retried, but cannot execute query", QUERY, ":", str(e)) + self.connection.close() + self.connection = None + return None + except Exception as e: - self.error("cannot execute query.", e) + self.error("cannot execute query", QUERY, ":", str(e)) self.connection.close() self.connection = None return None data = dict(raw_data) + + # check for slave data + # the first time is -1 (so we do it) + # then it is set to 1 or 0 and we keep it like that + if self.do_slave != 0: + slave_data = self._get_data_slave() + if slave_data is not None: + data.update(slave_data) + if self.do_slave == -1: + self.do_slave = 1 + else: + if self.do_slave == -1: + self.error("replication metrics will be disabled - please allow netdata to collect them.") + self.do_slave = 0 + + # do calculations try: - data["Thread_cache_misses"] = int(data["Threads_created"] * 10000 / float(data["Connections"])) + data["Thread_cache_misses"] = round(float(data["Threads_created"]) / float(data["Connections"]) * 10000) except: - data["Thread_cache_misses"] = 0 + data["Thread_cache_misses"] = None return data diff --git a/python.d/nginx.chart.py b/python.d/nginx.chart.py index ff8bfede8..88849a921 100644 --- a/python.d/nginx.chart.py +++ b/python.d/nginx.chart.py @@ -22,24 +22,24 @@ ORDER = ['connections', 'requests', 'connection_status', 'connect_rate'] CHARTS = { 'connections': { - 'options': [None, 'nginx Active Connections', 'connections', 'nginx', 'nginx.connections', 'line'], + 'options': [None, 'nginx Active Connections', 'connections', 'active connections', 'nginx.connections', 'line'], 'lines': [ ["active"] ]}, 'requests': { - 'options': [None, 'nginx Requests', 'requests/s', 'nginx', 'nginx.requests', 'line'], + 'options': [None, 'nginx Requests', 'requests/s', 'requests', 'nginx.requests', 'line'], 'lines': [ ["requests", None, 'incremental'] ]}, 'connection_status': { - 'options': [None, 'nginx Active Connections by Status', 'connections', 'nginx', 'nginx.connection_status', 'line'], + 'options': [None, 'nginx Active Connections by Status', 'connections', 'status', 'nginx.connection_status', 'line'], 'lines': [ ["reading"], ["writing"], ["waiting", "idle"] ]}, 'connect_rate': { - 'options': [None, 'nginx Connections Rate', 'connections/s', 'nginx', 'nginx.connect_rate', 'line'], + 'options': [None, 'nginx Connections Rate', 'connections/s', 'connections rate', 'nginx.connect_rate', 'line'], 'lines': [ ["accepts", "accepted", "incremental"], ["handled", None, "incremental"] diff --git a/python.d/nginx_log.chart.py b/python.d/nginx_log.chart.py index 95fb123d2..ef964a565 100644 --- a/python.d/nginx_log.chart.py +++ b/python.d/nginx_log.chart.py @@ -15,9 +15,11 @@ CHARTS = { 'options': [None, 'nginx status codes', 'requests/s', 'requests', 'nginx_log.codes', 'stacked'], 'lines': [ ["2xx", None, "incremental"], + ["5xx", None, "incremental"], ["3xx", None, "incremental"], ["4xx", None, "incremental"], - ["5xx", None, "incremental"] + ["1xx", None, "incremental"], + ["other", None, "incremental"] ]} } @@ -33,37 +35,48 @@ class Service(LogService): #pattern = r'(?:" )([0-9][0-9][0-9]) ?' self.regex = re.compile(pattern) + self.data = { + '1xx': 0, + '2xx': 0, + '3xx': 0, + '4xx': 0, + '5xx': 0, + 'other': 0 + } + def _get_data(self): """ Parse new log lines :return: dict """ - data = {'2xx': 0, - '3xx': 0, - '4xx': 0, - '5xx': 0} try: raw = self._get_raw_data() if raw is None: return None elif not raw: - return data + return self.data except (ValueError, AttributeError): return None regex = self.regex for line in raw: code = regex.search(line) - beginning = code.group(1)[0] + try: + beginning = code.group(1)[0] + except AttributeError: + continue if beginning == '2': - data["2xx"] += 1 + self.data["2xx"] += 1 elif beginning == '3': - data["3xx"] += 1 + self.data["3xx"] += 1 elif beginning == '4': - data["4xx"] += 1 + self.data["4xx"] += 1 elif beginning == '5': - data["5xx"] += 1 - - return data + self.data["5xx"] += 1 + elif beginning == '1': + self.data["1xx"] += 1 + else: + self.data["other"] += 1 + return self.data diff --git a/python.d/ovpn_status_log.chart.py b/python.d/ovpn_status_log.chart.py new file mode 100644 index 000000000..c5fca002a --- /dev/null +++ b/python.d/ovpn_status_log.chart.py @@ -0,0 +1,77 @@ +# -*- coding: utf-8 -*- +# Description: openvpn status log netdata python.d module +# Author: l2isbad + +from base import SimpleService +from re import compile, findall, search, subn +priority = 60000 +retries = 60 +update_every = 10 + +ORDER = ['users', 'traffic'] +CHARTS = { + 'users': { + 'options': [None, 'OpenVPN active users', 'active users', 'Users', 'openvpn_status.users', 'line'], + 'lines': [ + ["users", None, "absolute"], + ]}, + 'traffic': { + 'options': [None, 'OpenVPN traffic', 'kilobit/s', 'Traffic', 'openvpn_status.traffic', 'area'], + 'lines': [ + ["in", None, "incremental", 8, 1000], ["out", None, "incremental", 8, -1000] + ]}, + +} + +class Service(SimpleService): + def __init__(self, configuration=None, name=None): + SimpleService.__init__(self, configuration=configuration, name=name) + self.order = ORDER + self.definitions = CHARTS + self.log_path = self.configuration.get('log_path') + self.regex_data_inter = compile(r'(?<=Since ).*?(?=.ROUTING)') + self.regex_data_final = compile(r'\d{1,3}(?:\.\d{1,3}){3}[:0-9,. ]*') + self.regex_users = compile(r'\d{1,3}(?:\.\d{1,3}){3}:\d+') + self.regex_traffic = compile(r'(?<=(?:,| ))\d+(?=(?:,| ))') + + def check(self): + if not self._get_raw_data(): + self.error('Make sure that the openvpn status log file exists and netdata has permission to read it') + return False + else: + self.info('Plugin was started succesfully') + return True + + def _get_raw_data(self): + """ + Open log file + :return: str + """ + try: + with open(self.log_path, 'rt') as log: + result = log.read() + except Exception: + return None + else: + return result + + def _get_data(self): + """ + Parse openvpn-status log file. + Current regex version is ok for status-version 1, 2 and 3. Both users and bytes in/out are collecting. + """ + + raw_data = self._get_raw_data() + try: + data_inter = self.regex_data_inter.search(' '.join(raw_data.splitlines())).group() + except AttributeError: + data_inter = '' + + data_final = ' '.join(self.regex_data_final.findall(data_inter)) + users = self.regex_users.subn('', data_final)[1] + traffic = self.regex_traffic.findall(data_final) + + bytes_in = sum([int(traffic[i]) for i in range(len(traffic)) if (i + 1) % 2 is 1]) + bytes_out = sum([int(traffic[i]) for i in range(len(traffic)) if (i + 1) % 2 is 0]) + + return {'users': users, 'in': bytes_in, 'out': bytes_out} diff --git a/python.d/phpfpm.chart.py b/python.d/phpfpm.chart.py index d4168cc11..b79a35d75 100755 --- a/python.d/phpfpm.chart.py +++ b/python.d/phpfpm.chart.py @@ -23,37 +23,37 @@ ORDER = ['connections', 'requests', 'performance', 'request_duration', 'request_ CHARTS = { 'connections': { - 'options': [None, 'PHP-FPM Active Connections', 'connections', 'phpfpm', 'phpfpm.connections', 'line'], + 'options': [None, 'PHP-FPM Active Connections', 'connections', 'active connections', 'phpfpm.connections', 'line'], 'lines': [ ["active"], ["maxActive", 'max active'], ["idle"] ]}, 'requests': { - 'options': [None, 'PHP-FPM Requests', 'requests/s', 'phpfpm', 'phpfpm.requests', 'line'], + 'options': [None, 'PHP-FPM Requests', 'requests/s', 'requests', 'phpfpm.requests', 'line'], 'lines': [ ["requests", None, "incremental"] ]}, 'performance': { - 'options': [None, 'PHP-FPM Performance', 'status', 'phpfpm', 'phpfpm.performance', 'line'], + 'options': [None, 'PHP-FPM Performance', 'status', 'performance', 'phpfpm.performance', 'line'], 'lines': [ ["reached", 'max children reached'], ["slow", 'slow requests'] ]}, 'request_duration': { - 'options': [None, 'PHP-FPM Request Duration', 'milliseconds', 'phpfpm', 'phpfpm.request_duration', 'line'], + 'options': [None, 'PHP-FPM Request Duration', 'milliseconds', 'request duration', 'phpfpm.request_duration', 'line'], 'lines': [ ["maxReqDur", 'max request duration'], ["avgReqDur", 'average request duration'] ]}, 'request_cpu': { - 'options': [None, 'PHP-FPM Request CPU', 'percent', 'phpfpm', 'phpfpm.request_cpu', 'line'], + 'options': [None, 'PHP-FPM Request CPU', 'percent', 'request CPU', 'phpfpm.request_cpu', 'line'], 'lines': [ ["maxReqCPU", 'max request cpu'], ["avgReqCPU", 'average request cpu'] ]}, 'request_mem': { - 'options': [None, 'PHP-FPM Request Memory', 'kilobytes', 'phpfpm', 'phpfpm.request_mem', 'line'], + 'options': [None, 'PHP-FPM Request Memory', 'kilobytes', 'request memory', 'phpfpm.request_mem', 'line'], 'lines': [ ["maxReqMem", 'max request memory'], ["avgReqMem", 'average request memory'] @@ -93,9 +93,14 @@ class Service(UrlService): raw_json = json.loads(raw) except ValueError: return None - data = {self.assignment[k]: v for k, v in raw_json.items() if k in self.assignment} + data = {} + for k,v in raw_json.items(): + if k in self.assignment: + data[self.assignment[k]] = v + if '&full' in self.url or '?full' in self.url: c = 0 + sum_val = {} for proc in raw_json['processes']: if proc['state'] != 'Idle': continue @@ -108,9 +113,14 @@ class Service(UrlService): d = d/1024 if 'max' + v not in data or data['max' + v] < d: data['max' + v] = d - if 'avg' + v not in data: + if 'avg' + v not in sum_val: + sum_val['avg' + v] = 0 data['avg' + v] = 0 - data['avg' + v] = (data['avg' + v] + d) / c + sum_val['avg' + v] += d + if len(sum_val): + for k, v in sum_val.items(): + data[k] = v/c + if len(data) == 0: return None return data diff --git a/python.d/postgres.chart.py b/python.d/postgres.chart.py new file mode 100644 index 000000000..919b6f8ee --- /dev/null +++ b/python.d/postgres.chart.py @@ -0,0 +1,386 @@ +# -*- coding: utf-8 -*- +# Description: example netdata python.d module +# Authors: facetoe, dangtranhoang + +import re +from copy import deepcopy + +import psycopg2 +from psycopg2 import extensions +from psycopg2.extras import DictCursor + +from base import SimpleService + +# default module values +update_every = 1 +priority = 90000 +retries = 60 + +ARCHIVE = """ +SELECT + CAST(COUNT(*) AS INT) AS file_count, + CAST(COALESCE(SUM(CAST(archive_file ~ $r$\.ready$$r$ as INT)), 0) AS INT) AS ready_count, + CAST(COALESCE(SUM(CAST(archive_file ~ $r$\.done$$r$ AS INT)), 0) AS INT) AS done_count +FROM + pg_catalog.pg_ls_dir('pg_xlog/archive_status') AS archive_files (archive_file); +""" + +BACKENDS = """ +SELECT + count(*) - (SELECT count(*) FROM pg_stat_activity WHERE state = 'idle') AS backends_active, + (SELECT count(*) FROM pg_stat_activity WHERE state = 'idle' ) AS backends_idle +FROM + pg_stat_activity; +""" + +TABLE_STATS = """ +SELECT + ((sum(relpages) * 8) * 1024) AS size_relations, + count(1) AS relations +FROM pg_class +WHERE relkind IN ('r', 't'); +""" + +INDEX_STATS = """ +SELECT + ((sum(relpages) * 8) * 1024) AS size_indexes, + count(1) AS indexes +FROM pg_class +WHERE relkind = 'i';""" + +DATABASE = """ +SELECT + datname AS database_name, + sum(numbackends) AS connections, + sum(xact_commit) AS xact_commit, + sum(xact_rollback) AS xact_rollback, + sum(blks_read) AS blks_read, + sum(blks_hit) AS blks_hit, + sum(tup_returned) AS tup_returned, + sum(tup_fetched) AS tup_fetched, + sum(tup_inserted) AS tup_inserted, + sum(tup_updated) AS tup_updated, + sum(tup_deleted) AS tup_deleted, + sum(conflicts) AS conflicts +FROM pg_stat_database +WHERE NOT datname ~* '^template\d+' +GROUP BY database_name; +""" + +BGWRITER = 'SELECT * FROM pg_stat_bgwriter;' +DATABASE_LOCKS = """ +SELECT + pg_database.datname as database_name, + mode, + count(mode) AS count +FROM pg_locks + INNER JOIN pg_database ON pg_database.oid = pg_locks.database +GROUP BY datname, mode +ORDER BY datname, mode; +""" +REPLICATION = """ +SELECT + client_hostname, + client_addr, + state, + sent_offset - ( + replay_offset - (sent_xlog - replay_xlog) * 255 * 16 ^ 6 ) AS byte_lag +FROM ( + SELECT + client_addr, client_hostname, state, + ('x' || lpad(split_part(sent_location::text, '/', 1), 8, '0'))::bit(32)::bigint AS sent_xlog, + ('x' || lpad(split_part(replay_location::text, '/', 1), 8, '0'))::bit(32)::bigint AS replay_xlog, + ('x' || lpad(split_part(sent_location::text, '/', 2), 8, '0'))::bit(32)::bigint AS sent_offset, + ('x' || lpad(split_part(replay_location::text, '/', 2), 8, '0'))::bit(32)::bigint AS replay_offset + FROM pg_stat_replication +) AS s; +""" + +LOCK_TYPES = [ + 'ExclusiveLock', + 'RowShareLock', + 'SIReadLock', + 'ShareUpdateExclusiveLock', + 'AccessExclusiveLock', + 'AccessShareLock', + 'ShareRowExclusiveLock', + 'ShareLock', + 'RowExclusiveLock' +] + +ORDER = ['db_stat_transactions', 'db_stat_tuple_read', 'db_stat_tuple_returned', 'db_stat_tuple_write', + 'backend_process', 'index_count', 'index_size', 'table_count', 'table_size', 'wal', 'background_writer'] + +CHARTS = { + 'db_stat_transactions': { + 'options': [None, 'Transactions on db', 'transactions/s', 'db statistics', 'postgres.db_stat_transactions', 'line'], + 'lines': [ + ['db_stat_xact_commit', 'committed', 'incremental'], + ['db_stat_xact_rollback', 'rolled back', 'incremental'] + ]}, + 'db_stat_connections': { + 'options': [None, 'Current connections to db', 'count', 'db statistics', 'postgres.db_stat_connections', 'line'], + 'lines': [ + ['db_stat_connections', 'connections', 'absolute'] + ]}, + 'db_stat_tuple_read': { + 'options': [None, 'Tuple reads from db', 'reads/s', 'db statistics', 'postgres.db_stat_tuple_read', 'line'], + 'lines': [ + ['db_stat_blks_read', 'disk', 'incremental'], + ['db_stat_blks_hit', 'cache', 'incremental'] + ]}, + 'db_stat_tuple_returned': { + 'options': [None, 'Tuples returned from db', 'tuples/s', 'db statistics', 'postgres.db_stat_tuple_returned', 'line'], + 'lines': [ + ['db_stat_tup_returned', 'sequential', 'incremental'], + ['db_stat_tup_fetched', 'bitmap', 'incremental'] + ]}, + 'db_stat_tuple_write': { + 'options': [None, 'Tuples written to db', 'writes/s', 'db statistics', 'postgres.db_stat_tuple_write', 'line'], + 'lines': [ + ['db_stat_tup_inserted', 'inserted', 'incremental'], + ['db_stat_tup_updated', 'updated', 'incremental'], + ['db_stat_tup_deleted', 'deleted', 'incremental'], + ['db_stat_conflicts', 'conflicts', 'incremental'] + ]}, + 'backend_process': { + 'options': [None, 'Current Backend Processes', 'processes', 'backend processes', 'postgres.backend_process', 'line'], + 'lines': [ + ['backend_process_active', 'active', 'absolute'], + ['backend_process_idle', 'idle', 'absolute'] + ]}, + 'index_count': { + 'options': [None, 'Total indexes', 'index', 'indexes', 'postgres.index_count', 'line'], + 'lines': [ + ['index_count', 'total', 'absolute'] + ]}, + 'index_size': { + 'options': [None, 'Indexes size', 'MB', 'indexes', 'postgres.index_size', 'line'], + 'lines': [ + ['index_size', 'size', 'absolute', 1, 1024 * 1024] + ]}, + 'table_count': { + 'options': [None, 'Total Tables', 'tables', 'tables', 'postgres.table_count', 'line'], + 'lines': [ + ['table_count', 'total', 'absolute'] + ]}, + 'table_size': { + 'options': [None, 'Tables size', 'MB', 'tables', 'postgres.table_size', 'line'], + 'lines': [ + ['table_size', 'size', 'absolute', 1, 1024 * 1024] + ]}, + 'wal': { + 'options': [None, 'Write-Ahead Logging Statistics', 'files/s', 'write ahead log', 'postgres.wal', 'line'], + 'lines': [ + ['wal_total', 'total', 'incremental'], + ['wal_ready', 'ready', 'incremental'], + ['wal_done', 'done', 'incremental'] + ]}, + 'background_writer': { + 'options': [None, 'Checkpoints', 'writes/s', 'background writer', 'postgres.background_writer', 'line'], + 'lines': [ + ['background_writer_scheduled', 'scheduled', 'incremental'], + ['background_writer_requested', 'requested', 'incremental'] + ]} +} + + +class Service(SimpleService): + def __init__(self, configuration=None, name=None): + super(self.__class__, self).__init__(configuration=configuration, name=name) + self.order = ORDER + self.definitions = CHARTS + self.table_stats = configuration.pop('table_stats', True) + self.index_stats = configuration.pop('index_stats', True) + self.configuration = configuration + self.connection = None + self.is_superuser = False + self.data = {} + self.databases = set() + + def _connect(self): + params = dict(user='postgres', + database=None, + password=None, + host='localhost', + port=5432) + params.update(self.configuration) + + if not self.connection: + self.connection = psycopg2.connect(**params) + self.connection.set_isolation_level(extensions.ISOLATION_LEVEL_AUTOCOMMIT) + self.connection.set_session(readonly=True) + + def check(self): + try: + self._connect() + cursor = self.connection.cursor() + self._discover_databases(cursor) + self._check_if_superuser(cursor) + cursor.close() + + self._create_definitions() + return True + except Exception as e: + self.error(str(e)) + return False + + def _discover_databases(self, cursor): + cursor.execute(""" + SELECT datname + FROM pg_stat_database + WHERE NOT datname ~* '^template\d+' + """) + self.databases = set(r[0] for r in cursor) + + def _check_if_superuser(self, cursor): + cursor.execute(""" + SELECT current_setting('is_superuser') = 'on' AS is_superuser; + """) + self.is_superuser = cursor.fetchone()[0] + + def _create_definitions(self): + for database_name in self.databases: + for chart_template_name in list(CHARTS): + if chart_template_name.startswith('db_stat'): + self._add_database_stat_chart(chart_template_name, database_name) + self._add_database_lock_chart(database_name) + + def _add_database_stat_chart(self, chart_template_name, database_name): + chart_template = CHARTS[chart_template_name] + chart_name = "{0}_{1}".format(database_name, chart_template_name) + if chart_name not in self.order: + self.order.insert(0, chart_name) + name, title, units, family, context, chart_type = chart_template['options'] + self.definitions[chart_name] = { + 'options': [ + name, + title + ': ' + database_name, + units, + 'db ' + database_name, + context, + chart_type + ] + } + + self.definitions[chart_name]['lines'] = [] + for line in deepcopy(chart_template['lines']): + line[0] = "{0}_{1}".format(database_name, line[0]) + self.definitions[chart_name]['lines'].append(line) + + def _add_database_lock_chart(self, database_name): + chart_name = "{0}_locks".format(database_name) + if chart_name not in self.order: + self.order.insert(-1, chart_name) + self.definitions[chart_name] = dict( + options= + [ + None, + 'Locks on db: ' + database_name, + 'locks', + 'db ' + database_name, + 'postgres.db_locks', + 'line' + ], + lines=[] + ) + + for lock_type in LOCK_TYPES: + lock_id = "{0}_{1}".format(database_name, lock_type) + label = re.sub("([a-z])([A-Z])", "\g<1> \g<2>", lock_type) + self.definitions[chart_name]['lines'].append([lock_id, label, 'absolute']) + + def _get_data(self): + self._connect() + + cursor = self.connection.cursor(cursor_factory=DictCursor) + self.add_stats(cursor) + + cursor.close() + return self.data + + def add_stats(self, cursor): + self.add_database_stats(cursor) + self.add_backend_stats(cursor) + if self.index_stats: + self.add_index_stats(cursor) + if self.table_stats: + self.add_table_stats(cursor) + self.add_lock_stats(cursor) + self.add_bgwriter_stats(cursor) + + # self.add_replication_stats(cursor) + + if self.is_superuser: + self.add_wal_stats(cursor) + + def add_database_stats(self, cursor): + cursor.execute(DATABASE) + for row in cursor: + database_name = row.get('database_name') + self.data["{0}_{1}".format(database_name, 'db_stat_xact_commit')] = int(row.get('xact_commit', 0)) + self.data["{0}_{1}".format(database_name, 'db_stat_xact_rollback')] = int(row.get('xact_rollback', 0)) + self.data["{0}_{1}".format(database_name, 'db_stat_blks_read')] = int(row.get('blks_read', 0)) + self.data["{0}_{1}".format(database_name, 'db_stat_blks_hit')] = int(row.get('blks_hit', 0)) + self.data["{0}_{1}".format(database_name, 'db_stat_tup_returned')] = int(row.get('tup_returned', 0)) + self.data["{0}_{1}".format(database_name, 'db_stat_tup_fetched')] = int(row.get('tup_fetched', 0)) + self.data["{0}_{1}".format(database_name, 'db_stat_tup_inserted')] = int(row.get('tup_inserted', 0)) + self.data["{0}_{1}".format(database_name, 'db_stat_tup_updated')] = int(row.get('tup_updated', 0)) + self.data["{0}_{1}".format(database_name, 'db_stat_tup_deleted')] = int(row.get('tup_deleted', 0)) + self.data["{0}_{1}".format(database_name, 'db_stat_conflicts')] = int(row.get('conflicts', 0)) + self.data["{0}_{1}".format(database_name, 'db_stat_connections')] = int(row.get('connections', 0)) + + def add_backend_stats(self, cursor): + cursor.execute(BACKENDS) + temp = cursor.fetchone() + + self.data['backend_process_active'] = int(temp.get('backends_active', 0)) + self.data['backend_process_idle'] = int(temp.get('backends_idle', 0)) + + def add_index_stats(self, cursor): + cursor.execute(INDEX_STATS) + temp = cursor.fetchone() + self.data['index_count'] = int(temp.get('indexes', 0)) + self.data['index_size'] = int(temp.get('size_indexes', 0)) + + def add_table_stats(self, cursor): + cursor.execute(TABLE_STATS) + temp = cursor.fetchone() + self.data['table_count'] = int(temp.get('relations', 0)) + self.data['table_size'] = int(temp.get('size_relations', 0)) + + def add_lock_stats(self, cursor): + cursor.execute(DATABASE_LOCKS) + + # zero out all current lock values + for database_name in self.databases: + for lock_type in LOCK_TYPES: + self.data["{0}_{1}".format(database_name, lock_type)] = 0 + + # populate those that have current locks + for row in cursor: + database_name, lock_type, lock_count = row + self.data["{0}_{1}".format(database_name, lock_type)] = lock_count + + def add_wal_stats(self, cursor): + cursor.execute(ARCHIVE) + temp = cursor.fetchone() + self.data['wal_total'] = int(temp.get('file_count', 0)) + self.data['wal_ready'] = int(temp.get('ready_count', 0)) + self.data['wal_done'] = int(temp.get('done_count', 0)) + + def add_bgwriter_stats(self, cursor): + cursor.execute(BGWRITER) + temp = cursor.fetchone() + self.data['background_writer_scheduled'] = temp.get('checkpoints_timed', 0) + self.data['background_writer_requested'] = temp.get('checkpoints_requests', 0) + +''' + def add_replication_stats(self, cursor): + cursor.execute(REPLICATION) + temp = cursor.fetchall() + for row in temp: + self.add_gauge_value('Replication/%s' % row.get('client_addr', 'Unknown'), + 'byte_lag', + int(row.get('byte_lag', 0))) +''' diff --git a/python.d/python_modules/base.py b/python.d/python_modules/base.py index 1508e0965..320c54bae 100644 --- a/python.d/python_modules/base.py +++ b/python.d/python_modules/base.py @@ -116,33 +116,21 @@ class SimpleService(threading.Thread): Return value presents exit status of update() :return: boolean """ - t_start = time.time() - timetable = self.timetable + t_start = float(time.time()) chart_name = self.chart_name - # check if it is time to execute job update() function - if timetable['next'] > t_start: - self.debug(chart_name, "will be run in", str(int((timetable['next'] - t_start) * 1000)), "ms") - return True - since_last = int((t_start - timetable['last']) * 1000000) - self.debug(chart_name, - "ready to run, after", str(int((t_start - timetable['last']) * 1000)), - "ms (update_every:", str(timetable['freq'] * 1000), - "ms, latency:", str(int((t_start - timetable['next']) * 1000)), "ms") + since_last = int((t_start - self.timetable['last']) * 1000000) if self.__first_run: since_last = 0 + if not self.update(since_last): self.error("update function failed.") return False - t_end = time.time() - self.timetable['next'] = t_end - (t_end % timetable['freq']) + timetable['freq'] + # draw performance graph - run_time = str(int((t_end - t_start) * 1000)) - # noinspection SqlNoDataSourceInspection + run_time = int((time.time() - t_start) * 1000) print("BEGIN netdata.plugin_pythond_%s %s\nSET run_time = %s\nEND\n" % - (self.chart_name, str(since_last), run_time)) - # sys.stdout.write("BEGIN netdata.plugin_pythond_%s %s\nSET run_time = %s\nEND\n" % - # (self.chart_name, str(since_last), run_time)) + (self.chart_name, str(since_last), str(run_time))) self.debug(chart_name, "updated in", str(run_time), "ms") self.timetable['last'] = t_start @@ -155,23 +143,48 @@ class SimpleService(threading.Thread): Exits when job failed or timed out. :return: None """ - self.timetable['last'] = time.time() + step = float(self.timetable['freq']) + penalty = 0 + self.timetable['last'] = float(time.time() - step) + self.debug("starting data collection - update frequency:", str(step), " retries allowed:", str(self.retries)) while True: # run forever, unless something is wrong + now = float(time.time()) + next = self.timetable['next'] = now - (now % step) + step + penalty + + # it is important to do this in a loop + # sleep() is interruptable + while now < next: + self.debug("sleeping for", str(next - now), "secs to reach frequency of", str(step), "secs, now:", str(now), " next:", str(next), " penalty:", str(penalty)) + time.sleep(next - now) + now = float(time.time()) + + # do the job try: status = self._run_once() except Exception as e: - self.error("Something wrong: ", str(e)) - return - if status: # handle retries if update failed - time.sleep(self.timetable['next'] - time.time()) + status = False + + if status: + # it is good self.retries_left = self.retries + penalty = 0 else: + # it failed self.retries_left -= 1 if self.retries_left <= 0: - self.error("no more retries. Exiting") - return + if penalty == 0: + penalty = float(self.retries * step) / 2 + else: + penalty *= 1.5 + + if penalty > 600: + penalty = 600 + + self.retries_left = self.retries + self.alert("failed to collect data for " + str(self.retries) + " times - increasing penalty to " + str(penalty) + " sec and trying again") + else: - time.sleep(self.timetable['freq']) + self.error("failed to collect data - " + str(self.retries_left) + " retries left - penalty: " + str(penalty) + " sec") # --- CHART --- @@ -307,7 +320,10 @@ class SimpleService(threading.Thread): """ Upload new data to netdata. """ - print(self._data_stream) + try: + print(self._data_stream) + except Exception as e: + msg.fatal('cannot send data to netdata:', str(e)) self._data_stream = "" # --- ERROR HANDLING --- @@ -318,6 +334,12 @@ class SimpleService(threading.Thread): """ msg.error(self.chart_name, *params) + def alert(self, *params): + """ + Show error message on stderr + """ + msg.alert(self.chart_name, *params) + def debug(self, *params): """ Show debug message on stderr @@ -345,10 +367,18 @@ class SimpleService(threading.Thread): :return: boolean """ self.debug("Module", str(self.__module__), "doesn't implement check() function. Using default.") - if self._get_data() is None or len(self._get_data()) == 0: + data = self._get_data() + + if data is None: + self.debug("failed to receive data during check().") return False - else: - return True + + if len(data) == 0: + self.debug("empty data during check().") + return False + + self.debug("successfully received data during check(): '" + str(data) + "'") + return True def create(self): """ @@ -357,6 +387,7 @@ class SimpleService(threading.Thread): """ data = self._get_data() if data is None: + self.debug("failed to receive data during create().") return False idx = 0 @@ -380,7 +411,7 @@ class SimpleService(threading.Thread): """ data = self._get_data() if data is None: - self.debug("_get_data() returned no data") + self.debug("failed to receive data during update().") return False updated = False @@ -458,7 +489,7 @@ class UrlService(SimpleService): return None try: - raw = f.read().decode('utf-8') + raw = f.read().decode('utf-8', 'ignore') except Exception as e: self.error(str(e)) finally: @@ -506,8 +537,83 @@ class SocketService(SimpleService): self.unix_socket = None self.request = "" self.__socket_config = None + self.__empty_request = "".encode() SimpleService.__init__(self, configuration=configuration, name=name) + def _socketerror(self, message=None): + if self.unix_socket is not None: + self.error("unix socket '" + self.unix_socket + "':", message) + else: + if self.__socket_config is not None: + af, socktype, proto, canonname, sa = self.__socket_config + self.error("socket to '" + str(sa[0]) + "' port " + str(sa[1]) + ":", message) + else: + self.error("unknown socket:", message) + + def _connect2socket(self, res=None): + """ + Connect to a socket, passing the result of getaddrinfo() + :return: boolean + """ + if res is None: + res = self.__socket_config + if res is None: + self.error("Cannot create socket to 'None':") + return False + + af, socktype, proto, canonname, sa = res + try: + self.debug("creating socket to '" + str(sa[0]) + "', port " + str(sa[1])) + self._sock = socket.socket(af, socktype, proto) + except socket.error as e: + self.error("Failed to create socket to '" + str(sa[0]) + "', port " + str(sa[1]) + ":", str(e)) + self._sock = None + self.__socket_config = None + return False + + try: + self.debug("connecting socket to '" + str(sa[0]) + "', port " + str(sa[1])) + self._sock.connect(sa) + except socket.error as e: + self.error("Failed to connect to '" + str(sa[0]) + "', port " + str(sa[1]) + ":", str(e)) + self._disconnect() + self.__socket_config = None + return False + + self.debug("connected to '" + str(sa[0]) + "', port " + str(sa[1])) + self.__socket_config = res + return True + + def _connect2unixsocket(self): + """ + Connect to a unix socket, given its filename + :return: boolean + """ + if self.unix_socket is None: + self.error("cannot connect to unix socket 'None'") + return False + + try: + self.debug("attempting DGRAM unix socket '" + str(self.unix_socket) + "'") + self._sock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM) + self._sock.connect(self.unix_socket) + self.debug("connected DGRAM unix socket '" + str(self.unix_socket) + "'") + return True + except socket.error as e: + self.debug("Failed to connect DGRAM unix socket '" + str(self.unix_socket) + "':", str(e)) + + try: + self.debug("attempting STREAM unix socket '" + str(self.unix_socket) + "'") + self._sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + self._sock.connect(self.unix_socket) + self.debug("connected STREAM unix socket '" + str(self.unix_socket) + "'") + return True + except socket.error as e: + self.debug("Failed to connect STREAM unix socket '" + str(self.unix_socket) + "':", str(e)) + self.error("Failed to connect to unix socket '" + str(self.unix_socket) + "':", str(e)) + self._sock = None + return False + def _connect(self): """ Recreate socket and connect to it since sockets cannot be reused after closing @@ -515,63 +621,38 @@ class SocketService(SimpleService): :return: """ try: - if self.unix_socket is None: - if self.__socket_config is None: - # establish ipv6 or ipv4 connection. - for res in socket.getaddrinfo(self.host, self.port, socket.AF_UNSPEC, socket.SOCK_STREAM): - try: - # noinspection SpellCheckingInspection - af, socktype, proto, canonname, sa = res - self._sock = socket.socket(af, socktype, proto) - except socket.error as e: - self.debug("Cannot create socket:", str(e)) - self._sock = None - continue - try: - self._sock.connect(sa) - except socket.error as e: - self.debug("Cannot connect to socket:", str(e)) - self._disconnect() - continue - self.__socket_config = res - break - else: - # connect to socket with previously established configuration - try: - af, socktype, proto, canonname, sa = self.__socket_config - self._sock = socket.socket(af, socktype, proto) - self._sock.connect(sa) - except socket.error as e: - self.debug("Cannot create or connect to socket:", str(e)) - self._disconnect() + if self.unix_socket is not None: + self._connect2unixsocket() + else: - # connect to 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) + if self.__socket_config is not None: + self._connect2socket() + else: + for res in socket.getaddrinfo(self.host, self.port, socket.AF_UNSPEC, socket.SOCK_STREAM): + if self._connect2socket(res): break except Exception as e: - self.error(str(e), - "Cannot create socket with following configuration: host:", str(self.host), - "port:", str(self.port), - "socket:", str(self.unix_socket)) self._sock = None - self._sock.setblocking(0) + self.__socket_config = None + + if self._sock is not None: + self._sock.setblocking(0) + self._sock.settimeout(5) + self.debug("set socket timeout to: " + str(self._sock.gettimeout())) def _disconnect(self): """ Close socket connection :return: """ - try: - self._sock.shutdown(2) # 0 - read, 1 - write, 2 - all - self._sock.close() - except Exception: - pass - self._sock = None + if self._sock is not None: + try: + self.debug("closing socket") + self._sock.shutdown(2) # 0 - read, 1 - write, 2 - all + self._sock.close() + except Exception: + pass + self._sock = None def _send(self): """ @@ -579,15 +660,13 @@ class SocketService(SimpleService): :return: boolean """ # Send request if it is needed - if self.request != "".encode(): + if self.request != self.__empty_request: try: + self.debug("sending request:", str(self.request)) self._sock.send(self.request) except Exception as e: + self._socketerror("error sending request:" + str(e)) self._disconnect() - self.error(str(e), - "used configuration: host:", str(self.host), - "port:", str(self.port), - "socket:", str(self.unix_socket)) return False return True @@ -598,24 +677,28 @@ class SocketService(SimpleService): """ data = "" while True: + self.debug("receiving response") try: - ready_to_read, _, in_error = select.select([self._sock], [], [], 5) + buf = self._sock.recv(4096) except Exception as e: - self.debug("SELECT", str(e)) + self._socketerror("failed to receive response:" + str(e)) self._disconnect() break - if len(ready_to_read) > 0: - buf = self._sock.recv(4096) - if len(buf) == 0 or buf is None: # handle server disconnect - break - data += buf.decode() - if self._check_raw_data(data): - break - else: - self.error("Socket timed out.") + + if buf is None or len(buf) == 0: # handle server disconnect + if data == "": + self._socketerror("unexpectedly disconnected") + else: + self.debug("server closed the connection") self._disconnect() break + self.debug("received data:", str(buf)) + data += buf.decode('utf-8', 'ignore') + if self._check_raw_data(data): + break + + self.debug("final response:", str(data)) return data def _get_raw_data(self): @@ -625,6 +708,8 @@ class SocketService(SimpleService): """ if self._sock is None: self._connect() + if self._sock is None: + return None # Send request if it is needed if not self._send(): @@ -654,6 +739,7 @@ class SocketService(SimpleService): self.name = "" else: self.name = str(self.name) + try: self.unix_socket = str(self.configuration['socket']) except (KeyError, TypeError): @@ -667,10 +753,12 @@ class SocketService(SimpleService): self.port = int(self.configuration['port']) except (KeyError, TypeError): self.debug("No port specified. Using: '" + str(self.port) + "'") + try: self.request = str(self.configuration['request']) except (KeyError, TypeError): self.debug("No request specified. Using: '" + str(self.request) + "'") + self.request = self.request.encode() def check(self): @@ -724,7 +812,7 @@ class LogService(SimpleService): try: self.log_path = str(self.configuration['path']) except (KeyError, TypeError): - self.error("No path to log specified. Using: '" + self.log_path + "'") + self.info("No path to log specified. Using: '" + self.log_path + "'") if os.access(self.log_path, os.R_OK): return True @@ -779,22 +867,25 @@ class ExecutableService(SimpleService): try: self.command = str(self.configuration['command']) except (KeyError, TypeError): - self.error("No command specified. Using: '" + self.command + "'") - command = self.command.split(' ') + self.info("No command specified. Using: '" + self.command + "'") + # Splitting self.command on every space so subprocess.Popen reads it properly + self.command = self.command.split(' ') - for arg in command[1:]: + for arg in self.command[1:]: if any(st in arg for st in self.bad_substrings): self.error("Bad command argument:" + " ".join(self.command[1:])) return False + # test command and search for it in /usr/sbin or /sbin when failed - base = command[0].split('/')[-1] + base = self.command[0].split('/')[-1] if self._get_raw_data() is None: for prefix in ['/sbin/', '/usr/sbin/']: - command[0] = prefix + base - if os.path.isfile(command[0]): + self.command[0] = prefix + base + if os.path.isfile(self.command[0]): break - self.command = command + if self._get_data() is None or len(self._get_data()) == 0: self.error("Command", self.command, "returned no data") return False + return True diff --git a/python.d/python_modules/msg.py b/python.d/python_modules/msg.py index ecefba0a4..74716770c 100644 --- a/python.d/python_modules/msg.py +++ b/python.d/python_modules/msg.py @@ -1,14 +1,17 @@ # -*- coding: utf-8 -*- # Description: logging for netdata python.d modules +import traceback import sys from time import time, strftime DEBUG_FLAG = False +TRACE_FLAG = False PROGRAM = "" -LOG_COUNTER = 2 -LOG_INTERVAL = 5 -NEXT_CHECK = 0 +LOG_COUNTER = 0 +LOG_THROTTLE = 10000 # has to be too big during init +LOG_INTERVAL = 1 # has to be too low during init +LOG_NEXT_CHECK = 0 WRITE = sys.stderr.write FLUSH = sys.stderr.flush @@ -20,23 +23,39 @@ def log_msg(msg_type, *args): :param msg_type: str """ global LOG_COUNTER - if not DEBUG_FLAG: - LOG_COUNTER -= 1 + global LOG_THROTTLE + global LOG_INTERVAL + global LOG_NEXT_CHECK now = time() - if LOG_COUNTER >= 0: - timestamp = strftime('%y-%m-%d %X') + + if not DEBUG_FLAG: + LOG_COUNTER += 1 + + # WRITE("COUNTER " + str(LOG_COUNTER) + " THROTTLE " + str(LOG_THROTTLE) + " INTERVAL " + str(LOG_INTERVAL) + " NOW " + str(now) + " NEXT " + str(LOG_NEXT_CHECK) + "\n") + + if LOG_COUNTER <= LOG_THROTTLE or msg_type == "FATAL" or msg_type == "ALERT": + timestamp = strftime('%Y-%m-%d %X') msg = "%s: %s %s: %s" % (timestamp, PROGRAM, str(msg_type), " ".join(args)) WRITE(msg + "\n") FLUSH() + elif LOG_COUNTER == LOG_THROTTLE + 1: + timestamp = strftime('%Y-%m-%d %X') + msg = "%s: python.d.plugin: throttling further log messages for %s seconds" % (timestamp, str(int(LOG_NEXT_CHECK + 0.5) - int(now))) + WRITE(msg + "\n") + FLUSH() - global NEXT_CHECK - if NEXT_CHECK <= now: - NEXT_CHECK = now - (now % LOG_INTERVAL) + LOG_INTERVAL - if LOG_COUNTER < 0: - timestamp = strftime('%y-%m-%d %X') - msg = "%s: Prevented %s log messages from displaying" % (timestamp, str(0 - LOG_COUNTER)) + if LOG_NEXT_CHECK <= now: + if LOG_COUNTER >= LOG_THROTTLE: + timestamp = strftime('%Y-%m-%d %X') + msg = "%s: python.d.plugin: Prevented %s log messages from displaying" % (timestamp, str(LOG_COUNTER - LOG_THROTTLE)) WRITE(msg + "\n") FLUSH() + LOG_NEXT_CHECK = now - (now % LOG_INTERVAL) + LOG_INTERVAL + LOG_COUNTER = 0 + + if TRACE_FLAG: + if msg_type == "FATAL" or msg_type == "ERROR" or msg_type == "ALERT": + traceback.print_exc() def debug(*args): @@ -56,6 +75,13 @@ def error(*args): log_msg("ERROR", *args) +def alert(*args): + """ + Print message on stderr. + """ + log_msg("ALERT", *args) + + def info(*args): """ Print message on stderr. @@ -67,8 +93,9 @@ def fatal(*args): """ Print message on stderr and exit. """ - log_msg("FATAL", *args) - # using sys.stdout causes IOError: Broken Pipe - print('DISABLE') - # sys.stdout.write('DISABLE\n') - sys.exit(1)
\ No newline at end of file + try: + log_msg("FATAL", *args) + print('DISABLE') + except: + pass + sys.exit(1) diff --git a/python.d/python_modules/pyyaml2/__init__.py b/python.d/python_modules/pyyaml2/__init__.py deleted file mode 100644 index 76e19e13f..000000000 --- a/python.d/python_modules/pyyaml2/__init__.py +++ /dev/null @@ -1,315 +0,0 @@ - -from error import * - -from tokens import * -from events import * -from nodes import * - -from loader import * -from dumper import * - -__version__ = '3.11' - -try: - from cyaml import * - __with_libyaml__ = True -except ImportError: - __with_libyaml__ = False - -def scan(stream, Loader=Loader): - """ - Scan a YAML stream and produce scanning tokens. - """ - loader = Loader(stream) - try: - while loader.check_token(): - yield loader.get_token() - finally: - loader.dispose() - -def parse(stream, Loader=Loader): - """ - Parse a YAML stream and produce parsing events. - """ - loader = Loader(stream) - try: - while loader.check_event(): - yield loader.get_event() - finally: - loader.dispose() - -def compose(stream, Loader=Loader): - """ - Parse the first YAML document in a stream - and produce the corresponding representation tree. - """ - loader = Loader(stream) - try: - return loader.get_single_node() - finally: - loader.dispose() - -def compose_all(stream, Loader=Loader): - """ - Parse all YAML documents in a stream - and produce corresponding representation trees. - """ - loader = Loader(stream) - try: - while loader.check_node(): - yield loader.get_node() - finally: - loader.dispose() - -def load(stream, Loader=Loader): - """ - Parse the first YAML document in a stream - and produce the corresponding Python object. - """ - loader = Loader(stream) - try: - return loader.get_single_data() - finally: - loader.dispose() - -def load_all(stream, Loader=Loader): - """ - Parse all YAML documents in a stream - and produce corresponding Python objects. - """ - loader = Loader(stream) - try: - while loader.check_data(): - yield loader.get_data() - finally: - loader.dispose() - -def safe_load(stream): - """ - Parse the first YAML document in a stream - and produce the corresponding Python object. - Resolve only basic YAML tags. - """ - return load(stream, SafeLoader) - -def safe_load_all(stream): - """ - Parse all YAML documents in a stream - and produce corresponding Python objects. - Resolve only basic YAML tags. - """ - return load_all(stream, SafeLoader) - -def emit(events, stream=None, Dumper=Dumper, - canonical=None, indent=None, width=None, - allow_unicode=None, line_break=None): - """ - Emit YAML parsing events into a stream. - If stream is None, return the produced string instead. - """ - getvalue = None - if stream is None: - from StringIO import StringIO - stream = StringIO() - getvalue = stream.getvalue - dumper = Dumper(stream, canonical=canonical, indent=indent, width=width, - allow_unicode=allow_unicode, line_break=line_break) - try: - for event in events: - dumper.emit(event) - finally: - dumper.dispose() - if getvalue: - return getvalue() - -def serialize_all(nodes, stream=None, Dumper=Dumper, - canonical=None, indent=None, width=None, - allow_unicode=None, line_break=None, - encoding='utf-8', explicit_start=None, explicit_end=None, - version=None, tags=None): - """ - Serialize a sequence of representation trees into a YAML stream. - If stream is None, return the produced string instead. - """ - getvalue = None - if stream is None: - if encoding is None: - from StringIO import StringIO - else: - from cStringIO import StringIO - stream = StringIO() - getvalue = stream.getvalue - dumper = Dumper(stream, canonical=canonical, indent=indent, width=width, - allow_unicode=allow_unicode, line_break=line_break, - encoding=encoding, version=version, tags=tags, - explicit_start=explicit_start, explicit_end=explicit_end) - try: - dumper.open() - for node in nodes: - dumper.serialize(node) - dumper.close() - finally: - dumper.dispose() - if getvalue: - return getvalue() - -def serialize(node, stream=None, Dumper=Dumper, **kwds): - """ - Serialize a representation tree into a YAML stream. - If stream is None, return the produced string instead. - """ - return serialize_all([node], stream, Dumper=Dumper, **kwds) - -def dump_all(documents, stream=None, Dumper=Dumper, - default_style=None, default_flow_style=None, - canonical=None, indent=None, width=None, - allow_unicode=None, line_break=None, - encoding='utf-8', explicit_start=None, explicit_end=None, - version=None, tags=None): - """ - Serialize a sequence of Python objects into a YAML stream. - If stream is None, return the produced string instead. - """ - getvalue = None - if stream is None: - if encoding is None: - from StringIO import StringIO - else: - from cStringIO import StringIO - stream = StringIO() - getvalue = stream.getvalue - dumper = Dumper(stream, default_style=default_style, - default_flow_style=default_flow_style, - canonical=canonical, indent=indent, width=width, - allow_unicode=allow_unicode, line_break=line_break, - encoding=encoding, version=version, tags=tags, - explicit_start=explicit_start, explicit_end=explicit_end) - try: - dumper.open() - for data in documents: - dumper.represent(data) - dumper.close() - finally: - dumper.dispose() - if getvalue: - return getvalue() - -def dump(data, stream=None, Dumper=Dumper, **kwds): - """ - Serialize a Python object into a YAML stream. - If stream is None, return the produced string instead. - """ - return dump_all([data], stream, Dumper=Dumper, **kwds) - -def safe_dump_all(documents, stream=None, **kwds): - """ - Serialize a sequence of Python objects into a YAML stream. - Produce only basic YAML tags. - If stream is None, return the produced string instead. - """ - return dump_all(documents, stream, Dumper=SafeDumper, **kwds) - -def safe_dump(data, stream=None, **kwds): - """ - Serialize a Python object into a YAML stream. - Produce only basic YAML tags. - If stream is None, return the produced string instead. - """ - return dump_all([data], stream, Dumper=SafeDumper, **kwds) - -def add_implicit_resolver(tag, regexp, first=None, - Loader=Loader, Dumper=Dumper): - """ - Add an implicit scalar detector. - If an implicit scalar value matches the given regexp, - the corresponding tag is assigned to the scalar. - first is a sequence of possible initial characters or None. - """ - Loader.add_implicit_resolver(tag, regexp, first) - Dumper.add_implicit_resolver(tag, regexp, first) - -def add_path_resolver(tag, path, kind=None, Loader=Loader, Dumper=Dumper): - """ - Add a path based resolver for the given tag. - A path is a list of keys that forms a path - to a node in the representation tree. - Keys can be string values, integers, or None. - """ - Loader.add_path_resolver(tag, path, kind) - Dumper.add_path_resolver(tag, path, kind) - -def add_constructor(tag, constructor, Loader=Loader): - """ - Add a constructor for the given tag. - Constructor is a function that accepts a Loader instance - and a node object and produces the corresponding Python object. - """ - Loader.add_constructor(tag, constructor) - -def add_multi_constructor(tag_prefix, multi_constructor, Loader=Loader): - """ - Add a multi-constructor for the given tag prefix. - Multi-constructor is called for a node if its tag starts with tag_prefix. - Multi-constructor accepts a Loader instance, a tag suffix, - and a node object and produces the corresponding Python object. - """ - Loader.add_multi_constructor(tag_prefix, multi_constructor) - -def add_representer(data_type, representer, Dumper=Dumper): - """ - Add a representer for the given type. - Representer is a function accepting a Dumper instance - and an instance of the given data type - and producing the corresponding representation node. - """ - Dumper.add_representer(data_type, representer) - -def add_multi_representer(data_type, multi_representer, Dumper=Dumper): - """ - Add a representer for the given type. - Multi-representer is a function accepting a Dumper instance - and an instance of the given data type or subtype - and producing the corresponding representation node. - """ - Dumper.add_multi_representer(data_type, multi_representer) - -class YAMLObjectMetaclass(type): - """ - The metaclass for YAMLObject. - """ - def __init__(cls, name, bases, kwds): - super(YAMLObjectMetaclass, cls).__init__(name, bases, kwds) - if 'yaml_tag' in kwds and kwds['yaml_tag'] is not None: - cls.yaml_loader.add_constructor(cls.yaml_tag, cls.from_yaml) - cls.yaml_dumper.add_representer(cls, cls.to_yaml) - -class YAMLObject(object): - """ - An object that can dump itself to a YAML stream - and load itself from a YAML stream. - """ - - __metaclass__ = YAMLObjectMetaclass - __slots__ = () # no direct instantiation, so allow immutable subclasses - - yaml_loader = Loader - yaml_dumper = Dumper - - yaml_tag = None - yaml_flow_style = None - - def from_yaml(cls, loader, node): - """ - Convert a representation node to a Python object. - """ - return loader.construct_yaml_object(node, cls) - from_yaml = classmethod(from_yaml) - - def to_yaml(cls, dumper, data): - """ - Convert a Python object to a representation node. - """ - return dumper.represent_yaml_object(cls.yaml_tag, data, cls, - flow_style=cls.yaml_flow_style) - to_yaml = classmethod(to_yaml) - diff --git a/python.d/python_modules/pyyaml2/composer.py b/python.d/python_modules/pyyaml2/composer.py deleted file mode 100644 index 06e5ac782..000000000 --- a/python.d/python_modules/pyyaml2/composer.py +++ /dev/null @@ -1,139 +0,0 @@ - -__all__ = ['Composer', 'ComposerError'] - -from error import MarkedYAMLError -from events import * -from nodes import * - -class ComposerError(MarkedYAMLError): - pass - -class Composer(object): - - def __init__(self): - self.anchors = {} - - def check_node(self): - # Drop the STREAM-START event. - if self.check_event(StreamStartEvent): - self.get_event() - - # If there are more documents available? - return not self.check_event(StreamEndEvent) - - def get_node(self): - # Get the root node of the next document. - if not self.check_event(StreamEndEvent): - return self.compose_document() - - def get_single_node(self): - # Drop the STREAM-START event. - self.get_event() - - # Compose a document if the stream is not empty. - document = None - if not self.check_event(StreamEndEvent): - document = self.compose_document() - - # Ensure that the stream contains no more documents. - if not self.check_event(StreamEndEvent): - event = self.get_event() - raise ComposerError("expected a single document in the stream", - document.start_mark, "but found another document", - event.start_mark) - - # Drop the STREAM-END event. - self.get_event() - - return document - - def compose_document(self): - # Drop the DOCUMENT-START event. - self.get_event() - - # Compose the root node. - node = self.compose_node(None, None) - - # Drop the DOCUMENT-END event. - self.get_event() - - self.anchors = {} - return node - - def compose_node(self, parent, index): - if self.check_event(AliasEvent): - event = self.get_event() - anchor = event.anchor - if anchor not in self.anchors: - raise ComposerError(None, None, "found undefined alias %r" - % anchor.encode('utf-8'), event.start_mark) - return self.anchors[anchor] - event = self.peek_event() - anchor = event.anchor - if anchor is not None: - if anchor in self.anchors: - raise ComposerError("found duplicate anchor %r; first occurence" - % anchor.encode('utf-8'), self.anchors[anchor].start_mark, - "second occurence", event.start_mark) - self.descend_resolver(parent, index) - if self.check_event(ScalarEvent): - node = self.compose_scalar_node(anchor) - elif self.check_event(SequenceStartEvent): - node = self.compose_sequence_node(anchor) - elif self.check_event(MappingStartEvent): - node = self.compose_mapping_node(anchor) - self.ascend_resolver() - return node - - def compose_scalar_node(self, anchor): - event = self.get_event() - tag = event.tag - if tag is None or tag == u'!': - tag = self.resolve(ScalarNode, event.value, event.implicit) - node = ScalarNode(tag, event.value, - event.start_mark, event.end_mark, style=event.style) - if anchor is not None: - self.anchors[anchor] = node - return node - - def compose_sequence_node(self, anchor): - start_event = self.get_event() - tag = start_event.tag - if tag is None or tag == u'!': - tag = self.resolve(SequenceNode, None, start_event.implicit) - node = SequenceNode(tag, [], - start_event.start_mark, None, - flow_style=start_event.flow_style) - if anchor is not None: - self.anchors[anchor] = node - index = 0 - while not self.check_event(SequenceEndEvent): - node.value.append(self.compose_node(node, index)) - index += 1 - end_event = self.get_event() - node.end_mark = end_event.end_mark - return node - - def compose_mapping_node(self, anchor): - start_event = self.get_event() - tag = start_event.tag - if tag is None or tag == u'!': - tag = self.resolve(MappingNode, None, start_event.implicit) - node = MappingNode(tag, [], - start_event.start_mark, None, - flow_style=start_event.flow_style) - if anchor is not None: - self.anchors[anchor] = node - while not self.check_event(MappingEndEvent): - #key_event = self.peek_event() - item_key = self.compose_node(node, None) - #if item_key in node.value: - # raise ComposerError("while composing a mapping", start_event.start_mark, - # "found duplicate key", key_event.start_mark) - item_value = self.compose_node(node, item_key) - #node.value[item_key] = item_value - node.value.append((item_key, item_value)) - end_event = self.get_event() - node.end_mark = end_event.end_mark - return node - diff --git a/python.d/python_modules/pyyaml2/constructor.py b/python.d/python_modules/pyyaml2/constructor.py deleted file mode 100644 index 635faac3e..000000000 --- a/python.d/python_modules/pyyaml2/constructor.py +++ /dev/null @@ -1,675 +0,0 @@ - -__all__ = ['BaseConstructor', 'SafeConstructor', 'Constructor', - 'ConstructorError'] - -from error import * -from nodes import * - -import datetime - -import binascii, re, sys, types - -class ConstructorError(MarkedYAMLError): - pass - -class BaseConstructor(object): - - yaml_constructors = {} - yaml_multi_constructors = {} - - def __init__(self): - self.constructed_objects = {} - self.recursive_objects = {} - self.state_generators = [] - self.deep_construct = False - - def check_data(self): - # If there are more documents available? - return self.check_node() - - def get_data(self): - # Construct and return the next document. - if self.check_node(): - return self.construct_document(self.get_node()) - - def get_single_data(self): - # Ensure that the stream contains a single document and construct it. - node = self.get_single_node() - if node is not None: - return self.construct_document(node) - return None - - def construct_document(self, node): - data = self.construct_object(node) - while self.state_generators: - state_generators = self.state_generators - self.state_generators = [] - for generator in state_generators: - for dummy in generator: - pass - self.constructed_objects = {} - self.recursive_objects = {} - self.deep_construct = False - return data - - def construct_object(self, node, deep=False): - if node in self.constructed_objects: - return self.constructed_objects[node] - if deep: - old_deep = self.deep_construct - self.deep_construct = True - if node in self.recursive_objects: - raise ConstructorError(None, None, - "found unconstructable recursive node", node.start_mark) - self.recursive_objects[node] = None - constructor = None - tag_suffix = None - if node.tag in self.yaml_constructors: - constructor = self.yaml_constructors[node.tag] - else: - for tag_prefix in self.yaml_multi_constructors: - if node.tag.startswith(tag_prefix): - tag_suffix = node.tag[len(tag_prefix):] - constructor = self.yaml_multi_constructors[tag_prefix] - break - else: - if None in self.yaml_multi_constructors: - tag_suffix = node.tag - constructor = self.yaml_multi_constructors[None] - elif None in self.yaml_constructors: - constructor = self.yaml_constructors[None] - elif isinstance(node, ScalarNode): - constructor = self.__class__.construct_scalar - elif isinstance(node, SequenceNode): - constructor = self.__class__.construct_sequence - elif isinstance(node, MappingNode): - constructor = self.__class__.construct_mapping - if tag_suffix is None: - data = constructor(self, node) - else: - data = constructor(self, tag_suffix, node) - if isinstance(data, types.GeneratorType): - generator = data - data = generator.next() - if self.deep_construct: - for dummy in generator: - pass - else: - self.state_generators.append(generator) - self.constructed_objects[node] = data - del self.recursive_objects[node] - if deep: - self.deep_construct = old_deep - return data - - def construct_scalar(self, node): - if not isinstance(node, ScalarNode): - raise ConstructorError(None, None, - "expected a scalar node, but found %s" % node.id, - node.start_mark) - return node.value - - def construct_sequence(self, node, deep=False): - if not isinstance(node, SequenceNode): - raise ConstructorError(None, None, - "expected a sequence node, but found %s" % node.id, - node.start_mark) - return [self.construct_object(child, deep=deep) - for child in node.value] - - def construct_mapping(self, node, deep=False): - if not isinstance(node, MappingNode): - raise ConstructorError(None, None, - "expected a mapping node, but found %s" % node.id, - node.start_mark) - mapping = {} - for key_node, value_node in node.value: - key = self.construct_object(key_node, deep=deep) - try: - hash(key) - except TypeError, exc: - raise ConstructorError("while constructing a mapping", node.start_mark, - "found unacceptable key (%s)" % exc, key_node.start_mark) - value = self.construct_object(value_node, deep=deep) - mapping[key] = value - return mapping - - def construct_pairs(self, node, deep=False): - if not isinstance(node, MappingNode): - raise ConstructorError(None, None, - "expected a mapping node, but found %s" % node.id, - node.start_mark) - pairs = [] - for key_node, value_node in node.value: - key = self.construct_object(key_node, deep=deep) - value = self.construct_object(value_node, deep=deep) - pairs.append((key, value)) - return pairs - - def add_constructor(cls, tag, constructor): - if not 'yaml_constructors' in cls.__dict__: - cls.yaml_constructors = cls.yaml_constructors.copy() - cls.yaml_constructors[tag] = constructor - add_constructor = classmethod(add_constructor) - - def add_multi_constructor(cls, tag_prefix, multi_constructor): - if not 'yaml_multi_constructors' in cls.__dict__: - cls.yaml_multi_constructors = cls.yaml_multi_constructors.copy() - cls.yaml_multi_constructors[tag_prefix] = multi_constructor - add_multi_constructor = classmethod(add_multi_constructor) - -class SafeConstructor(BaseConstructor): - - def construct_scalar(self, node): - if isinstance(node, MappingNode): - for key_node, value_node in node.value: - if key_node.tag == u'tag:yaml.org,2002:value': - return self.construct_scalar(value_node) - return BaseConstructor.construct_scalar(self, node) - - def flatten_mapping(self, node): - merge = [] - index = 0 - while index < len(node.value): - key_node, value_node = node.value[index] - if key_node.tag == u'tag:yaml.org,2002:merge': - del node.value[index] - if isinstance(value_node, MappingNode): - self.flatten_mapping(value_node) - merge.extend(value_node.value) - elif isinstance(value_node, SequenceNode): - submerge = [] - for subnode in value_node.value: - if not isinstance(subnode, MappingNode): - raise ConstructorError("while constructing a mapping", - node.start_mark, - "expected a mapping for merging, but found %s" - % subnode.id, subnode.start_mark) - self.flatten_mapping(subnode) - submerge.append(subnode.value) - submerge.reverse() - for value in submerge: - merge.extend(value) - else: - raise ConstructorError("while constructing a mapping", node.start_mark, - "expected a mapping or list of mappings for merging, but found %s" - % value_node.id, value_node.start_mark) - elif key_node.tag == u'tag:yaml.org,2002:value': - key_node.tag = u'tag:yaml.org,2002:str' - index += 1 - else: - index += 1 - if merge: - node.value = merge + node.value - - def construct_mapping(self, node, deep=False): - if isinstance(node, MappingNode): - self.flatten_mapping(node) - return BaseConstructor.construct_mapping(self, node, deep=deep) - - def construct_yaml_null(self, node): - self.construct_scalar(node) - return None - - bool_values = { - u'yes': True, - u'no': False, - u'true': True, - u'false': False, - u'on': True, - u'off': False, - } - - def construct_yaml_bool(self, node): - value = self.construct_scalar(node) - return self.bool_values[value.lower()] - - def construct_yaml_int(self, node): - value = str(self.construct_scalar(node)) - value = value.replace('_', '') - sign = +1 - if value[0] == '-': - sign = -1 - if value[0] in '+-': - value = value[1:] - if value == '0': - return 0 - elif value.startswith('0b'): - return sign*int(value[2:], 2) - elif value.startswith('0x'): - return sign*int(value[2:], 16) - elif value[0] == '0': - return sign*int(value, 8) - elif ':' in value: - digits = [int(part) for part in value.split(':')] - digits.reverse() - base = 1 - value = 0 - for digit in digits: - value += digit*base - base *= 60 - return sign*value - else: - return sign*int(value) - - inf_value = 1e300 - while inf_value != inf_value*inf_value: - inf_value *= inf_value - nan_value = -inf_value/inf_value # Trying to make a quiet NaN (like C99). - - def construct_yaml_float(self, node): - value = str(self.construct_scalar(node)) - value = value.replace('_', '').lower() - sign = +1 - if value[0] == '-': - sign = -1 - if value[0] in '+-': - value = value[1:] - if value == '.inf': - return sign*self.inf_value - elif value == '.nan': - return self.nan_value - elif ':' in value: - digits = [float(part) for part in value.split(':')] - digits.reverse() - base = 1 - value = 0.0 - for digit in digits: - value += digit*base - base *= 60 - return sign*value - else: - return sign*float(value) - - def construct_yaml_binary(self, node): - value = self.construct_scalar(node) - try: - return str(value).decode('base64') - except (binascii.Error, UnicodeEncodeError), exc: - raise ConstructorError(None, None, - "failed to decode base64 data: %s" % exc, node.start_mark) - - timestamp_regexp = re.compile( - ur'''^(?P<year>[0-9][0-9][0-9][0-9]) - -(?P<month>[0-9][0-9]?) - -(?P<day>[0-9][0-9]?) - (?:(?:[Tt]|[ \t]+) - (?P<hour>[0-9][0-9]?) - :(?P<minute>[0-9][0-9]) - :(?P<second>[0-9][0-9]) - (?:\.(?P<fraction>[0-9]*))? - (?:[ \t]*(?P<tz>Z|(?P<tz_sign>[-+])(?P<tz_hour>[0-9][0-9]?) - (?::(?P<tz_minute>[0-9][0-9]))?))?)?$''', re.X) - - def construct_yaml_timestamp(self, node): - value = self.construct_scalar(node) - match = self.timestamp_regexp.match(node.value) - values = match.groupdict() - year = int(values['year']) - month = int(values['month']) - day = int(values['day']) - if not values['hour']: - return datetime.date(year, month, day) - hour = int(values['hour']) - minute = int(values['minute']) - second = int(values['second']) - fraction = 0 - if values['fraction']: - fraction = values['fraction'][:6] - while len(fraction) < 6: - fraction += '0' - fraction = int(fraction) - delta = None - if values['tz_sign']: - tz_hour = int(values['tz_hour']) - tz_minute = int(values['tz_minute'] or 0) - delta = datetime.timedelta(hours=tz_hour, minutes=tz_minute) - if values['tz_sign'] == '-': - delta = -delta - data = datetime.datetime(year, month, day, hour, minute, second, fraction) - if delta: - data -= delta - return data - - def construct_yaml_omap(self, node): - # Note: we do not check for duplicate keys, because it's too - # CPU-expensive. - omap = [] - yield omap - if not isinstance(node, SequenceNode): - raise ConstructorError("while constructing an ordered map", node.start_mark, - "expected a sequence, but found %s" % node.id, node.start_mark) - for subnode in node.value: - if not isinstance(subnode, MappingNode): - raise ConstructorError("while constructing an ordered map", node.start_mark, - "expected a mapping of length 1, but found %s" % subnode.id, - subnode.start_mark) - if len(subnode.value) != 1: - raise ConstructorError("while constructing an ordered map", node.start_mark, - "expected a single mapping item, but found %d items" % len(subnode.value), - subnode.start_mark) - key_node, value_node = subnode.value[0] - key = self.construct_object(key_node) - value = self.construct_object(value_node) - omap.append((key, value)) - - def construct_yaml_pairs(self, node): - # Note: the same code as `construct_yaml_omap`. - pairs = [] - yield pairs - if not isinstance(node, SequenceNode): - raise ConstructorError("while constructing pairs", node.start_mark, - "expected a sequence, but found %s" % node.id, node.start_mark) - for subnode in node.value: - if not isinstance(subnode, MappingNode): - raise ConstructorError("while constructing pairs", node.start_mark, - "expected a mapping of length 1, but found %s" % subnode.id, - subnode.start_mark) - if len(subnode.value) != 1: - raise ConstructorError("while constructing pairs", node.start_mark, - "expected a single mapping item, but found %d items" % len(subnode.value), - subnode.start_mark) - key_node, value_node = subnode.value[0] - key = self.construct_object(key_node) - value = self.construct_object(value_node) - pairs.append((key, value)) - - def construct_yaml_set(self, node): - data = set() - yield data - value = self.construct_mapping(node) - data.update(value) - - def construct_yaml_str(self, node): - value = self.construct_scalar(node) - try: - return value.encode('ascii') - except UnicodeEncodeError: - return value - - def construct_yaml_seq(self, node): - data = [] - yield data - data.extend(self.construct_sequence(node)) - - def construct_yaml_map(self, node): - data = {} - yield data - value = self.construct_mapping(node) - data.update(value) - - def construct_yaml_object(self, node, cls): - data = cls.__new__(cls) - yield data - if hasattr(data, '__setstate__'): - state = self.construct_mapping(node, deep=True) - data.__setstate__(state) - else: - state = self.construct_mapping(node) - data.__dict__.update(state) - - def construct_undefined(self, node): - raise ConstructorError(None, None, - "could not determine a constructor for the tag %r" % node.tag.encode('utf-8'), - node.start_mark) - -SafeConstructor.add_constructor( - u'tag:yaml.org,2002:null', - SafeConstructor.construct_yaml_null) - -SafeConstructor.add_constructor( - u'tag:yaml.org,2002:bool', - SafeConstructor.construct_yaml_bool) - -SafeConstructor.add_constructor( - u'tag:yaml.org,2002:int', - SafeConstructor.construct_yaml_int) - -SafeConstructor.add_constructor( - u'tag:yaml.org,2002:float', - SafeConstructor.construct_yaml_float) - -SafeConstructor.add_constructor( - u'tag:yaml.org,2002:binary', - SafeConstructor.construct_yaml_binary) - -SafeConstructor.add_constructor( - u'tag:yaml.org,2002:timestamp', - SafeConstructor.construct_yaml_timestamp) - -SafeConstructor.add_constructor( - u'tag:yaml.org,2002:omap', - SafeConstructor.construct_yaml_omap) - -SafeConstructor.add_constructor( - u'tag:yaml.org,2002:pairs', - SafeConstructor.construct_yaml_pairs) - -SafeConstructor.add_constructor( - u'tag:yaml.org,2002:set', - SafeConstructor.construct_yaml_set) - -SafeConstructor.add_constructor( - u'tag:yaml.org,2002:str', - SafeConstructor.construct_yaml_str) - -SafeConstructor.add_constructor( - u'tag:yaml.org,2002:seq', - SafeConstructor.construct_yaml_seq) - -SafeConstructor.add_constructor( - u'tag:yaml.org,2002:map', - SafeConstructor.construct_yaml_map) - -SafeConstructor.add_constructor(None, - SafeConstructor.construct_undefined) - -class Constructor(SafeConstructor): - - def construct_python_str(self, node): - return self.construct_scalar(node).encode('utf-8') - - def construct_python_unicode(self, node): - return self.construct_scalar(node) - - def construct_python_long(self, node): - return long(self.construct_yaml_int(node)) - - def construct_python_complex(self, node): - return complex(self.construct_scalar(node)) - - def construct_python_tuple(self, node): - return tuple(self.construct_sequence(node)) - - def find_python_module(self, name, mark): - if not name: - raise ConstructorError("while constructing a Python module", mark, - "expected non-empty name appended to the tag", mark) - try: - __import__(name) - except ImportError, exc: - raise ConstructorError("while constructing a Python module", mark, - "cannot find module %r (%s)" % (name.encode('utf-8'), exc), mark) - return sys.modules[name] - - def find_python_name(self, name, mark): - if not name: - raise ConstructorError("while constructing a Python object", mark, - "expected non-empty name appended to the tag", mark) - if u'.' in name: - module_name, object_name = name.rsplit('.', 1) - else: - module_name = '__builtin__' - object_name = name - try: - __import__(module_name) - except ImportError, exc: - raise ConstructorError("while constructing a Python object", mark, - "cannot find module %r (%s)" % (module_name.encode('utf-8'), exc), mark) - module = sys.modules[module_name] - if not hasattr(module, object_name): - raise ConstructorError("while constructing a Python object", mark, - "cannot find %r in the module %r" % (object_name.encode('utf-8'), - module.__name__), mark) - return getattr(module, object_name) - - def construct_python_name(self, suffix, node): - value = self.construct_scalar(node) - if value: - raise ConstructorError("while constructing a Python name", node.start_mark, - "expected the empty value, but found %r" % value.encode('utf-8'), - node.start_mark) - return self.find_python_name(suffix, node.start_mark) - - def construct_python_module(self, suffix, node): - value = self.construct_scalar(node) - if value: - raise ConstructorError("while constructing a Python module", node.start_mark, - "expected the empty value, but found %r" % value.encode('utf-8'), - node.start_mark) - return self.find_python_module(suffix, node.start_mark) - - class classobj: pass - - def make_python_instance(self, suffix, node, - args=None, kwds=None, newobj=False): - if not args: - args = [] - if not kwds: - kwds = {} - cls = self.find_python_name(suffix, node.start_mark) - if newobj and isinstance(cls, type(self.classobj)) \ - and not args and not kwds: - instance = self.classobj() - instance.__class__ = cls - return instance - elif newobj and isinstance(cls, type): - return cls.__new__(cls, *args, **kwds) - else: - return cls(*args, **kwds) - - def set_python_instance_state(self, instance, state): - if hasattr(instance, '__setstate__'): - instance.__setstate__(state) - else: - slotstate = {} - if isinstance(state, tuple) and len(state) == 2: - state, slotstate = state - if hasattr(instance, '__dict__'): - instance.__dict__.update(state) - elif state: - slotstate.update(state) - for key, value in slotstate.items(): - setattr(object, key, value) - - def construct_python_object(self, suffix, node): - # Format: - # !!python/object:module.name { ... state ... } - instance = self.make_python_instance(suffix, node, newobj=True) - yield instance - deep = hasattr(instance, '__setstate__') - state = self.construct_mapping(node, deep=deep) - self.set_python_instance_state(instance, state) - - def construct_python_object_apply(self, suffix, node, newobj=False): - # Format: - # !!python/object/apply # (or !!python/object/new) - # args: [ ... arguments ... ] - # kwds: { ... keywords ... } - # state: ... state ... - # listitems: [ ... listitems ... ] - # dictitems: { ... dictitems ... } - # or short format: - # !!python/object/apply [ ... arguments ... ] - # The difference between !!python/object/apply and !!python/object/new - # is how an object is created, check make_python_instance for details. - if isinstance(node, SequenceNode): - args = self.construct_sequence(node, deep=True) - kwds = {} - state = {} - listitems = [] - dictitems = {} - else: - value = self.construct_mapping(node, deep=True) - args = value.get('args', []) - kwds = value.get('kwds', {}) - state = value.get('state', {}) - listitems = value.get('listitems', []) - dictitems = value.get('dictitems', {}) - instance = self.make_python_instance(suffix, node, args, kwds, newobj) - if state: - self.set_python_instance_state(instance, state) - if listitems: - instance.extend(listitems) - if dictitems: - for key in dictitems: - instance[key] = dictitems[key] - return instance - - def construct_python_object_new(self, suffix, node): - return self.construct_python_object_apply(suffix, node, newobj=True) - -Constructor.add_constructor( - u'tag:yaml.org,2002:python/none', - Constructor.construct_yaml_null) - -Constructor.add_constructor( - u'tag:yaml.org,2002:python/bool', - Constructor.construct_yaml_bool) - -Constructor.add_constructor( - u'tag:yaml.org,2002:python/str', - Constructor.construct_python_str) - -Constructor.add_constructor( - u'tag:yaml.org,2002:python/unicode', - Constructor.construct_python_unicode) - -Constructor.add_constructor( - u'tag:yaml.org,2002:python/int', - Constructor.construct_yaml_int) - -Constructor.add_constructor( - u'tag:yaml.org,2002:python/long', - Constructor.construct_python_long) - -Constructor.add_constructor( - u'tag:yaml.org,2002:python/float', - Constructor.construct_yaml_float) - -Constructor.add_constructor( - u'tag:yaml.org,2002:python/complex', - Constructor.construct_python_complex) - -Constructor.add_constructor( - u'tag:yaml.org,2002:python/list', - Constructor.construct_yaml_seq) - -Constructor.add_constructor( - u'tag:yaml.org,2002:python/tuple', - Constructor.construct_python_tuple) - -Constructor.add_constructor( - u'tag:yaml.org,2002:python/dict', - Constructor.construct_yaml_map) - -Constructor.add_multi_constructor( - u'tag:yaml.org,2002:python/name:', - Constructor.construct_python_name) - -Constructor.add_multi_constructor( - u'tag:yaml.org,2002:python/module:', - Constructor.construct_python_module) - -Constructor.add_multi_constructor( - u'tag:yaml.org,2002:python/object:', - Constructor.construct_python_object) - -Constructor.add_multi_constructor( - u'tag:yaml.org,2002:python/object/apply:', - Constructor.construct_python_object_apply) - -Constructor.add_multi_constructor( - u'tag:yaml.org,2002:python/object/new:', - Constructor.construct_python_object_new) - diff --git a/python.d/python_modules/pyyaml2/cyaml.py b/python.d/python_modules/pyyaml2/cyaml.py deleted file mode 100644 index 68dcd7519..000000000 --- a/python.d/python_modules/pyyaml2/cyaml.py +++ /dev/null @@ -1,85 +0,0 @@ - -__all__ = ['CBaseLoader', 'CSafeLoader', 'CLoader', - 'CBaseDumper', 'CSafeDumper', 'CDumper'] - -from _yaml import CParser, CEmitter - -from constructor import * - -from serializer import * -from representer import * - -from resolver import * - -class CBaseLoader(CParser, BaseConstructor, BaseResolver): - - def __init__(self, stream): - CParser.__init__(self, stream) - BaseConstructor.__init__(self) - BaseResolver.__init__(self) - -class CSafeLoader(CParser, SafeConstructor, Resolver): - - def __init__(self, stream): - CParser.__init__(self, stream) - SafeConstructor.__init__(self) - Resolver.__init__(self) - -class CLoader(CParser, Constructor, Resolver): - - def __init__(self, stream): - CParser.__init__(self, stream) - Constructor.__init__(self) - Resolver.__init__(self) - -class CBaseDumper(CEmitter, BaseRepresenter, BaseResolver): - - def __init__(self, stream, - default_style=None, default_flow_style=None, - canonical=None, indent=None, width=None, - allow_unicode=None, line_break=None, - encoding=None, explicit_start=None, explicit_end=None, - version=None, tags=None): - CEmitter.__init__(self, stream, canonical=canonical, - indent=indent, width=width, encoding=encoding, - allow_unicode=allow_unicode, line_break=line_break, - explicit_start=explicit_start, explicit_end=explicit_end, - version=version, tags=tags) - Representer.__init__(self, default_style=default_style, - default_flow_style=default_flow_style) - Resolver.__init__(self) - -class CSafeDumper(CEmitter, SafeRepresenter, Resolver): - - def __init__(self, stream, - default_style=None, default_flow_style=None, - canonical=None, indent=None, width=None, - allow_unicode=None, line_break=None, - encoding=None, explicit_start=None, explicit_end=None, - version=None, tags=None): - CEmitter.__init__(self, stream, canonical=canonical, - indent=indent, width=width, encoding=encoding, - allow_unicode=allow_unicode, line_break=line_break, - explicit_start=explicit_start, explicit_end=explicit_end, - version=version, tags=tags) - SafeRepresenter.__init__(self, default_style=default_style, - default_flow_style=default_flow_style) - Resolver.__init__(self) - -class CDumper(CEmitter, Serializer, Representer, Resolver): - - def __init__(self, stream, - default_style=None, default_flow_style=None, - canonical=None, indent=None, width=None, - allow_unicode=None, line_break=None, - encoding=None, explicit_start=None, explicit_end=None, - version=None, tags=None): - CEmitter.__init__(self, stream, canonical=canonical, - indent=indent, width=width, encoding=encoding, - allow_unicode=allow_unicode, line_break=line_break, - explicit_start=explicit_start, explicit_end=explicit_end, - version=version, tags=tags) - Representer.__init__(self, default_style=default_style, - default_flow_style=default_flow_style) - Resolver.__init__(self) - diff --git a/python.d/python_modules/pyyaml2/dumper.py b/python.d/python_modules/pyyaml2/dumper.py deleted file mode 100644 index f811d2c91..000000000 --- a/python.d/python_modules/pyyaml2/dumper.py +++ /dev/null @@ -1,62 +0,0 @@ - -__all__ = ['BaseDumper', 'SafeDumper', 'Dumper'] - -from emitter import * -from serializer import * -from representer import * -from resolver import * - -class BaseDumper(Emitter, Serializer, BaseRepresenter, BaseResolver): - - def __init__(self, stream, - default_style=None, default_flow_style=None, - canonical=None, indent=None, width=None, - allow_unicode=None, line_break=None, - encoding=None, explicit_start=None, explicit_end=None, - version=None, tags=None): - Emitter.__init__(self, stream, canonical=canonical, - indent=indent, width=width, - allow_unicode=allow_unicode, line_break=line_break) - Serializer.__init__(self, encoding=encoding, - explicit_start=explicit_start, explicit_end=explicit_end, - version=version, tags=tags) - Representer.__init__(self, default_style=default_style, - default_flow_style=default_flow_style) - Resolver.__init__(self) - -class SafeDumper(Emitter, Serializer, SafeRepresenter, Resolver): - - def __init__(self, stream, - default_style=None, default_flow_style=None, - canonical=None, indent=None, width=None, - allow_unicode=None, line_break=None, - encoding=None, explicit_start=None, explicit_end=None, - version=None, tags=None): - Emitter.__init__(self, stream, canonical=canonical, - indent=indent, width=width, - allow_unicode=allow_unicode, line_break=line_break) - Serializer.__init__(self, encoding=encoding, - explicit_start=explicit_start, explicit_end=explicit_end, - version=version, tags=tags) - SafeRepresenter.__init__(self, default_style=default_style, - default_flow_style=default_flow_style) - Resolver.__init__(self) - -class Dumper(Emitter, Serializer, Representer, Resolver): - - def __init__(self, stream, - default_style=None, default_flow_style=None, - canonical=None, indent=None, width=None, - allow_unicode=None, line_break=None, - encoding=None, explicit_start=None, explicit_end=None, - version=None, tags=None): - Emitter.__init__(self, stream, canonical=canonical, - indent=indent, width=width, - allow_unicode=allow_unicode, line_break=line_break) - Serializer.__init__(self, encoding=encoding, - explicit_start=explicit_start, explicit_end=explicit_end, - version=version, tags=tags) - Representer.__init__(self, default_style=default_style, - default_flow_style=default_flow_style) - Resolver.__init__(self) - diff --git a/python.d/python_modules/pyyaml2/emitter.py b/python.d/python_modules/pyyaml2/emitter.py deleted file mode 100644 index e5bcdcccb..000000000 --- a/python.d/python_modules/pyyaml2/emitter.py +++ /dev/null @@ -1,1140 +0,0 @@ - -# Emitter expects events obeying the following grammar: -# stream ::= STREAM-START document* STREAM-END -# document ::= DOCUMENT-START node DOCUMENT-END -# node ::= SCALAR | sequence | mapping -# sequence ::= SEQUENCE-START node* SEQUENCE-END -# mapping ::= MAPPING-START (node node)* MAPPING-END - -__all__ = ['Emitter', 'EmitterError'] - -from error import YAMLError -from events import * - -class EmitterError(YAMLError): - pass - -class ScalarAnalysis(object): - def __init__(self, scalar, empty, multiline, - allow_flow_plain, allow_block_plain, - allow_single_quoted, allow_double_quoted, - allow_block): - self.scalar = scalar - self.empty = empty - self.multiline = multiline - self.allow_flow_plain = allow_flow_plain - self.allow_block_plain = allow_block_plain - self.allow_single_quoted = allow_single_quoted - self.allow_double_quoted = allow_double_quoted - self.allow_block = allow_block - -class Emitter(object): - - DEFAULT_TAG_PREFIXES = { - u'!' : u'!', - u'tag:yaml.org,2002:' : u'!!', - } - - def __init__(self, stream, canonical=None, indent=None, width=None, - allow_unicode=None, line_break=None): - - # The stream should have the methods `write` and possibly `flush`. - self.stream = stream - - # Encoding can be overriden by STREAM-START. - self.encoding = None - - # Emitter is a state machine with a stack of states to handle nested - # structures. - self.states = [] - self.state = self.expect_stream_start - - # Current event and the event queue. - self.events = [] - self.event = None - - # The current indentation level and the stack of previous indents. - self.indents = [] - self.indent = None - - # Flow level. - self.flow_level = 0 - - # Contexts. - self.root_context = False - self.sequence_context = False - self.mapping_context = False - self.simple_key_context = False - - # Characteristics of the last emitted character: - # - current position. - # - is it a whitespace? - # - is it an indention character - # (indentation space, '-', '?', or ':')? - self.line = 0 - self.column = 0 - self.whitespace = True - self.indention = True - - # Whether the document requires an explicit document indicator - self.open_ended = False - - # Formatting details. - self.canonical = canonical - self.allow_unicode = allow_unicode - self.best_indent = 2 - if indent and 1 < indent < 10: - self.best_indent = indent - self.best_width = 80 - if width and width > self.best_indent*2: - self.best_width = width - self.best_line_break = u'\n' - if line_break in [u'\r', u'\n', u'\r\n']: - self.best_line_break = line_break - - # Tag prefixes. - self.tag_prefixes = None - - # Prepared anchor and tag. - self.prepared_anchor = None - self.prepared_tag = None - - # Scalar analysis and style. - self.analysis = None - self.style = None - - def dispose(self): - # Reset the state attributes (to clear self-references) - self.states = [] - self.state = None - - def emit(self, event): - self.events.append(event) - while not self.need_more_events(): - self.event = self.events.pop(0) - self.state() - self.event = None - - # In some cases, we wait for a few next events before emitting. - - def need_more_events(self): - if not self.events: - return True - event = self.events[0] - if isinstance(event, DocumentStartEvent): - return self.need_events(1) - elif isinstance(event, SequenceStartEvent): - return self.need_events(2) - elif isinstance(event, MappingStartEvent): - return self.need_events(3) - else: - return False - - def need_events(self, count): - level = 0 - for event in self.events[1:]: - if isinstance(event, (DocumentStartEvent, CollectionStartEvent)): - level += 1 - elif isinstance(event, (DocumentEndEvent, CollectionEndEvent)): - level -= 1 - elif isinstance(event, StreamEndEvent): - level = -1 - if level < 0: - return False - return (len(self.events) < count+1) - - def increase_indent(self, flow=False, indentless=False): - self.indents.append(self.indent) - if self.indent is None: - if flow: - self.indent = self.best_indent - else: - self.indent = 0 - elif not indentless: - self.indent += self.best_indent - - # States. - - # Stream handlers. - - def expect_stream_start(self): - if isinstance(self.event, StreamStartEvent): - if self.event.encoding and not getattr(self.stream, 'encoding', None): - self.encoding = self.event.encoding - self.write_stream_start() - self.state = self.expect_first_document_start - else: - raise EmitterError("expected StreamStartEvent, but got %s" - % self.event) - - def expect_nothing(self): - raise EmitterError("expected nothing, but got %s" % self.event) - - # Document handlers. - - def expect_first_document_start(self): - return self.expect_document_start(first=True) - - def expect_document_start(self, first=False): - if isinstance(self.event, DocumentStartEvent): - if (self.event.version or self.event.tags) and self.open_ended: - self.write_indicator(u'...', True) - self.write_indent() - if self.event.version: - version_text = self.prepare_version(self.event.version) - self.write_version_directive(version_text) - self.tag_prefixes = self.DEFAULT_TAG_PREFIXES.copy() - if self.event.tags: - handles = self.event.tags.keys() - handles.sort() - for handle in handles: - prefix = self.event.tags[handle] - self.tag_prefixes[prefix] = handle - handle_text = self.prepare_tag_handle(handle) - prefix_text = self.prepare_tag_prefix(prefix) - self.write_tag_directive(handle_text, prefix_text) - implicit = (first and not self.event.explicit and not self.canonical - and not self.event.version and not self.event.tags - and not self.check_empty_document()) - if not implicit: - self.write_indent() - self.write_indicator(u'---', True) - if self.canonical: - self.write_indent() - self.state = self.expect_document_root - elif isinstance(self.event, StreamEndEvent): - if self.open_ended: - self.write_indicator(u'...', True) - self.write_indent() - self.write_stream_end() - self.state = self.expect_nothing - else: - raise EmitterError("expected DocumentStartEvent, but got %s" - % self.event) - - def expect_document_end(self): - if isinstance(self.event, DocumentEndEvent): - self.write_indent() - if self.event.explicit: - self.write_indicator(u'...', True) - self.write_indent() - self.flush_stream() - self.state = self.expect_document_start - else: - raise EmitterError("expected DocumentEndEvent, but got %s" - % self.event) - - def expect_document_root(self): - self.states.append(self.expect_document_end) - self.expect_node(root=True) - - # Node handlers. - - def expect_node(self, root=False, sequence=False, mapping=False, - simple_key=False): - self.root_context = root - self.sequence_context = sequence - self.mapping_context = mapping - self.simple_key_context = simple_key - if isinstance(self.event, AliasEvent): - self.expect_alias() - elif isinstance(self.event, (ScalarEvent, CollectionStartEvent)): - self.process_anchor(u'&') - self.process_tag() - if isinstance(self.event, ScalarEvent): - self.expect_scalar() - elif isinstance(self.event, SequenceStartEvent): - if self.flow_level or self.canonical or self.event.flow_style \ - or self.check_empty_sequence(): - self.expect_flow_sequence() - else: - self.expect_block_sequence() - elif isinstance(self.event, MappingStartEvent): - if self.flow_level or self.canonical or self.event.flow_style \ - or self.check_empty_mapping(): - self.expect_flow_mapping() - else: - self.expect_block_mapping() - else: - raise EmitterError("expected NodeEvent, but got %s" % self.event) - - def expect_alias(self): - if self.event.anchor is None: - raise EmitterError("anchor is not specified for alias") - self.process_anchor(u'*') - self.state = self.states.pop() - - def expect_scalar(self): - self.increase_indent(flow=True) - self.process_scalar() - self.indent = self.indents.pop() - self.state = self.states.pop() - - # Flow sequence handlers. - - def expect_flow_sequence(self): - self.write_indicator(u'[', True, whitespace=True) - self.flow_level += 1 - self.increase_indent(flow=True) - self.state = self.expect_first_flow_sequence_item - - def expect_first_flow_sequence_item(self): - if isinstance(self.event, SequenceEndEvent): - self.indent = self.indents.pop() - self.flow_level -= 1 - self.write_indicator(u']', False) - self.state = self.states.pop() - else: - if self.canonical or self.column > self.best_width: - self.write_indent() - self.states.append(self.expect_flow_sequence_item) - self.expect_node(sequence=True) - - def expect_flow_sequence_item(self): - if isinstance(self.event, SequenceEndEvent): - self.indent = self.indents.pop() - self.flow_level -= 1 - if self.canonical: - self.write_indicator(u',', False) - self.write_indent() - self.write_indicator(u']', False) - self.state = self.states.pop() - else: - self.write_indicator(u',', False) - if self.canonical or self.column > self.best_width: - self.write_indent() - self.states.append(self.expect_flow_sequence_item) - self.expect_node(sequence=True) - - # Flow mapping handlers. - - def expect_flow_mapping(self): - self.write_indicator(u'{', True, whitespace=True) - self.flow_level += 1 - self.increase_indent(flow=True) - self.state = self.expect_first_flow_mapping_key - - def expect_first_flow_mapping_key(self): - if isinstance(self.event, MappingEndEvent): - self.indent = self.indents.pop() - self.flow_level -= 1 - self.write_indicator(u'}', False) - self.state = self.states.pop() - else: - if self.canonical or self.column > self.best_width: - self.write_indent() - if not self.canonical and self.check_simple_key(): - self.states.append(self.expect_flow_mapping_simple_value) - self.expect_node(mapping=True, simple_key=True) - else: - self.write_indicator(u'?', True) - self.states.append(self.expect_flow_mapping_value) - self.expect_node(mapping=True) - - def expect_flow_mapping_key(self): - if isinstance(self.event, MappingEndEvent): - self.indent = self.indents.pop() - self.flow_level -= 1 - if self.canonical: - self.write_indicator(u',', False) - self.write_indent() - self.write_indicator(u'}', False) - self.state = self.states.pop() - else: - self.write_indicator(u',', False) - if self.canonical or self.column > self.best_width: - self.write_indent() - if not self.canonical and self.check_simple_key(): - self.states.append(self.expect_flow_mapping_simple_value) - self.expect_node(mapping=True, simple_key=True) - else: - self.write_indicator(u'?', True) - self.states.append(self.expect_flow_mapping_value) - self.expect_node(mapping=True) - - def expect_flow_mapping_simple_value(self): - self.write_indicator(u':', False) - self.states.append(self.expect_flow_mapping_key) - self.expect_node(mapping=True) - - def expect_flow_mapping_value(self): - if self.canonical or self.column > self.best_width: - self.write_indent() - self.write_indicator(u':', True) - self.states.append(self.expect_flow_mapping_key) - self.expect_node(mapping=True) - - # Block sequence handlers. - - def expect_block_sequence(self): - indentless = (self.mapping_context and not self.indention) - self.increase_indent(flow=False, indentless=indentless) - self.state = self.expect_first_block_sequence_item - - def expect_first_block_sequence_item(self): - return self.expect_block_sequence_item(first=True) - - def expect_block_sequence_item(self, first=False): - if not first and isinstance(self.event, SequenceEndEvent): - self.indent = self.indents.pop() - self.state = self.states.pop() - else: - self.write_indent() - self.write_indicator(u'-', True, indention=True) - self.states.append(self.expect_block_sequence_item) - self.expect_node(sequence=True) - - # Block mapping handlers. - - def expect_block_mapping(self): - self.increase_indent(flow=False) - self.state = self.expect_first_block_mapping_key - - def expect_first_block_mapping_key(self): - return self.expect_block_mapping_key(first=True) - - def expect_block_mapping_key(self, first=False): - if not first and isinstance(self.event, MappingEndEvent): - self.indent = self.indents.pop() - self.state = self.states.pop() - else: - self.write_indent() - if self.check_simple_key(): - self.states.append(self.expect_block_mapping_simple_value) - self.expect_node(mapping=True, simple_key=True) - else: - self.write_indicator(u'?', True, indention=True) - self.states.append(self.expect_block_mapping_value) - self.expect_node(mapping=True) - - def expect_block_mapping_simple_value(self): - self.write_indicator(u':', False) - self.states.append(self.expect_block_mapping_key) - self.expect_node(mapping=True) - - def expect_block_mapping_value(self): - self.write_indent() - self.write_indicator(u':', True, indention=True) - self.states.append(self.expect_block_mapping_key) - self.expect_node(mapping=True) - - # Checkers. - - def check_empty_sequence(self): - return (isinstance(self.event, SequenceStartEvent) and self.events - and isinstance(self.events[0], SequenceEndEvent)) - - def check_empty_mapping(self): - return (isinstance(self.event, MappingStartEvent) and self.events - and isinstance(self.events[0], MappingEndEvent)) - - def check_empty_document(self): - if not isinstance(self.event, DocumentStartEvent) or not self.events: - return False - event = self.events[0] - return (isinstance(event, ScalarEvent) and event.anchor is None - and event.tag is None and event.implicit and event.value == u'') - - def check_simple_key(self): - length = 0 - if isinstance(self.event, NodeEvent) and self.event.anchor is not None: - if self.prepared_anchor is None: - self.prepared_anchor = self.prepare_anchor(self.event.anchor) - length += len(self.prepared_anchor) - if isinstance(self.event, (ScalarEvent, CollectionStartEvent)) \ - and self.event.tag is not None: - if self.prepared_tag is None: - self.prepared_tag = self.prepare_tag(self.event.tag) - length += len(self.prepared_tag) - if isinstance(self.event, ScalarEvent): - if self.analysis is None: - self.analysis = self.analyze_scalar(self.event.value) - length += len(self.analysis.scalar) - return (length < 128 and (isinstance(self.event, AliasEvent) - or (isinstance(self.event, ScalarEvent) - and not self.analysis.empty and not self.analysis.multiline) - or self.check_empty_sequence() or self.check_empty_mapping())) - - # Anchor, Tag, and Scalar processors. - - def process_anchor(self, indicator): - if self.event.anchor is None: - self.prepared_anchor = None - return - if self.prepared_anchor is None: - self.prepared_anchor = self.prepare_anchor(self.event.anchor) - if self.prepared_anchor: - self.write_indicator(indicator+self.prepared_anchor, True) - self.prepared_anchor = None - - def process_tag(self): - tag = self.event.tag - if isinstance(self.event, ScalarEvent): - if self.style is None: - self.style = self.choose_scalar_style() - if ((not self.canonical or tag is None) and - ((self.style == '' and self.event.implicit[0]) - or (self.style != '' and self.event.implicit[1]))): - self.prepared_tag = None - return - if self.event.implicit[0] and tag is None: - tag = u'!' - self.prepared_tag = None - else: - if (not self.canonical or tag is None) and self.event.implicit: - self.prepared_tag = None - return - if tag is None: - raise EmitterError("tag is not specified") - if self.prepared_tag is None: - self.prepared_tag = self.prepare_tag(tag) - if self.prepared_tag: - self.write_indicator(self.prepared_tag, True) - self.prepared_tag = None - - def choose_scalar_style(self): - if self.analysis is None: - self.analysis = self.analyze_scalar(self.event.value) - if self.event.style == '"' or self.canonical: - return '"' - if not self.event.style and self.event.implicit[0]: - if (not (self.simple_key_context and - (self.analysis.empty or self.analysis.multiline)) - and (self.flow_level and self.analysis.allow_flow_plain - or (not self.flow_level and self.analysis.allow_block_plain))): - return '' - if self.event.style and self.event.style in '|>': - if (not self.flow_level and not self.simple_key_context - and self.analysis.allow_block): - return self.event.style - if not self.event.style or self.event.style == '\'': - if (self.analysis.allow_single_quoted and - not (self.simple_key_context and self.analysis.multiline)): - return '\'' - return '"' - - def process_scalar(self): - if self.analysis is None: - self.analysis = self.analyze_scalar(self.event.value) - if self.style is None: - self.style = self.choose_scalar_style() - split = (not self.simple_key_context) - #if self.analysis.multiline and split \ - # and (not self.style or self.style in '\'\"'): - # self.write_indent() - if self.style == '"': - self.write_double_quoted(self.analysis.scalar, split) - elif self.style == '\'': - self.write_single_quoted(self.analysis.scalar, split) - elif self.style == '>': - self.write_folded(self.analysis.scalar) - elif self.style == '|': - self.write_literal(self.analysis.scalar) - else: - self.write_plain(self.analysis.scalar, split) - self.analysis = None - self.style = None - - # Analyzers. - - def prepare_version(self, version): - major, minor = version - if major != 1: - raise EmitterError("unsupported YAML version: %d.%d" % (major, minor)) - return u'%d.%d' % (major, minor) - - def prepare_tag_handle(self, handle): - if not handle: - raise EmitterError("tag handle must not be empty") - if handle[0] != u'!' or handle[-1] != u'!': - raise EmitterError("tag handle must start and end with '!': %r" - % (handle.encode('utf-8'))) - for ch in handle[1:-1]: - if not (u'0' <= ch <= u'9' or u'A' <= ch <= u'Z' or u'a' <= ch <= u'z' \ - or ch in u'-_'): - raise EmitterError("invalid character %r in the tag handle: %r" - % (ch.encode('utf-8'), handle.encode('utf-8'))) - return handle - - def prepare_tag_prefix(self, prefix): - if not prefix: - raise EmitterError("tag prefix must not be empty") - chunks = [] - start = end = 0 - if prefix[0] == u'!': - end = 1 - while end < len(prefix): - ch = prefix[end] - if u'0' <= ch <= u'9' or u'A' <= ch <= u'Z' or u'a' <= ch <= u'z' \ - or ch in u'-;/?!:@&=+$,_.~*\'()[]': - end += 1 - else: - if start < end: - chunks.append(prefix[start:end]) - start = end = end+1 - data = ch.encode('utf-8') - for ch in data: - chunks.append(u'%%%02X' % ord(ch)) - if start < end: - chunks.append(prefix[start:end]) - return u''.join(chunks) - - def prepare_tag(self, tag): - if not tag: - raise EmitterError("tag must not be empty") - if tag == u'!': - return tag - handle = None - suffix = tag - prefixes = self.tag_prefixes.keys() - prefixes.sort() - for prefix in prefixes: - if tag.startswith(prefix) \ - and (prefix == u'!' or len(prefix) < len(tag)): - handle = self.tag_prefixes[prefix] - suffix = tag[len(prefix):] - chunks = [] - start = end = 0 - while end < len(suffix): - ch = suffix[end] - if u'0' <= ch <= u'9' or u'A' <= ch <= u'Z' or u'a' <= ch <= u'z' \ - or ch in u'-;/?:@&=+$,_.~*\'()[]' \ - or (ch == u'!' and handle != u'!'): - end += 1 - else: - if start < end: - chunks.append(suffix[start:end]) - start = end = end+1 - data = ch.encode('utf-8') - for ch in data: - chunks.append(u'%%%02X' % ord(ch)) - if start < end: - chunks.append(suffix[start:end]) - suffix_text = u''.join(chunks) - if handle: - return u'%s%s' % (handle, suffix_text) - else: - return u'!<%s>' % suffix_text - - def prepare_anchor(self, anchor): - if not anchor: - raise EmitterError("anchor must not be empty") - for ch in anchor: - if not (u'0' <= ch <= u'9' or u'A' <= ch <= u'Z' or u'a' <= ch <= u'z' \ - or ch in u'-_'): - raise EmitterError("invalid character %r in the anchor: %r" - % (ch.encode('utf-8'), anchor.encode('utf-8'))) - return anchor - - def analyze_scalar(self, scalar): - - # Empty scalar is a special case. - if not scalar: - return ScalarAnalysis(scalar=scalar, empty=True, multiline=False, - allow_flow_plain=False, allow_block_plain=True, - allow_single_quoted=True, allow_double_quoted=True, - allow_block=False) - - # Indicators and special characters. - block_indicators = False - flow_indicators = False - line_breaks = False - special_characters = False - - # Important whitespace combinations. - leading_space = False - leading_break = False - trailing_space = False - trailing_break = False - break_space = False - space_break = False - - # Check document indicators. - if scalar.startswith(u'---') or scalar.startswith(u'...'): - block_indicators = True - flow_indicators = True - - # First character or preceded by a whitespace. - preceeded_by_whitespace = True - - # Last character or followed by a whitespace. - followed_by_whitespace = (len(scalar) == 1 or - scalar[1] in u'\0 \t\r\n\x85\u2028\u2029') - - # The previous character is a space. - previous_space = False - - # The previous character is a break. - previous_break = False - - index = 0 - while index < len(scalar): - ch = scalar[index] - - # Check for indicators. - if index == 0: - # Leading indicators are special characters. - if ch in u'#,[]{}&*!|>\'\"%@`': - flow_indicators = True - block_indicators = True - if ch in u'?:': - flow_indicators = True - if followed_by_whitespace: - block_indicators = True - if ch == u'-' and followed_by_whitespace: - flow_indicators = True - block_indicators = True - else: - # Some indicators cannot appear within a scalar as well. - if ch in u',?[]{}': - flow_indicators = True - if ch == u':': - flow_indicators = True - if followed_by_whitespace: - block_indicators = True - if ch == u'#' and preceeded_by_whitespace: - flow_indicators = True - block_indicators = True - - # Check for line breaks, special, and unicode characters. - if ch in u'\n\x85\u2028\u2029': - line_breaks = True - if not (ch == u'\n' or u'\x20' <= ch <= u'\x7E'): - if (ch == u'\x85' or u'\xA0' <= ch <= u'\uD7FF' - or u'\uE000' <= ch <= u'\uFFFD') and ch != u'\uFEFF': - unicode_characters = True - if not self.allow_unicode: - special_characters = True - else: - special_characters = True - - # Detect important whitespace combinations. - if ch == u' ': - if index == 0: - leading_space = True - if index == len(scalar)-1: - trailing_space = True - if previous_break: - break_space = True - previous_space = True - previous_break = False - elif ch in u'\n\x85\u2028\u2029': - if index == 0: - leading_break = True - if index == len(scalar)-1: - trailing_break = True - if previous_space: - space_break = True - previous_space = False - previous_break = True - else: - previous_space = False - previous_break = False - - # Prepare for the next character. - index += 1 - preceeded_by_whitespace = (ch in u'\0 \t\r\n\x85\u2028\u2029') - followed_by_whitespace = (index+1 >= len(scalar) or - scalar[index+1] in u'\0 \t\r\n\x85\u2028\u2029') - - # Let's decide what styles are allowed. - allow_flow_plain = True - allow_block_plain = True - allow_single_quoted = True - allow_double_quoted = True - allow_block = True - - # Leading and trailing whitespaces are bad for plain scalars. - if (leading_space or leading_break - or trailing_space or trailing_break): - allow_flow_plain = allow_block_plain = False - - # We do not permit trailing spaces for block scalars. - if trailing_space: - allow_block = False - - # Spaces at the beginning of a new line are only acceptable for block - # scalars. - if break_space: - allow_flow_plain = allow_block_plain = allow_single_quoted = False - - # Spaces followed by breaks, as well as special character are only - # allowed for double quoted scalars. - if space_break or special_characters: - allow_flow_plain = allow_block_plain = \ - allow_single_quoted = allow_block = False - - # Although the plain scalar writer supports breaks, we never emit - # multiline plain scalars. - if line_breaks: - allow_flow_plain = allow_block_plain = False - - # Flow indicators are forbidden for flow plain scalars. - if flow_indicators: - allow_flow_plain = False - - # Block indicators are forbidden for block plain scalars. - if block_indicators: - allow_block_plain = False - - return ScalarAnalysis(scalar=scalar, - empty=False, multiline=line_breaks, - allow_flow_plain=allow_flow_plain, - allow_block_plain=allow_block_plain, - allow_single_quoted=allow_single_quoted, - allow_double_quoted=allow_double_quoted, - allow_block=allow_block) - - # Writers. - - def flush_stream(self): - if hasattr(self.stream, 'flush'): - self.stream.flush() - - def write_stream_start(self): - # Write BOM if needed. - if self.encoding and self.encoding.startswith('utf-16'): - self.stream.write(u'\uFEFF'.encode(self.encoding)) - - def write_stream_end(self): - self.flush_stream() - - def write_indicator(self, indicator, need_whitespace, - whitespace=False, indention=False): - if self.whitespace or not need_whitespace: - data = indicator - else: - data = u' '+indicator - self.whitespace = whitespace - self.indention = self.indention and indention - self.column += len(data) - self.open_ended = False - if self.encoding: - data = data.encode(self.encoding) - self.stream.write(data) - - def write_indent(self): - indent = self.indent or 0 - if not self.indention or self.column > indent \ - or (self.column == indent and not self.whitespace): - self.write_line_break() - if self.column < indent: - self.whitespace = True - data = u' '*(indent-self.column) - self.column = indent - if self.encoding: - data = data.encode(self.encoding) - self.stream.write(data) - - def write_line_break(self, data=None): - if data is None: - data = self.best_line_break - self.whitespace = True - self.indention = True - self.line += 1 - self.column = 0 - if self.encoding: - data = data.encode(self.encoding) - self.stream.write(data) - - def write_version_directive(self, version_text): - data = u'%%YAML %s' % version_text - if self.encoding: - data = data.encode(self.encoding) - self.stream.write(data) - self.write_line_break() - - def write_tag_directive(self, handle_text, prefix_text): - data = u'%%TAG %s %s' % (handle_text, prefix_text) - if self.encoding: - data = data.encode(self.encoding) - self.stream.write(data) - self.write_line_break() - - # Scalar streams. - - def write_single_quoted(self, text, split=True): - self.write_indicator(u'\'', True) - spaces = False - breaks = False - start = end = 0 - while end <= len(text): - ch = None - if end < len(text): - ch = text[end] - if spaces: - if ch is None or ch != u' ': - if start+1 == end and self.column > self.best_width and split \ - and start != 0 and end != len(text): - self.write_indent() - else: - data = text[start:end] - self.column += len(data) - if self.encoding: - data = data.encode(self.encoding) - self.stream.write(data) - start = end - elif breaks: - if ch is None or ch not in u'\n\x85\u2028\u2029': - if text[start] == u'\n': - self.write_line_break() - for br in text[start:end]: - if br == u'\n': - self.write_line_break() - else: - self.write_line_break(br) - self.write_indent() - start = end - else: - if ch is None or ch in u' \n\x85\u2028\u2029' or ch == u'\'': - if start < end: - data = text[start:end] - self.column += len(data) - if self.encoding: - data = data.encode(self.encoding) - self.stream.write(data) - start = end - if ch == u'\'': - data = u'\'\'' - self.column += 2 - if self.encoding: - data = data.encode(self.encoding) - self.stream.write(data) - start = end + 1 - if ch is not None: - spaces = (ch == u' ') - breaks = (ch in u'\n\x85\u2028\u2029') - end += 1 - self.write_indicator(u'\'', False) - - ESCAPE_REPLACEMENTS = { - u'\0': u'0', - u'\x07': u'a', - u'\x08': u'b', - u'\x09': u't', - u'\x0A': u'n', - u'\x0B': u'v', - u'\x0C': u'f', - u'\x0D': u'r', - u'\x1B': u'e', - u'\"': u'\"', - u'\\': u'\\', - u'\x85': u'N', - u'\xA0': u'_', - u'\u2028': u'L', - u'\u2029': u'P', - } - - def write_double_quoted(self, text, split=True): - self.write_indicator(u'"', True) - start = end = 0 - while end <= len(text): - ch = None - if end < len(text): - ch = text[end] - if ch is None or ch in u'"\\\x85\u2028\u2029\uFEFF' \ - or not (u'\x20' <= ch <= u'\x7E' - or (self.allow_unicode - and (u'\xA0' <= ch <= u'\uD7FF' - or u'\uE000' <= ch <= u'\uFFFD'))): - if start < end: - data = text[start:end] - self.column += len(data) - if self.encoding: - data = data.encode(self.encoding) - self.stream.write(data) - start = end - if ch is not None: - if ch in self.ESCAPE_REPLACEMENTS: - data = u'\\'+self.ESCAPE_REPLACEMENTS[ch] - elif ch <= u'\xFF': - data = u'\\x%02X' % ord(ch) - elif ch <= u'\uFFFF': - data = u'\\u%04X' % ord(ch) - else: - data = u'\\U%08X' % ord(ch) - self.column += len(data) - if self.encoding: - data = data.encode(self.encoding) - self.stream.write(data) - start = end+1 - if 0 < end < len(text)-1 and (ch == u' ' or start >= end) \ - and self.column+(end-start) > self.best_width and split: - data = text[start:end]+u'\\' - if start < end: - start = end - self.column += len(data) - if self.encoding: - data = data.encode(self.encoding) - self.stream.write(data) - self.write_indent() - self.whitespace = False - self.indention = False - if text[start] == u' ': - data = u'\\' - self.column += len(data) - if self.encoding: - data = data.encode(self.encoding) - self.stream.write(data) - end += 1 - self.write_indicator(u'"', False) - - def determine_block_hints(self, text): - hints = u'' - if text: - if text[0] in u' \n\x85\u2028\u2029': - hints += unicode(self.best_indent) - if text[-1] not in u'\n\x85\u2028\u2029': - hints += u'-' - elif len(text) == 1 or text[-2] in u'\n\x85\u2028\u2029': - hints += u'+' - return hints - - def write_folded(self, text): - hints = self.determine_block_hints(text) - self.write_indicator(u'>'+hints, True) - if hints[-1:] == u'+': - self.open_ended = True - self.write_line_break() - leading_space = True - spaces = False - breaks = True - start = end = 0 - while end <= len(text): - ch = None - if end < len(text): - ch = text[end] - if breaks: - if ch is None or ch not in u'\n\x85\u2028\u2029': - if not leading_space and ch is not None and ch != u' ' \ - and text[start] == u'\n': - self.write_line_break() - leading_space = (ch == u' ') - for br in text[start:end]: - if br == u'\n': - self.write_line_break() - else: - self.write_line_break(br) - if ch is not None: - self.write_indent() - start = end - elif spaces: - if ch != u' ': - if start+1 == end and self.column > self.best_width: - self.write_indent() - else: - data = text[start:end] - self.column += len(data) - if self.encoding: - data = data.encode(self.encoding) - self.stream.write(data) - start = end - else: - if ch is None or ch in u' \n\x85\u2028\u2029': - data = text[start:end] - self.column += len(data) - if self.encoding: - data = data.encode(self.encoding) - self.stream.write(data) - if ch is None: - self.write_line_break() - start = end - if ch is not None: - breaks = (ch in u'\n\x85\u2028\u2029') - spaces = (ch == u' ') - end += 1 - - def write_literal(self, text): - hints = self.determine_block_hints(text) - self.write_indicator(u'|'+hints, True) - if hints[-1:] == u'+': - self.open_ended = True - self.write_line_break() - breaks = True - start = end = 0 - while end <= len(text): - ch = None - if end < len(text): - ch = text[end] - if breaks: - if ch is None or ch not in u'\n\x85\u2028\u2029': - for br in text[start:end]: - if br == u'\n': - self.write_line_break() - else: - self.write_line_break(br) - if ch is not None: - self.write_indent() - start = end - else: - if ch is None or ch in u'\n\x85\u2028\u2029': - data = text[start:end] - if self.encoding: - data = data.encode(self.encoding) - self.stream.write(data) - if ch is None: - self.write_line_break() - start = end - if ch is not None: - breaks = (ch in u'\n\x85\u2028\u2029') - end += 1 - - def write_plain(self, text, split=True): - if self.root_context: - self.open_ended = True - if not text: - return - if not self.whitespace: - data = u' ' - self.column += len(data) - if self.encoding: - data = data.encode(self.encoding) - self.stream.write(data) - self.whitespace = False - self.indention = False - spaces = False - breaks = False - start = end = 0 - while end <= len(text): - ch = None - if end < len(text): - ch = text[end] - if spaces: - if ch != u' ': - if start+1 == end and self.column > self.best_width and split: - self.write_indent() - self.whitespace = False - self.indention = False - else: - data = text[start:end] - self.column += len(data) - if self.encoding: - data = data.encode(self.encoding) - self.stream.write(data) - start = end - elif breaks: - if ch not in u'\n\x85\u2028\u2029': - if text[start] == u'\n': - self.write_line_break() - for br in text[start:end]: - if br == u'\n': - self.write_line_break() - else: - self.write_line_break(br) - self.write_indent() - self.whitespace = False - self.indention = False - start = end - else: - if ch is None or ch in u' \n\x85\u2028\u2029': - data = text[start:end] - self.column += len(data) - if self.encoding: - data = data.encode(self.encoding) - self.stream.write(data) - start = end - if ch is not None: - spaces = (ch == u' ') - breaks = (ch in u'\n\x85\u2028\u2029') - end += 1 - diff --git a/python.d/python_modules/pyyaml2/error.py b/python.d/python_modules/pyyaml2/error.py deleted file mode 100644 index 577686db5..000000000 --- a/python.d/python_modules/pyyaml2/error.py +++ /dev/null @@ -1,75 +0,0 @@ - -__all__ = ['Mark', 'YAMLError', 'MarkedYAMLError'] - -class Mark(object): - - def __init__(self, name, index, line, column, buffer, pointer): - self.name = name - self.index = index - self.line = line - self.column = column - self.buffer = buffer - self.pointer = pointer - - def get_snippet(self, indent=4, max_length=75): - if self.buffer is None: - return None - head = '' - start = self.pointer - while start > 0 and self.buffer[start-1] not in u'\0\r\n\x85\u2028\u2029': - start -= 1 - if self.pointer-start > max_length/2-1: - head = ' ... ' - start += 5 - break - tail = '' - end = self.pointer - while end < len(self.buffer) and self.buffer[end] not in u'\0\r\n\x85\u2028\u2029': - end += 1 - if end-self.pointer > max_length/2-1: - tail = ' ... ' - end -= 5 - break - snippet = self.buffer[start:end].encode('utf-8') - return ' '*indent + head + snippet + tail + '\n' \ - + ' '*(indent+self.pointer-start+len(head)) + '^' - - def __str__(self): - snippet = self.get_snippet() - where = " in \"%s\", line %d, column %d" \ - % (self.name, self.line+1, self.column+1) - if snippet is not None: - where += ":\n"+snippet - return where - -class YAMLError(Exception): - pass - -class MarkedYAMLError(YAMLError): - - def __init__(self, context=None, context_mark=None, - problem=None, problem_mark=None, note=None): - self.context = context - self.context_mark = context_mark - self.problem = problem - self.problem_mark = problem_mark - self.note = note - - def __str__(self): - lines = [] - if self.context is not None: - lines.append(self.context) - if self.context_mark is not None \ - and (self.problem is None or self.problem_mark is None - or self.context_mark.name != self.problem_mark.name - or self.context_mark.line != self.problem_mark.line - or self.context_mark.column != self.problem_mark.column): - lines.append(str(self.context_mark)) - if self.problem is not None: - lines.append(self.problem) - if self.problem_mark is not None: - lines.append(str(self.problem_mark)) - if self.note is not None: - lines.append(self.note) - return '\n'.join(lines) - diff --git a/python.d/python_modules/pyyaml2/events.py b/python.d/python_modules/pyyaml2/events.py deleted file mode 100644 index f79ad389c..000000000 --- a/python.d/python_modules/pyyaml2/events.py +++ /dev/null @@ -1,86 +0,0 @@ - -# Abstract classes. - -class Event(object): - def __init__(self, start_mark=None, end_mark=None): - self.start_mark = start_mark - self.end_mark = end_mark - def __repr__(self): - attributes = [key for key in ['anchor', 'tag', 'implicit', 'value'] - if hasattr(self, key)] - arguments = ', '.join(['%s=%r' % (key, getattr(self, key)) - for key in attributes]) - return '%s(%s)' % (self.__class__.__name__, arguments) - -class NodeEvent(Event): - def __init__(self, anchor, start_mark=None, end_mark=None): - self.anchor = anchor - self.start_mark = start_mark - self.end_mark = end_mark - -class CollectionStartEvent(NodeEvent): - def __init__(self, anchor, tag, implicit, start_mark=None, end_mark=None, - flow_style=None): - self.anchor = anchor - self.tag = tag - self.implicit = implicit - self.start_mark = start_mark - self.end_mark = end_mark - self.flow_style = flow_style - -class CollectionEndEvent(Event): - pass - -# Implementations. - -class StreamStartEvent(Event): - def __init__(self, start_mark=None, end_mark=None, encoding=None): - self.start_mark = start_mark - self.end_mark = end_mark - self.encoding = encoding - -class StreamEndEvent(Event): - pass - -class DocumentStartEvent(Event): - def __init__(self, start_mark=None, end_mark=None, - explicit=None, version=None, tags=None): - self.start_mark = start_mark - self.end_mark = end_mark - self.explicit = explicit - self.version = version - self.tags = tags - -class DocumentEndEvent(Event): - def __init__(self, start_mark=None, end_mark=None, - explicit=None): - self.start_mark = start_mark - self.end_mark = end_mark - self.explicit = explicit - -class AliasEvent(NodeEvent): - pass - -class ScalarEvent(NodeEvent): - def __init__(self, anchor, tag, implicit, value, - start_mark=None, end_mark=None, style=None): - self.anchor = anchor - self.tag = tag - self.implicit = implicit - self.value = value - self.start_mark = start_mark - self.end_mark = end_mark - self.style = style - -class SequenceStartEvent(CollectionStartEvent): - pass - -class SequenceEndEvent(CollectionEndEvent): - pass - -class MappingStartEvent(CollectionStartEvent): - pass - -class MappingEndEvent(CollectionEndEvent): - pass - diff --git a/python.d/python_modules/pyyaml2/loader.py b/python.d/python_modules/pyyaml2/loader.py deleted file mode 100644 index 293ff467b..000000000 --- a/python.d/python_modules/pyyaml2/loader.py +++ /dev/null @@ -1,40 +0,0 @@ - -__all__ = ['BaseLoader', 'SafeLoader', 'Loader'] - -from reader import * -from scanner import * -from parser import * -from composer import * -from constructor import * -from resolver import * - -class BaseLoader(Reader, Scanner, Parser, Composer, BaseConstructor, BaseResolver): - - def __init__(self, stream): - Reader.__init__(self, stream) - Scanner.__init__(self) - Parser.__init__(self) - Composer.__init__(self) - BaseConstructor.__init__(self) - BaseResolver.__init__(self) - -class SafeLoader(Reader, Scanner, Parser, Composer, SafeConstructor, Resolver): - - def __init__(self, stream): - Reader.__init__(self, stream) - Scanner.__init__(self) - Parser.__init__(self) - Composer.__init__(self) - SafeConstructor.__init__(self) - Resolver.__init__(self) - -class Loader(Reader, Scanner, Parser, Composer, Constructor, Resolver): - - def __init__(self, stream): - Reader.__init__(self, stream) - Scanner.__init__(self) - Parser.__init__(self) - Composer.__init__(self) - Constructor.__init__(self) - Resolver.__init__(self) - diff --git a/python.d/python_modules/pyyaml2/nodes.py b/python.d/python_modules/pyyaml2/nodes.py deleted file mode 100644 index c4f070c41..000000000 --- a/python.d/python_modules/pyyaml2/nodes.py +++ /dev/null @@ -1,49 +0,0 @@ - -class Node(object): - def __init__(self, tag, value, start_mark, end_mark): - self.tag = tag - self.value = value - self.start_mark = start_mark - self.end_mark = end_mark - def __repr__(self): - value = self.value - #if isinstance(value, list): - # if len(value) == 0: - # value = '<empty>' - # elif len(value) == 1: - # value = '<1 item>' - # else: - # value = '<%d items>' % len(value) - #else: - # if len(value) > 75: - # value = repr(value[:70]+u' ... ') - # else: - # value = repr(value) - value = repr(value) - return '%s(tag=%r, value=%s)' % (self.__class__.__name__, self.tag, value) - -class ScalarNode(Node): - id = 'scalar' - def __init__(self, tag, value, - start_mark=None, end_mark=None, style=None): - self.tag = tag - self.value = value - self.start_mark = start_mark - self.end_mark = end_mark - self.style = style - -class CollectionNode(Node): - def __init__(self, tag, value, - start_mark=None, end_mark=None, flow_style=None): - self.tag = tag - self.value = value - self.start_mark = start_mark - self.end_mark = end_mark - self.flow_style = flow_style - -class SequenceNode(CollectionNode): - id = 'sequence' - -class MappingNode(CollectionNode): - id = 'mapping' - diff --git a/python.d/python_modules/pyyaml2/parser.py b/python.d/python_modules/pyyaml2/parser.py deleted file mode 100644 index f9e3057f3..000000000 --- a/python.d/python_modules/pyyaml2/parser.py +++ /dev/null @@ -1,589 +0,0 @@ - -# The following YAML grammar is LL(1) and is parsed by a recursive descent -# parser. -# -# stream ::= STREAM-START implicit_document? explicit_document* STREAM-END -# implicit_document ::= block_node DOCUMENT-END* -# explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END* -# block_node_or_indentless_sequence ::= -# ALIAS -# | properties (block_content | indentless_block_sequence)? -# | block_content -# | indentless_block_sequence -# block_node ::= ALIAS -# | properties block_content? -# | block_content -# flow_node ::= ALIAS -# | properties flow_content? -# | flow_content -# properties ::= TAG ANCHOR? | ANCHOR TAG? -# block_content ::= block_collection | flow_collection | SCALAR -# flow_content ::= flow_collection | SCALAR -# block_collection ::= block_sequence | block_mapping -# flow_collection ::= flow_sequence | flow_mapping -# block_sequence ::= BLOCK-SEQUENCE-START (BLOCK-ENTRY block_node?)* BLOCK-END -# indentless_sequence ::= (BLOCK-ENTRY block_node?)+ -# block_mapping ::= BLOCK-MAPPING_START -# ((KEY block_node_or_indentless_sequence?)? -# (VALUE block_node_or_indentless_sequence?)?)* -# BLOCK-END -# flow_sequence ::= FLOW-SEQUENCE-START -# (flow_sequence_entry FLOW-ENTRY)* -# flow_sequence_entry? -# FLOW-SEQUENCE-END -# flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? -# flow_mapping ::= FLOW-MAPPING-START -# (flow_mapping_entry FLOW-ENTRY)* -# flow_mapping_entry? -# FLOW-MAPPING-END -# flow_mapping_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? -# -# FIRST sets: -# -# stream: { STREAM-START } -# explicit_document: { DIRECTIVE DOCUMENT-START } -# implicit_document: FIRST(block_node) -# block_node: { ALIAS TAG ANCHOR SCALAR BLOCK-SEQUENCE-START BLOCK-MAPPING-START FLOW-SEQUENCE-START FLOW-MAPPING-START } -# flow_node: { ALIAS ANCHOR TAG SCALAR FLOW-SEQUENCE-START FLOW-MAPPING-START } -# block_content: { BLOCK-SEQUENCE-START BLOCK-MAPPING-START FLOW-SEQUENCE-START FLOW-MAPPING-START SCALAR } -# flow_content: { FLOW-SEQUENCE-START FLOW-MAPPING-START SCALAR } -# block_collection: { BLOCK-SEQUENCE-START BLOCK-MAPPING-START } -# flow_collection: { FLOW-SEQUENCE-START FLOW-MAPPING-START } -# block_sequence: { BLOCK-SEQUENCE-START } -# block_mapping: { BLOCK-MAPPING-START } -# block_node_or_indentless_sequence: { ALIAS ANCHOR TAG SCALAR BLOCK-SEQUENCE-START BLOCK-MAPPING-START FLOW-SEQUENCE-START FLOW-MAPPING-START BLOCK-ENTRY } -# indentless_sequence: { ENTRY } -# flow_collection: { FLOW-SEQUENCE-START FLOW-MAPPING-START } -# flow_sequence: { FLOW-SEQUENCE-START } -# flow_mapping: { FLOW-MAPPING-START } -# flow_sequence_entry: { ALIAS ANCHOR TAG SCALAR FLOW-SEQUENCE-START FLOW-MAPPING-START KEY } -# flow_mapping_entry: { ALIAS ANCHOR TAG SCALAR FLOW-SEQUENCE-START FLOW-MAPPING-START KEY } - -__all__ = ['Parser', 'ParserError'] - -from error import MarkedYAMLError -from tokens import * -from events import * -from scanner import * - -class ParserError(MarkedYAMLError): - pass - -class Parser(object): - # Since writing a recursive-descendant parser is a straightforward task, we - # do not give many comments here. - - DEFAULT_TAGS = { - u'!': u'!', - u'!!': u'tag:yaml.org,2002:', - } - - def __init__(self): - self.current_event = None - self.yaml_version = None - self.tag_handles = {} - self.states = [] - self.marks = [] - self.state = self.parse_stream_start - - def dispose(self): - # Reset the state attributes (to clear self-references) - self.states = [] - self.state = None - - def check_event(self, *choices): - # Check the type of the next event. - if self.current_event is None: - if self.state: - self.current_event = self.state() - if self.current_event is not None: - if not choices: - return True - for choice in choices: - if isinstance(self.current_event, choice): - return True - return False - - def peek_event(self): - # Get the next event. - if self.current_event is None: - if self.state: - self.current_event = self.state() - return self.current_event - - def get_event(self): - # Get the next event and proceed further. - if self.current_event is None: - if self.state: - self.current_event = self.state() - value = self.current_event - self.current_event = None - return value - - # stream ::= STREAM-START implicit_document? explicit_document* STREAM-END - # implicit_document ::= block_node DOCUMENT-END* - # explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END* - - def parse_stream_start(self): - - # Parse the stream start. - token = self.get_token() - event = StreamStartEvent(token.start_mark, token.end_mark, - encoding=token.encoding) - - # Prepare the next state. - self.state = self.parse_implicit_document_start - - return event - - def parse_implicit_document_start(self): - - # Parse an implicit document. - if not self.check_token(DirectiveToken, DocumentStartToken, - StreamEndToken): - self.tag_handles = self.DEFAULT_TAGS - token = self.peek_token() - start_mark = end_mark = token.start_mark - event = DocumentStartEvent(start_mark, end_mark, - explicit=False) - - # Prepare the next state. - self.states.append(self.parse_document_end) - self.state = self.parse_block_node - - return event - - else: - return self.parse_document_start() - - def parse_document_start(self): - - # Parse any extra document end indicators. - while self.check_token(DocumentEndToken): - self.get_token() - - # Parse an explicit document. - if not self.check_token(StreamEndToken): - token = self.peek_token() - start_mark = token.start_mark - version, tags = self.process_directives() - if not self.check_token(DocumentStartToken): - raise ParserError(None, None, - "expected '<document start>', but found %r" - % self.peek_token().id, - self.peek_token().start_mark) - token = self.get_token() - end_mark = token.end_mark - event = DocumentStartEvent(start_mark, end_mark, - explicit=True, version=version, tags=tags) - self.states.append(self.parse_document_end) - self.state = self.parse_document_content - else: - # Parse the end of the stream. - token = self.get_token() - event = StreamEndEvent(token.start_mark, token.end_mark) - assert not self.states - assert not self.marks - self.state = None - return event - - def parse_document_end(self): - - # Parse the document end. - token = self.peek_token() - start_mark = end_mark = token.start_mark - explicit = False - if self.check_token(DocumentEndToken): - token = self.get_token() - end_mark = token.end_mark - explicit = True - event = DocumentEndEvent(start_mark, end_mark, - explicit=explicit) - - # Prepare the next state. - self.state = self.parse_document_start - - return event - - def parse_document_content(self): - if self.check_token(DirectiveToken, - DocumentStartToken, DocumentEndToken, StreamEndToken): - event = self.process_empty_scalar(self.peek_token().start_mark) - self.state = self.states.pop() - return event - else: - return self.parse_block_node() - - def process_directives(self): - self.yaml_version = None - self.tag_handles = {} - while self.check_token(DirectiveToken): - token = self.get_token() - if token.name == u'YAML': - if self.yaml_version is not None: - raise ParserError(None, None, - "found duplicate YAML directive", token.start_mark) - major, minor = token.value - if major != 1: - raise ParserError(None, None, - "found incompatible YAML document (version 1.* is required)", - token.start_mark) - self.yaml_version = token.value - elif token.name == u'TAG': - handle, prefix = token.value - if handle in self.tag_handles: - raise ParserError(None, None, - "duplicate tag handle %r" % handle.encode('utf-8'), - token.start_mark) - self.tag_handles[handle] = prefix - if self.tag_handles: - value = self.yaml_version, self.tag_handles.copy() - else: - value = self.yaml_version, None - for key in self.DEFAULT_TAGS: - if key not in self.tag_handles: - self.tag_handles[key] = self.DEFAULT_TAGS[key] - return value - - # block_node_or_indentless_sequence ::= ALIAS - # | properties (block_content | indentless_block_sequence)? - # | block_content - # | indentless_block_sequence - # block_node ::= ALIAS - # | properties block_content? - # | block_content - # flow_node ::= ALIAS - # | properties flow_content? - # | flow_content - # properties ::= TAG ANCHOR? | ANCHOR TAG? - # block_content ::= block_collection | flow_collection | SCALAR - # flow_content ::= flow_collection | SCALAR - # block_collection ::= block_sequence | block_mapping - # flow_collection ::= flow_sequence | flow_mapping - - def parse_block_node(self): - return self.parse_node(block=True) - - def parse_flow_node(self): - return self.parse_node() - - def parse_block_node_or_indentless_sequence(self): - return self.parse_node(block=True, indentless_sequence=True) - - def parse_node(self, block=False, indentless_sequence=False): - if self.check_token(AliasToken): - token = self.get_token() - event = AliasEvent(token.value, token.start_mark, token.end_mark) - self.state = self.states.pop() - else: - anchor = None - tag = None - start_mark = end_mark = tag_mark = None - if self.check_token(AnchorToken): - token = self.get_token() - start_mark = token.start_mark - end_mark = token.end_mark - anchor = token.value - if self.check_token(TagToken): - token = self.get_token() - tag_mark = token.start_mark - end_mark = token.end_mark - tag = token.value - elif self.check_token(TagToken): - token = self.get_token() - start_mark = tag_mark = token.start_mark - end_mark = token.end_mark - tag = token.value - if self.check_token(AnchorToken): - token = self.get_token() - end_mark = token.end_mark - anchor = token.value - if tag is not None: - handle, suffix = tag - if handle is not None: - if handle not in self.tag_handles: - raise ParserError("while parsing a node", start_mark, - "found undefined tag handle %r" % handle.encode('utf-8'), - tag_mark) - tag = self.tag_handles[handle]+suffix - else: - tag = suffix - #if tag == u'!': - # raise ParserError("while parsing a node", start_mark, - # "found non-specific tag '!'", tag_mark, - # "Please check 'http://pyyaml.org/wiki/YAMLNonSpecificTag' and share your opinion.") - if start_mark is None: - start_mark = end_mark = self.peek_token().start_mark - event = None - implicit = (tag is None or tag == u'!') - if indentless_sequence and self.check_token(BlockEntryToken): - end_mark = self.peek_token().end_mark - event = SequenceStartEvent(anchor, tag, implicit, - start_mark, end_mark) - self.state = self.parse_indentless_sequence_entry - else: - if self.check_token(ScalarToken): - token = self.get_token() - end_mark = token.end_mark - if (token.plain and tag is None) or tag == u'!': - implicit = (True, False) - elif tag is None: - implicit = (False, True) - else: - implicit = (False, False) - event = ScalarEvent(anchor, tag, implicit, token.value, - start_mark, end_mark, style=token.style) - self.state = self.states.pop() - elif self.check_token(FlowSequenceStartToken): - end_mark = self.peek_token().end_mark - event = SequenceStartEvent(anchor, tag, implicit, - start_mark, end_mark, flow_style=True) - self.state = self.parse_flow_sequence_first_entry - elif self.check_token(FlowMappingStartToken): - end_mark = self.peek_token().end_mark - event = MappingStartEvent(anchor, tag, implicit, - start_mark, end_mark, flow_style=True) - self.state = self.parse_flow_mapping_first_key - elif block and self.check_token(BlockSequenceStartToken): - end_mark = self.peek_token().start_mark - event = SequenceStartEvent(anchor, tag, implicit, - start_mark, end_mark, flow_style=False) - self.state = self.parse_block_sequence_first_entry - elif block and self.check_token(BlockMappingStartToken): - end_mark = self.peek_token().start_mark - event = MappingStartEvent(anchor, tag, implicit, - start_mark, end_mark, flow_style=False) - self.state = self.parse_block_mapping_first_key - elif anchor is not None or tag is not None: - # Empty scalars are allowed even if a tag or an anchor is - # specified. - event = ScalarEvent(anchor, tag, (implicit, False), u'', - start_mark, end_mark) - self.state = self.states.pop() - else: - if block: - node = 'block' - else: - node = 'flow' - token = self.peek_token() - raise ParserError("while parsing a %s node" % node, start_mark, - "expected the node content, but found %r" % token.id, - token.start_mark) - return event - - # block_sequence ::= BLOCK-SEQUENCE-START (BLOCK-ENTRY block_node?)* BLOCK-END - - def parse_block_sequence_first_entry(self): - token = self.get_token() - self.marks.append(token.start_mark) - return self.parse_block_sequence_entry() - - def parse_block_sequence_entry(self): - if self.check_token(BlockEntryToken): - token = self.get_token() - if not self.check_token(BlockEntryToken, BlockEndToken): - self.states.append(self.parse_block_sequence_entry) - return self.parse_block_node() - else: - self.state = self.parse_block_sequence_entry - return self.process_empty_scalar(token.end_mark) - if not self.check_token(BlockEndToken): - token = self.peek_token() - raise ParserError("while parsing a block collection", self.marks[-1], - "expected <block end>, but found %r" % token.id, token.start_mark) - token = self.get_token() - event = SequenceEndEvent(token.start_mark, token.end_mark) - self.state = self.states.pop() - self.marks.pop() - return event - - # indentless_sequence ::= (BLOCK-ENTRY block_node?)+ - - def parse_indentless_sequence_entry(self): - if self.check_token(BlockEntryToken): - token = self.get_token() - if not self.check_token(BlockEntryToken, - KeyToken, ValueToken, BlockEndToken): - self.states.append(self.parse_indentless_sequence_entry) - return self.parse_block_node() - else: - self.state = self.parse_indentless_sequence_entry - return self.process_empty_scalar(token.end_mark) - token = self.peek_token() - event = SequenceEndEvent(token.start_mark, token.start_mark) - self.state = self.states.pop() - return event - - # block_mapping ::= BLOCK-MAPPING_START - # ((KEY block_node_or_indentless_sequence?)? - # (VALUE block_node_or_indentless_sequence?)?)* - # BLOCK-END - - def parse_block_mapping_first_key(self): - token = self.get_token() - self.marks.append(token.start_mark) - return self.parse_block_mapping_key() - - def parse_block_mapping_key(self): - if self.check_token(KeyToken): - token = self.get_token() - if not self.check_token(KeyToken, ValueToken, BlockEndToken): - self.states.append(self.parse_block_mapping_value) - return self.parse_block_node_or_indentless_sequence() - else: - self.state = self.parse_block_mapping_value - return self.process_empty_scalar(token.end_mark) - if not self.check_token(BlockEndToken): - token = self.peek_token() - raise ParserError("while parsing a block mapping", self.marks[-1], - "expected <block end>, but found %r" % token.id, token.start_mark) - token = self.get_token() - event = MappingEndEvent(token.start_mark, token.end_mark) - self.state = self.states.pop() - self.marks.pop() - return event - - def parse_block_mapping_value(self): - if self.check_token(ValueToken): - token = self.get_token() - if not self.check_token(KeyToken, ValueToken, BlockEndToken): - self.states.append(self.parse_block_mapping_key) - return self.parse_block_node_or_indentless_sequence() - else: - self.state = self.parse_block_mapping_key - return self.process_empty_scalar(token.end_mark) - else: - self.state = self.parse_block_mapping_key - token = self.peek_token() - return self.process_empty_scalar(token.start_mark) - - # flow_sequence ::= FLOW-SEQUENCE-START - # (flow_sequence_entry FLOW-ENTRY)* - # flow_sequence_entry? - # FLOW-SEQUENCE-END - # flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? - # - # Note that while production rules for both flow_sequence_entry and - # flow_mapping_entry are equal, their interpretations are different. - # For `flow_sequence_entry`, the part `KEY flow_node? (VALUE flow_node?)?` - # generate an inline mapping (set syntax). - - def parse_flow_sequence_first_entry(self): - token = self.get_token() - self.marks.append(token.start_mark) - return self.parse_flow_sequence_entry(first=True) - - def parse_flow_sequence_entry(self, first=False): - if not self.check_token(FlowSequenceEndToken): - if not first: - if self.check_token(FlowEntryToken): - self.get_token() - else: - token = self.peek_token() - raise ParserError("while parsing a flow sequence", self.marks[-1], - "expected ',' or ']', but got %r" % token.id, token.start_mark) - - if self.check_token(KeyToken): - token = self.peek_token() - event = MappingStartEvent(None, None, True, - token.start_mark, token.end_mark, - flow_style=True) - self.state = self.parse_flow_sequence_entry_mapping_key - return event - elif not self.check_token(FlowSequenceEndToken): - self.states.append(self.parse_flow_sequence_entry) - return self.parse_flow_node() - token = self.get_token() - event = SequenceEndEvent(token.start_mark, token.end_mark) - self.state = self.states.pop() - self.marks.pop() - return event - - def parse_flow_sequence_entry_mapping_key(self): - token = self.get_token() - if not self.check_token(ValueToken, - FlowEntryToken, FlowSequenceEndToken): - self.states.append(self.parse_flow_sequence_entry_mapping_value) - return self.parse_flow_node() - else: - self.state = self.parse_flow_sequence_entry_mapping_value - return self.process_empty_scalar(token.end_mark) - - def parse_flow_sequence_entry_mapping_value(self): - if self.check_token(ValueToken): - token = self.get_token() - if not self.check_token(FlowEntryToken, FlowSequenceEndToken): - self.states.append(self.parse_flow_sequence_entry_mapping_end) - return self.parse_flow_node() - else: - self.state = self.parse_flow_sequence_entry_mapping_end - return self.process_empty_scalar(token.end_mark) - else: - self.state = self.parse_flow_sequence_entry_mapping_end - token = self.peek_token() - return self.process_empty_scalar(token.start_mark) - - def parse_flow_sequence_entry_mapping_end(self): - self.state = self.parse_flow_sequence_entry - token = self.peek_token() - return MappingEndEvent(token.start_mark, token.start_mark) - - # flow_mapping ::= FLOW-MAPPING-START - # (flow_mapping_entry FLOW-ENTRY)* - # flow_mapping_entry? - # FLOW-MAPPING-END - # flow_mapping_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? - - def parse_flow_mapping_first_key(self): - token = self.get_token() - self.marks.append(token.start_mark) - return self.parse_flow_mapping_key(first=True) - - def parse_flow_mapping_key(self, first=False): - if not self.check_token(FlowMappingEndToken): - if not first: - if self.check_token(FlowEntryToken): - self.get_token() - else: - token = self.peek_token() - raise ParserError("while parsing a flow mapping", self.marks[-1], - "expected ',' or '}', but got %r" % token.id, token.start_mark) - if self.check_token(KeyToken): - token = self.get_token() - if not self.check_token(ValueToken, - FlowEntryToken, FlowMappingEndToken): - self.states.append(self.parse_flow_mapping_value) - return self.parse_flow_node() - else: - self.state = self.parse_flow_mapping_value - return self.process_empty_scalar(token.end_mark) - elif not self.check_token(FlowMappingEndToken): - self.states.append(self.parse_flow_mapping_empty_value) - return self.parse_flow_node() - token = self.get_token() - event = MappingEndEvent(token.start_mark, token.end_mark) - self.state = self.states.pop() - self.marks.pop() - return event - - def parse_flow_mapping_value(self): - if self.check_token(ValueToken): - token = self.get_token() - if not self.check_token(FlowEntryToken, FlowMappingEndToken): - self.states.append(self.parse_flow_mapping_key) - return self.parse_flow_node() - else: - self.state = self.parse_flow_mapping_key - return self.process_empty_scalar(token.end_mark) - else: - self.state = self.parse_flow_mapping_key - token = self.peek_token() - return self.process_empty_scalar(token.start_mark) - - def parse_flow_mapping_empty_value(self): - self.state = self.parse_flow_mapping_key - return self.process_empty_scalar(self.peek_token().start_mark) - - def process_empty_scalar(self, mark): - return ScalarEvent(None, None, (True, False), u'', mark, mark) - diff --git a/python.d/python_modules/pyyaml2/reader.py b/python.d/python_modules/pyyaml2/reader.py deleted file mode 100644 index 3249e6b9f..000000000 --- a/python.d/python_modules/pyyaml2/reader.py +++ /dev/null @@ -1,190 +0,0 @@ -# This module contains abstractions for the input stream. You don't have to -# looks further, there are no pretty code. -# -# We define two classes here. -# -# Mark(source, line, column) -# It's just a record and its only use is producing nice error messages. -# Parser does not use it for any other purposes. -# -# Reader(source, data) -# Reader determines the encoding of `data` and converts it to unicode. -# Reader provides the following methods and attributes: -# reader.peek(length=1) - return the next `length` characters -# reader.forward(length=1) - move the current position to `length` characters. -# reader.index - the number of the current character. -# reader.line, stream.column - the line and the column of the current character. - -__all__ = ['Reader', 'ReaderError'] - -from error import YAMLError, Mark - -import codecs, re - -class ReaderError(YAMLError): - - def __init__(self, name, position, character, encoding, reason): - self.name = name - self.character = character - self.position = position - self.encoding = encoding - self.reason = reason - - def __str__(self): - if isinstance(self.character, str): - return "'%s' codec can't decode byte #x%02x: %s\n" \ - " in \"%s\", position %d" \ - % (self.encoding, ord(self.character), self.reason, - self.name, self.position) - else: - return "unacceptable character #x%04x: %s\n" \ - " in \"%s\", position %d" \ - % (self.character, self.reason, - self.name, self.position) - -class Reader(object): - # Reader: - # - determines the data encoding and converts it to unicode, - # - checks if characters are in allowed range, - # - adds '\0' to the end. - - # Reader accepts - # - a `str` object, - # - a `unicode` object, - # - a file-like object with its `read` method returning `str`, - # - a file-like object with its `read` method returning `unicode`. - - # Yeah, it's ugly and slow. - - def __init__(self, stream): - self.name = None - self.stream = None - self.stream_pointer = 0 - self.eof = True - self.buffer = u'' - self.pointer = 0 - self.raw_buffer = None - self.raw_decode = None - self.encoding = None - self.index = 0 - self.line = 0 - self.column = 0 - if isinstance(stream, unicode): - self.name = "<unicode string>" - self.check_printable(stream) - self.buffer = stream+u'\0' - elif isinstance(stream, str): - self.name = "<string>" - self.raw_buffer = stream - self.determine_encoding() - else: - self.stream = stream - self.name = getattr(stream, 'name', "<file>") - self.eof = False - self.raw_buffer = '' - self.determine_encoding() - - def peek(self, index=0): - try: - return self.buffer[self.pointer+index] - except IndexError: - self.update(index+1) - return self.buffer[self.pointer+index] - - def prefix(self, length=1): - if self.pointer+length >= len(self.buffer): - self.update(length) - return self.buffer[self.pointer:self.pointer+length] - - def forward(self, length=1): - if self.pointer+length+1 >= len(self.buffer): - self.update(length+1) - while length: - ch = self.buffer[self.pointer] - self.pointer += 1 - self.index += 1 - if ch in u'\n\x85\u2028\u2029' \ - or (ch == u'\r' and self.buffer[self.pointer] != u'\n'): - self.line += 1 - self.column = 0 - elif ch != u'\uFEFF': - self.column += 1 - length -= 1 - - def get_mark(self): - if self.stream is None: - return Mark(self.name, self.index, self.line, self.column, - self.buffer, self.pointer) - else: - return Mark(self.name, self.index, self.line, self.column, - None, None) - - def determine_encoding(self): - while not self.eof and len(self.raw_buffer) < 2: - self.update_raw() - if not isinstance(self.raw_buffer, unicode): - if self.raw_buffer.startswith(codecs.BOM_UTF16_LE): - self.raw_decode = codecs.utf_16_le_decode - self.encoding = 'utf-16-le' - elif self.raw_buffer.startswith(codecs.BOM_UTF16_BE): - self.raw_decode = codecs.utf_16_be_decode - self.encoding = 'utf-16-be' - else: - self.raw_decode = codecs.utf_8_decode - self.encoding = 'utf-8' - self.update(1) - - NON_PRINTABLE = re.compile(u'[^\x09\x0A\x0D\x20-\x7E\x85\xA0-\uD7FF\uE000-\uFFFD]') - def check_printable(self, data): - match = self.NON_PRINTABLE.search(data) - if match: - character = match.group() - position = self.index+(len(self.buffer)-self.pointer)+match.start() - raise ReaderError(self.name, position, ord(character), - 'unicode', "special characters are not allowed") - - def update(self, length): - if self.raw_buffer is None: - return - self.buffer = self.buffer[self.pointer:] - self.pointer = 0 - while len(self.buffer) < length: - if not self.eof: - self.update_raw() - if self.raw_decode is not None: - try: - data, converted = self.raw_decode(self.raw_buffer, - 'strict', self.eof) - except UnicodeDecodeError, exc: - character = exc.object[exc.start] - if self.stream is not None: - position = self.stream_pointer-len(self.raw_buffer)+exc.start - else: - position = exc.start - raise ReaderError(self.name, position, character, - exc.encoding, exc.reason) - else: - data = self.raw_buffer - converted = len(data) - self.check_printable(data) - self.buffer += data - self.raw_buffer = self.raw_buffer[converted:] - if self.eof: - self.buffer += u'\0' - self.raw_buffer = None - break - - def update_raw(self, size=1024): - data = self.stream.read(size) - if data: - self.raw_buffer += data - self.stream_pointer += len(data) - else: - self.eof = True - -#try: -# import psyco -# psyco.bind(Reader) -#except ImportError: -# pass - diff --git a/python.d/python_modules/pyyaml2/representer.py b/python.d/python_modules/pyyaml2/representer.py deleted file mode 100644 index 5f4fc70db..000000000 --- a/python.d/python_modules/pyyaml2/representer.py +++ /dev/null @@ -1,484 +0,0 @@ - -__all__ = ['BaseRepresenter', 'SafeRepresenter', 'Representer', - 'RepresenterError'] - -from error import * -from nodes import * - -import datetime - -import sys, copy_reg, types - -class RepresenterError(YAMLError): - pass - -class BaseRepresenter(object): - - yaml_representers = {} - yaml_multi_representers = {} - - def __init__(self, default_style=None, default_flow_style=None): - self.default_style = default_style - self.default_flow_style = default_flow_style - self.represented_objects = {} - self.object_keeper = [] - self.alias_key = None - - def represent(self, data): - node = self.represent_data(data) - self.serialize(node) - self.represented_objects = {} - self.object_keeper = [] - self.alias_key = None - - def get_classobj_bases(self, cls): - bases = [cls] - for base in cls.__bases__: - bases.extend(self.get_classobj_bases(base)) - return bases - - def represent_data(self, data): - if self.ignore_aliases(data): - self.alias_key = None - else: - self.alias_key = id(data) - if self.alias_key is not None: - if self.alias_key in self.represented_objects: - node = self.represented_objects[self.alias_key] - #if node is None: - # raise RepresenterError("recursive objects are not allowed: %r" % data) - return node - #self.represented_objects[alias_key] = None - self.object_keeper.append(data) - data_types = type(data).__mro__ - if type(data) is types.InstanceType: - data_types = self.get_classobj_bases(data.__class__)+list(data_types) - if data_types[0] in self.yaml_representers: - node = self.yaml_representers[data_types[0]](self, data) - else: - for data_type in data_types: - if data_type in self.yaml_multi_representers: - node = self.yaml_multi_representers[data_type](self, data) - break - else: - if None in self.yaml_multi_representers: - node = self.yaml_multi_representers[None](self, data) - elif None in self.yaml_representers: - node = self.yaml_representers[None](self, data) - else: - node = ScalarNode(None, unicode(data)) - #if alias_key is not None: - # self.represented_objects[alias_key] = node - return node - - def add_representer(cls, data_type, representer): - if not 'yaml_representers' in cls.__dict__: - cls.yaml_representers = cls.yaml_representers.copy() - cls.yaml_representers[data_type] = representer - add_representer = classmethod(add_representer) - - def add_multi_representer(cls, data_type, representer): - if not 'yaml_multi_representers' in cls.__dict__: - cls.yaml_multi_representers = cls.yaml_multi_representers.copy() - cls.yaml_multi_representers[data_type] = representer - add_multi_representer = classmethod(add_multi_representer) - - def represent_scalar(self, tag, value, style=None): - if style is None: - style = self.default_style - node = ScalarNode(tag, value, style=style) - if self.alias_key is not None: - self.represented_objects[self.alias_key] = node - return node - - def represent_sequence(self, tag, sequence, flow_style=None): - value = [] - node = SequenceNode(tag, value, flow_style=flow_style) - if self.alias_key is not None: - self.represented_objects[self.alias_key] = node - best_style = True - for item in sequence: - node_item = self.represent_data(item) - if not (isinstance(node_item, ScalarNode) and not node_item.style): - best_style = False - value.append(node_item) - if flow_style is None: - if self.default_flow_style is not None: - node.flow_style = self.default_flow_style - else: - node.flow_style = best_style - return node - - def represent_mapping(self, tag, mapping, flow_style=None): - value = [] - node = MappingNode(tag, value, flow_style=flow_style) - if self.alias_key is not None: - self.represented_objects[self.alias_key] = node - best_style = True - if hasattr(mapping, 'items'): - mapping = mapping.items() - mapping.sort() - for item_key, item_value in mapping: - node_key = self.represent_data(item_key) - node_value = self.represent_data(item_value) - if not (isinstance(node_key, ScalarNode) and not node_key.style): - best_style = False - if not (isinstance(node_value, ScalarNode) and not node_value.style): - best_style = False - value.append((node_key, node_value)) - if flow_style is None: - if self.default_flow_style is not None: - node.flow_style = self.default_flow_style - else: - node.flow_style = best_style - return node - - def ignore_aliases(self, data): - return False - -class SafeRepresenter(BaseRepresenter): - - def ignore_aliases(self, data): - if data in [None, ()]: - return True - if isinstance(data, (str, unicode, bool, int, float)): - return True - - def represent_none(self, data): - return self.represent_scalar(u'tag:yaml.org,2002:null', - u'null') - - def represent_str(self, data): - tag = None - style = None - try: - data = unicode(data, 'ascii') - tag = u'tag:yaml.org,2002:str' - except UnicodeDecodeError: - try: - data = unicode(data, 'utf-8') - tag = u'tag:yaml.org,2002:str' - except UnicodeDecodeError: - data = data.encode('base64') - tag = u'tag:yaml.org,2002:binary' - style = '|' - return self.represent_scalar(tag, data, style=style) - - def represent_unicode(self, data): - return self.represent_scalar(u'tag:yaml.org,2002:str', data) - - def represent_bool(self, data): - if data: - value = u'true' - else: - value = u'false' - return self.represent_scalar(u'tag:yaml.org,2002:bool', value) - - def represent_int(self, data): - return self.represent_scalar(u'tag:yaml.org,2002:int', unicode(data)) - - def represent_long(self, data): - return self.represent_scalar(u'tag:yaml.org,2002:int', unicode(data)) - - inf_value = 1e300 - while repr(inf_value) != repr(inf_value*inf_value): - inf_value *= inf_value - - def represent_float(self, data): - if data != data or (data == 0.0 and data == 1.0): - value = u'.nan' - elif data == self.inf_value: - value = u'.inf' - elif data == -self.inf_value: - value = u'-.inf' - else: - value = unicode(repr(data)).lower() - # Note that in some cases `repr(data)` represents a float number - # without the decimal parts. For instance: - # >>> repr(1e17) - # '1e17' - # Unfortunately, this is not a valid float representation according - # to the definition of the `!!float` tag. We fix this by adding - # '.0' before the 'e' symbol. - if u'.' not in value and u'e' in value: - value = value.replace(u'e', u'.0e', 1) - return self.represent_scalar(u'tag:yaml.org,2002:float', value) - - def represent_list(self, data): - #pairs = (len(data) > 0 and isinstance(data, list)) - #if pairs: - # for item in data: - # if not isinstance(item, tuple) or len(item) != 2: - # pairs = False - # break - #if not pairs: - return self.represent_sequence(u'tag:yaml.org,2002:seq', data) - #value = [] - #for item_key, item_value in data: - # value.append(self.represent_mapping(u'tag:yaml.org,2002:map', - # [(item_key, item_value)])) - #return SequenceNode(u'tag:yaml.org,2002:pairs', value) - - def represent_dict(self, data): - return self.represent_mapping(u'tag:yaml.org,2002:map', data) - - def represent_set(self, data): - value = {} - for key in data: - value[key] = None - return self.represent_mapping(u'tag:yaml.org,2002:set', value) - - def represent_date(self, data): - value = unicode(data.isoformat()) - return self.represent_scalar(u'tag:yaml.org,2002:timestamp', value) - - def represent_datetime(self, data): - value = unicode(data.isoformat(' ')) - return self.represent_scalar(u'tag:yaml.org,2002:timestamp', value) - - def represent_yaml_object(self, tag, data, cls, flow_style=None): - if hasattr(data, '__getstate__'): - state = data.__getstate__() - else: - state = data.__dict__.copy() - return self.represent_mapping(tag, state, flow_style=flow_style) - - def represent_undefined(self, data): - raise RepresenterError("cannot represent an object: %s" % data) - -SafeRepresenter.add_representer(type(None), - SafeRepresenter.represent_none) - -SafeRepresenter.add_representer(str, - SafeRepresenter.represent_str) - -SafeRepresenter.add_representer(unicode, - SafeRepresenter.represent_unicode) - -SafeRepresenter.add_representer(bool, - SafeRepresenter.represent_bool) - -SafeRepresenter.add_representer(int, - SafeRepresenter.represent_int) - -SafeRepresenter.add_representer(long, - SafeRepresenter.represent_long) - -SafeRepresenter.add_representer(float, - SafeRepresenter.represent_float) - -SafeRepresenter.add_representer(list, - SafeRepresenter.represent_list) - -SafeRepresenter.add_representer(tuple, - SafeRepresenter.represent_list) - -SafeRepresenter.add_representer(dict, - SafeRepresenter.represent_dict) - -SafeRepresenter.add_representer(set, - SafeRepresenter.represent_set) - -SafeRepresenter.add_representer(datetime.date, - SafeRepresenter.represent_date) - -SafeRepresenter.add_representer(datetime.datetime, - SafeRepresenter.represent_datetime) - -SafeRepresenter.add_representer(None, - SafeRepresenter.represent_undefined) - -class Representer(SafeRepresenter): - - def represent_str(self, data): - tag = None - style = None - try: - data = unicode(data, 'ascii') - tag = u'tag:yaml.org,2002:str' - except UnicodeDecodeError: - try: - data = unicode(data, 'utf-8') - tag = u'tag:yaml.org,2002:python/str' - except UnicodeDecodeError: - data = data.encode('base64') - tag = u'tag:yaml.org,2002:binary' - style = '|' - return self.represent_scalar(tag, data, style=style) - - def represent_unicode(self, data): - tag = None - try: - data.encode('ascii') - tag = u'tag:yaml.org,2002:python/unicode' - except UnicodeEncodeError: - tag = u'tag:yaml.org,2002:str' - return self.represent_scalar(tag, data) - - def represent_long(self, data): - tag = u'tag:yaml.org,2002:int' - if int(data) is not data: - tag = u'tag:yaml.org,2002:python/long' - return self.represent_scalar(tag, unicode(data)) - - def represent_complex(self, data): - if data.imag == 0.0: - data = u'%r' % data.real - elif data.real == 0.0: - data = u'%rj' % data.imag - elif data.imag > 0: - data = u'%r+%rj' % (data.real, data.imag) - else: - data = u'%r%rj' % (data.real, data.imag) - return self.represent_scalar(u'tag:yaml.org,2002:python/complex', data) - - def represent_tuple(self, data): - return self.represent_sequence(u'tag:yaml.org,2002:python/tuple', data) - - def represent_name(self, data): - name = u'%s.%s' % (data.__module__, data.__name__) - return self.represent_scalar(u'tag:yaml.org,2002:python/name:'+name, u'') - - def represent_module(self, data): - return self.represent_scalar( - u'tag:yaml.org,2002:python/module:'+data.__name__, u'') - - def represent_instance(self, data): - # For instances of classic classes, we use __getinitargs__ and - # __getstate__ to serialize the data. - - # If data.__getinitargs__ exists, the object must be reconstructed by - # calling cls(**args), where args is a tuple returned by - # __getinitargs__. Otherwise, the cls.__init__ method should never be - # called and the class instance is created by instantiating a trivial - # class and assigning to the instance's __class__ variable. - - # If data.__getstate__ exists, it returns the state of the object. - # Otherwise, the state of the object is data.__dict__. - - # We produce either a !!python/object or !!python/object/new node. - # If data.__getinitargs__ does not exist and state is a dictionary, we - # produce a !!python/object node . Otherwise we produce a - # !!python/object/new node. - - cls = data.__class__ - class_name = u'%s.%s' % (cls.__module__, cls.__name__) - args = None - state = None - if hasattr(data, '__getinitargs__'): - args = list(data.__getinitargs__()) - if hasattr(data, '__getstate__'): - state = data.__getstate__() - else: - state = data.__dict__ - if args is None and isinstance(state, dict): - return self.represent_mapping( - u'tag:yaml.org,2002:python/object:'+class_name, state) - if isinstance(state, dict) and not state: - return self.represent_sequence( - u'tag:yaml.org,2002:python/object/new:'+class_name, args) - value = {} - if args: - value['args'] = args - value['state'] = state - return self.represent_mapping( - u'tag:yaml.org,2002:python/object/new:'+class_name, value) - - def represent_object(self, data): - # We use __reduce__ API to save the data. data.__reduce__ returns - # a tuple of length 2-5: - # (function, args, state, listitems, dictitems) - - # For reconstructing, we calls function(*args), then set its state, - # listitems, and dictitems if they are not None. - - # A special case is when function.__name__ == '__newobj__'. In this - # case we create the object with args[0].__new__(*args). - - # Another special case is when __reduce__ returns a string - we don't - # support it. - - # We produce a !!python/object, !!python/object/new or - # !!python/object/apply node. - - cls = type(data) - if cls in copy_reg.dispatch_table: - reduce = copy_reg.dispatch_table[cls](data) - elif hasattr(data, '__reduce_ex__'): - reduce = data.__reduce_ex__(2) - elif hasattr(data, '__reduce__'): - reduce = data.__reduce__() - else: - raise RepresenterError("cannot represent object: %r" % data) - reduce = (list(reduce)+[None]*5)[:5] - function, args, state, listitems, dictitems = reduce - args = list(args) - if state is None: - state = {} - if listitems is not None: - listitems = list(listitems) - if dictitems is not None: - dictitems = dict(dictitems) - if function.__name__ == '__newobj__': - function = args[0] - args = args[1:] - tag = u'tag:yaml.org,2002:python/object/new:' - newobj = True - else: - tag = u'tag:yaml.org,2002:python/object/apply:' - newobj = False - function_name = u'%s.%s' % (function.__module__, function.__name__) - if not args and not listitems and not dictitems \ - and isinstance(state, dict) and newobj: - return self.represent_mapping( - u'tag:yaml.org,2002:python/object:'+function_name, state) - if not listitems and not dictitems \ - and isinstance(state, dict) and not state: - return self.represent_sequence(tag+function_name, args) - value = {} - if args: - value['args'] = args - if state or not isinstance(state, dict): - value['state'] = state - if listitems: - value['listitems'] = listitems - if dictitems: - value['dictitems'] = dictitems - return self.represent_mapping(tag+function_name, value) - -Representer.add_representer(str, - Representer.represent_str) - -Representer.add_representer(unicode, - Representer.represent_unicode) - -Representer.add_representer(long, - Representer.represent_long) - -Representer.add_representer(complex, - Representer.represent_complex) - -Representer.add_representer(tuple, - Representer.represent_tuple) - -Representer.add_representer(type, - Representer.represent_name) - -Representer.add_representer(types.ClassType, - Representer.represent_name) - -Representer.add_representer(types.FunctionType, - Representer.represent_name) - -Representer.add_representer(types.BuiltinFunctionType, - Representer.represent_name) - -Representer.add_representer(types.ModuleType, - Representer.represent_module) - -Representer.add_multi_representer(types.InstanceType, - Representer.represent_instance) - -Representer.add_multi_representer(object, - Representer.represent_object) - diff --git a/python.d/python_modules/pyyaml2/resolver.py b/python.d/python_modules/pyyaml2/resolver.py deleted file mode 100644 index 6b5ab8759..000000000 --- a/python.d/python_modules/pyyaml2/resolver.py +++ /dev/null @@ -1,224 +0,0 @@ - -__all__ = ['BaseResolver', 'Resolver'] - -from error import * -from nodes import * - -import re - -class ResolverError(YAMLError): - pass - -class BaseResolver(object): - - DEFAULT_SCALAR_TAG = u'tag:yaml.org,2002:str' - DEFAULT_SEQUENCE_TAG = u'tag:yaml.org,2002:seq' - DEFAULT_MAPPING_TAG = u'tag:yaml.org,2002:map' - - yaml_implicit_resolvers = {} - yaml_path_resolvers = {} - - def __init__(self): - self.resolver_exact_paths = [] - self.resolver_prefix_paths = [] - - def add_implicit_resolver(cls, tag, regexp, first): - if not 'yaml_implicit_resolvers' in cls.__dict__: - cls.yaml_implicit_resolvers = cls.yaml_implicit_resolvers.copy() - if first is None: - first = [None] - for ch in first: - cls.yaml_implicit_resolvers.setdefault(ch, []).append((tag, regexp)) - add_implicit_resolver = classmethod(add_implicit_resolver) - - def add_path_resolver(cls, tag, path, kind=None): - # Note: `add_path_resolver` is experimental. The API could be changed. - # `new_path` is a pattern that is matched against the path from the - # root to the node that is being considered. `node_path` elements are - # tuples `(node_check, index_check)`. `node_check` is a node class: - # `ScalarNode`, `SequenceNode`, `MappingNode` or `None`. `None` - # matches any kind of a node. `index_check` could be `None`, a boolean - # value, a string value, or a number. `None` and `False` match against - # any _value_ of sequence and mapping nodes. `True` matches against - # any _key_ of a mapping node. A string `index_check` matches against - # a mapping value that corresponds to a scalar key which content is - # equal to the `index_check` value. An integer `index_check` matches - # against a sequence value with the index equal to `index_check`. - if not 'yaml_path_resolvers' in cls.__dict__: - cls.yaml_path_resolvers = cls.yaml_path_resolvers.copy() - new_path = [] - for element in path: - if isinstance(element, (list, tuple)): - if len(element) == 2: - node_check, index_check = element - elif len(element) == 1: - node_check = element[0] - index_check = True - else: - raise ResolverError("Invalid path element: %s" % element) - else: - node_check = None - index_check = element - if node_check is str: - node_check = ScalarNode - elif node_check is list: - node_check = SequenceNode - elif node_check is dict: - node_check = MappingNode - elif node_check not in [ScalarNode, SequenceNode, MappingNode] \ - and not isinstance(node_check, basestring) \ - and node_check is not None: - raise ResolverError("Invalid node checker: %s" % node_check) - if not isinstance(index_check, (basestring, int)) \ - and index_check is not None: - raise ResolverError("Invalid index checker: %s" % index_check) - new_path.append((node_check, index_check)) - if kind is str: - kind = ScalarNode - elif kind is list: - kind = SequenceNode - elif kind is dict: - kind = MappingNode - elif kind not in [ScalarNode, SequenceNode, MappingNode] \ - and kind is not None: - raise ResolverError("Invalid node kind: %s" % kind) - cls.yaml_path_resolvers[tuple(new_path), kind] = tag - add_path_resolver = classmethod(add_path_resolver) - - def descend_resolver(self, current_node, current_index): - if not self.yaml_path_resolvers: - return - exact_paths = {} - prefix_paths = [] - if current_node: - depth = len(self.resolver_prefix_paths) - for path, kind in self.resolver_prefix_paths[-1]: - if self.check_resolver_prefix(depth, path, kind, - current_node, current_index): - if len(path) > depth: - prefix_paths.append((path, kind)) - else: - exact_paths[kind] = self.yaml_path_resolvers[path, kind] - else: - for path, kind in self.yaml_path_resolvers: - if not path: - exact_paths[kind] = self.yaml_path_resolvers[path, kind] - else: - prefix_paths.append((path, kind)) - self.resolver_exact_paths.append(exact_paths) - self.resolver_prefix_paths.append(prefix_paths) - - def ascend_resolver(self): - if not self.yaml_path_resolvers: - return - self.resolver_exact_paths.pop() - self.resolver_prefix_paths.pop() - - def check_resolver_prefix(self, depth, path, kind, - current_node, current_index): - node_check, index_check = path[depth-1] - if isinstance(node_check, basestring): - if current_node.tag != node_check: - return - elif node_check is not None: - if not isinstance(current_node, node_check): - return - if index_check is True and current_index is not None: - return - if (index_check is False or index_check is None) \ - and current_index is None: - return - if isinstance(index_check, basestring): - if not (isinstance(current_index, ScalarNode) - and index_check == current_index.value): - return - elif isinstance(index_check, int) and not isinstance(index_check, bool): - if index_check != current_index: - return - return True - - def resolve(self, kind, value, implicit): - if kind is ScalarNode and implicit[0]: - if value == u'': - resolvers = self.yaml_implicit_resolvers.get(u'', []) - else: - resolvers = self.yaml_implicit_resolvers.get(value[0], []) - resolvers += self.yaml_implicit_resolvers.get(None, []) - for tag, regexp in resolvers: - if regexp.match(value): - return tag - implicit = implicit[1] - if self.yaml_path_resolvers: - exact_paths = self.resolver_exact_paths[-1] - if kind in exact_paths: - return exact_paths[kind] - if None in exact_paths: - return exact_paths[None] - if kind is ScalarNode: - return self.DEFAULT_SCALAR_TAG - elif kind is SequenceNode: - return self.DEFAULT_SEQUENCE_TAG - elif kind is MappingNode: - return self.DEFAULT_MAPPING_TAG - -class Resolver(BaseResolver): - pass - -Resolver.add_implicit_resolver( - u'tag:yaml.org,2002:bool', - re.compile(ur'''^(?:yes|Yes|YES|no|No|NO - |true|True|TRUE|false|False|FALSE - |on|On|ON|off|Off|OFF)$''', re.X), - list(u'yYnNtTfFoO')) - -Resolver.add_implicit_resolver( - u'tag:yaml.org,2002:float', - re.compile(ur'''^(?:[-+]?(?:[0-9][0-9_]*)\.[0-9_]*(?:[eE][-+][0-9]+)? - |\.[0-9_]+(?:[eE][-+][0-9]+)? - |[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+\.[0-9_]* - |[-+]?\.(?:inf|Inf|INF) - |\.(?:nan|NaN|NAN))$''', re.X), - list(u'-+0123456789.')) - -Resolver.add_implicit_resolver( - u'tag:yaml.org,2002:int', - re.compile(ur'''^(?:[-+]?0b[0-1_]+ - |[-+]?0[0-7_]+ - |[-+]?(?:0|[1-9][0-9_]*) - |[-+]?0x[0-9a-fA-F_]+ - |[-+]?[1-9][0-9_]*(?::[0-5]?[0-9])+)$''', re.X), - list(u'-+0123456789')) - -Resolver.add_implicit_resolver( - u'tag:yaml.org,2002:merge', - re.compile(ur'^(?:<<)$'), - [u'<']) - -Resolver.add_implicit_resolver( - u'tag:yaml.org,2002:null', - re.compile(ur'''^(?: ~ - |null|Null|NULL - | )$''', re.X), - [u'~', u'n', u'N', u'']) - -Resolver.add_implicit_resolver( - u'tag:yaml.org,2002:timestamp', - re.compile(ur'''^(?:[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9] - |[0-9][0-9][0-9][0-9] -[0-9][0-9]? -[0-9][0-9]? - (?:[Tt]|[ \t]+)[0-9][0-9]? - :[0-9][0-9] :[0-9][0-9] (?:\.[0-9]*)? - (?:[ \t]*(?:Z|[-+][0-9][0-9]?(?::[0-9][0-9])?))?)$''', re.X), - list(u'0123456789')) - -Resolver.add_implicit_resolver( - u'tag:yaml.org,2002:value', - re.compile(ur'^(?:=)$'), - [u'=']) - -# The following resolver is only for documentation purposes. It cannot work -# because plain scalars cannot start with '!', '&', or '*'. -Resolver.add_implicit_resolver( - u'tag:yaml.org,2002:yaml', - re.compile(ur'^(?:!|&|\*)$'), - list(u'!&*')) - diff --git a/python.d/python_modules/pyyaml2/scanner.py b/python.d/python_modules/pyyaml2/scanner.py deleted file mode 100644 index 5228fad65..000000000 --- a/python.d/python_modules/pyyaml2/scanner.py +++ /dev/null @@ -1,1457 +0,0 @@ - -# Scanner produces tokens of the following types: -# STREAM-START -# STREAM-END -# DIRECTIVE(name, value) -# DOCUMENT-START -# DOCUMENT-END -# BLOCK-SEQUENCE-START -# BLOCK-MAPPING-START -# BLOCK-END -# FLOW-SEQUENCE-START -# FLOW-MAPPING-START -# FLOW-SEQUENCE-END -# FLOW-MAPPING-END -# BLOCK-ENTRY -# FLOW-ENTRY -# KEY -# VALUE -# ALIAS(value) -# ANCHOR(value) -# TAG(value) -# SCALAR(value, plain, style) -# -# Read comments in the Scanner code for more details. -# - -__all__ = ['Scanner', 'ScannerError'] - -from error import MarkedYAMLError -from tokens import * - -class ScannerError(MarkedYAMLError): - pass - -class SimpleKey(object): - # See below simple keys treatment. - - def __init__(self, token_number, required, index, line, column, mark): - self.token_number = token_number - self.required = required - self.index = index - self.line = line - self.column = column - self.mark = mark - -class Scanner(object): - - def __init__(self): - """Initialize the scanner.""" - # It is assumed that Scanner and Reader will have a common descendant. - # Reader do the dirty work of checking for BOM and converting the - # input data to Unicode. It also adds NUL to the end. - # - # Reader supports the following methods - # self.peek(i=0) # peek the next i-th character - # self.prefix(l=1) # peek the next l characters - # self.forward(l=1) # read the next l characters and move the pointer. - - # Had we reached the end of the stream? - self.done = False - - # The number of unclosed '{' and '['. `flow_level == 0` means block - # context. - self.flow_level = 0 - - # List of processed tokens that are not yet emitted. - self.tokens = [] - - # Add the STREAM-START token. - self.fetch_stream_start() - - # Number of tokens that were emitted through the `get_token` method. - self.tokens_taken = 0 - - # The current indentation level. - self.indent = -1 - - # Past indentation levels. - self.indents = [] - - # Variables related to simple keys treatment. - - # A simple key is a key that is not denoted by the '?' indicator. - # Example of simple keys: - # --- - # block simple key: value - # ? not a simple key: - # : { flow simple key: value } - # We emit the KEY token before all keys, so when we find a potential - # simple key, we try to locate the corresponding ':' indicator. - # Simple keys should be limited to a single line and 1024 characters. - - # Can a simple key start at the current position? A simple key may - # start: - # - at the beginning of the line, not counting indentation spaces - # (in block context), - # - after '{', '[', ',' (in the flow context), - # - after '?', ':', '-' (in the block context). - # In the block context, this flag also signifies if a block collection - # may start at the current position. - self.allow_simple_key = True - - # Keep track of possible simple keys. This is a dictionary. The key - # is `flow_level`; there can be no more that one possible simple key - # for each level. The value is a SimpleKey record: - # (token_number, required, index, line, column, mark) - # A simple key may start with ALIAS, ANCHOR, TAG, SCALAR(flow), - # '[', or '{' tokens. - self.possible_simple_keys = {} - - # Public methods. - - def check_token(self, *choices): - # Check if the next token is one of the given types. - while self.need_more_tokens(): - self.fetch_more_tokens() - if self.tokens: - if not choices: - return True - for choice in choices: - if isinstance(self.tokens[0], choice): - return True - return False - - def peek_token(self): - # Return the next token, but do not delete if from the queue. - while self.need_more_tokens(): - self.fetch_more_tokens() - if self.tokens: - return self.tokens[0] - - def get_token(self): - # Return the next token. - while self.need_more_tokens(): - self.fetch_more_tokens() - if self.tokens: - self.tokens_taken += 1 - return self.tokens.pop(0) - - # Private methods. - - def need_more_tokens(self): - if self.done: - return False - if not self.tokens: - return True - # The current token may be a potential simple key, so we - # need to look further. - self.stale_possible_simple_keys() - if self.next_possible_simple_key() == self.tokens_taken: - return True - - def fetch_more_tokens(self): - - # Eat whitespaces and comments until we reach the next token. - self.scan_to_next_token() - - # Remove obsolete possible simple keys. - self.stale_possible_simple_keys() - - # Compare the current indentation and column. It may add some tokens - # and decrease the current indentation level. - self.unwind_indent(self.column) - - # Peek the next character. - ch = self.peek() - - # Is it the end of stream? - if ch == u'\0': - return self.fetch_stream_end() - - # Is it a directive? - if ch == u'%' and self.check_directive(): - return self.fetch_directive() - - # Is it the document start? - if ch == u'-' and self.check_document_start(): - return self.fetch_document_start() - - # Is it the document end? - if ch == u'.' and self.check_document_end(): - return self.fetch_document_end() - - # TODO: support for BOM within a stream. - #if ch == u'\uFEFF': - # return self.fetch_bom() <-- issue BOMToken - - # Note: the order of the following checks is NOT significant. - - # Is it the flow sequence start indicator? - if ch == u'[': - return self.fetch_flow_sequence_start() - - # Is it the flow mapping start indicator? - if ch == u'{': - return self.fetch_flow_mapping_start() - - # Is it the flow sequence end indicator? - if ch == u']': - return self.fetch_flow_sequence_end() - - # Is it the flow mapping end indicator? - if ch == u'}': - return self.fetch_flow_mapping_end() - - # Is it the flow entry indicator? - if ch == u',': - return self.fetch_flow_entry() - - # Is it the block entry indicator? - if ch == u'-' and self.check_block_entry(): - return self.fetch_block_entry() - - # Is it the key indicator? - if ch == u'?' and self.check_key(): - return self.fetch_key() - - # Is it the value indicator? - if ch == u':' and self.check_value(): - return self.fetch_value() - - # Is it an alias? - if ch == u'*': - return self.fetch_alias() - - # Is it an anchor? - if ch == u'&': - return self.fetch_anchor() - - # Is it a tag? - if ch == u'!': - return self.fetch_tag() - - # Is it a literal scalar? - if ch == u'|' and not self.flow_level: - return self.fetch_literal() - - # Is it a folded scalar? - if ch == u'>' and not self.flow_level: - return self.fetch_folded() - - # Is it a single quoted scalar? - if ch == u'\'': - return self.fetch_single() - - # Is it a double quoted scalar? - if ch == u'\"': - return self.fetch_double() - - # It must be a plain scalar then. - if self.check_plain(): - return self.fetch_plain() - - # No? It's an error. Let's produce a nice error message. - raise ScannerError("while scanning for the next token", None, - "found character %r that cannot start any token" - % ch.encode('utf-8'), self.get_mark()) - - # Simple keys treatment. - - def next_possible_simple_key(self): - # Return the number of the nearest possible simple key. Actually we - # don't need to loop through the whole dictionary. We may replace it - # with the following code: - # if not self.possible_simple_keys: - # return None - # return self.possible_simple_keys[ - # min(self.possible_simple_keys.keys())].token_number - min_token_number = None - for level in self.possible_simple_keys: - key = self.possible_simple_keys[level] - if min_token_number is None or key.token_number < min_token_number: - min_token_number = key.token_number - return min_token_number - - def stale_possible_simple_keys(self): - # Remove entries that are no longer possible simple keys. According to - # the YAML specification, simple keys - # - should be limited to a single line, - # - should be no longer than 1024 characters. - # Disabling this procedure will allow simple keys of any length and - # height (may cause problems if indentation is broken though). - for level in self.possible_simple_keys.keys(): - key = self.possible_simple_keys[level] - if key.line != self.line \ - or self.index-key.index > 1024: - if key.required: - raise ScannerError("while scanning a simple key", key.mark, - "could not found expected ':'", self.get_mark()) - del self.possible_simple_keys[level] - - def save_possible_simple_key(self): - # The next token may start a simple key. We check if it's possible - # and save its position. This function is called for - # ALIAS, ANCHOR, TAG, SCALAR(flow), '[', and '{'. - - # Check if a simple key is required at the current position. - required = not self.flow_level and self.indent == self.column - - # A simple key is required only if it is the first token in the current - # line. Therefore it is always allowed. - assert self.allow_simple_key or not required - - # The next token might be a simple key. Let's save it's number and - # position. - if self.allow_simple_key: - self.remove_possible_simple_key() - token_number = self.tokens_taken+len(self.tokens) - key = SimpleKey(token_number, required, - self.index, self.line, self.column, self.get_mark()) - self.possible_simple_keys[self.flow_level] = key - - def remove_possible_simple_key(self): - # Remove the saved possible key position at the current flow level. - if self.flow_level in self.possible_simple_keys: - key = self.possible_simple_keys[self.flow_level] - - if key.required: - raise ScannerError("while scanning a simple key", key.mark, - "could not found expected ':'", self.get_mark()) - - del self.possible_simple_keys[self.flow_level] - - # Indentation functions. - - def unwind_indent(self, column): - - ## In flow context, tokens should respect indentation. - ## Actually the condition should be `self.indent >= column` according to - ## the spec. But this condition will prohibit intuitively correct - ## constructions such as - ## key : { - ## } - #if self.flow_level and self.indent > column: - # raise ScannerError(None, None, - # "invalid intendation or unclosed '[' or '{'", - # self.get_mark()) - - # In the flow context, indentation is ignored. We make the scanner less - # restrictive then specification requires. - if self.flow_level: - return - - # In block context, we may need to issue the BLOCK-END tokens. - while self.indent > column: - mark = self.get_mark() - self.indent = self.indents.pop() - self.tokens.append(BlockEndToken(mark, mark)) - - def add_indent(self, column): - # Check if we need to increase indentation. - if self.indent < column: - self.indents.append(self.indent) - self.indent = column - return True - return False - - # Fetchers. - - def fetch_stream_start(self): - # We always add STREAM-START as the first token and STREAM-END as the - # last token. - - # Read the token. - mark = self.get_mark() - - # Add STREAM-START. - self.tokens.append(StreamStartToken(mark, mark, - encoding=self.encoding)) - - - def fetch_stream_end(self): - - # Set the current intendation to -1. - self.unwind_indent(-1) - - # Reset simple keys. - self.remove_possible_simple_key() - self.allow_simple_key = False - self.possible_simple_keys = {} - - # Read the token. - mark = self.get_mark() - - # Add STREAM-END. - self.tokens.append(StreamEndToken(mark, mark)) - - # The steam is finished. - self.done = True - - def fetch_directive(self): - - # Set the current intendation to -1. - self.unwind_indent(-1) - - # Reset simple keys. - self.remove_possible_simple_key() - self.allow_simple_key = False - - # Scan and add DIRECTIVE. - self.tokens.append(self.scan_directive()) - - def fetch_document_start(self): - self.fetch_document_indicator(DocumentStartToken) - - def fetch_document_end(self): - self.fetch_document_indicator(DocumentEndToken) - - def fetch_document_indicator(self, TokenClass): - - # Set the current intendation to -1. - self.unwind_indent(-1) - - # Reset simple keys. Note that there could not be a block collection - # after '---'. - self.remove_possible_simple_key() - self.allow_simple_key = False - - # Add DOCUMENT-START or DOCUMENT-END. - start_mark = self.get_mark() - self.forward(3) - end_mark = self.get_mark() - self.tokens.append(TokenClass(start_mark, end_mark)) - - def fetch_flow_sequence_start(self): - self.fetch_flow_collection_start(FlowSequenceStartToken) - - def fetch_flow_mapping_start(self): - self.fetch_flow_collection_start(FlowMappingStartToken) - - def fetch_flow_collection_start(self, TokenClass): - - # '[' and '{' may start a simple key. - self.save_possible_simple_key() - - # Increase the flow level. - self.flow_level += 1 - - # Simple keys are allowed after '[' and '{'. - self.allow_simple_key = True - - # Add FLOW-SEQUENCE-START or FLOW-MAPPING-START. - start_mark = self.get_mark() - self.forward() - end_mark = self.get_mark() - self.tokens.append(TokenClass(start_mark, end_mark)) - - def fetch_flow_sequence_end(self): - self.fetch_flow_collection_end(FlowSequenceEndToken) - - def fetch_flow_mapping_end(self): - self.fetch_flow_collection_end(FlowMappingEndToken) - - def fetch_flow_collection_end(self, TokenClass): - - # Reset possible simple key on the current level. - self.remove_possible_simple_key() - - # Decrease the flow level. - self.flow_level -= 1 - - # No simple keys after ']' or '}'. - self.allow_simple_key = False - - # Add FLOW-SEQUENCE-END or FLOW-MAPPING-END. - start_mark = self.get_mark() - self.forward() - end_mark = self.get_mark() - self.tokens.append(TokenClass(start_mark, end_mark)) - - def fetch_flow_entry(self): - - # Simple keys are allowed after ','. - self.allow_simple_key = True - - # Reset possible simple key on the current level. - self.remove_possible_simple_key() - - # Add FLOW-ENTRY. - start_mark = self.get_mark() - self.forward() - end_mark = self.get_mark() - self.tokens.append(FlowEntryToken(start_mark, end_mark)) - - def fetch_block_entry(self): - - # Block context needs additional checks. - if not self.flow_level: - - # Are we allowed to start a new entry? - if not self.allow_simple_key: - raise ScannerError(None, None, - "sequence entries are not allowed here", - self.get_mark()) - - # We may need to add BLOCK-SEQUENCE-START. - if self.add_indent(self.column): - mark = self.get_mark() - self.tokens.append(BlockSequenceStartToken(mark, mark)) - - # It's an error for the block entry to occur in the flow context, - # but we let the parser detect this. - else: - pass - - # Simple keys are allowed after '-'. - self.allow_simple_key = True - - # Reset possible simple key on the current level. - self.remove_possible_simple_key() - - # Add BLOCK-ENTRY. - start_mark = self.get_mark() - self.forward() - end_mark = self.get_mark() - self.tokens.append(BlockEntryToken(start_mark, end_mark)) - - def fetch_key(self): - - # Block context needs additional checks. - if not self.flow_level: - - # Are we allowed to start a key (not nessesary a simple)? - if not self.allow_simple_key: - raise ScannerError(None, None, - "mapping keys are not allowed here", - self.get_mark()) - - # We may need to add BLOCK-MAPPING-START. - if self.add_indent(self.column): - mark = self.get_mark() - self.tokens.append(BlockMappingStartToken(mark, mark)) - - # Simple keys are allowed after '?' in the block context. - self.allow_simple_key = not self.flow_level - - # Reset possible simple key on the current level. - self.remove_possible_simple_key() - - # Add KEY. - start_mark = self.get_mark() - self.forward() - end_mark = self.get_mark() - self.tokens.append(KeyToken(start_mark, end_mark)) - - def fetch_value(self): - - # Do we determine a simple key? - if self.flow_level in self.possible_simple_keys: - - # Add KEY. - key = self.possible_simple_keys[self.flow_level] - del self.possible_simple_keys[self.flow_level] - self.tokens.insert(key.token_number-self.tokens_taken, - KeyToken(key.mark, key.mark)) - - # If this key starts a new block mapping, we need to add - # BLOCK-MAPPING-START. - if not self.flow_level: - if self.add_indent(key.column): - self.tokens.insert(key.token_number-self.tokens_taken, - BlockMappingStartToken(key.mark, key.mark)) - - # There cannot be two simple keys one after another. - self.allow_simple_key = False - - # It must be a part of a complex key. - else: - - # Block context needs additional checks. - # (Do we really need them? They will be catched by the parser - # anyway.) - if not self.flow_level: - - # We are allowed to start a complex value if and only if - # we can start a simple key. - if not self.allow_simple_key: - raise ScannerError(None, None, - "mapping values are not allowed here", - self.get_mark()) - - # If this value starts a new block mapping, we need to add - # BLOCK-MAPPING-START. It will be detected as an error later by - # the parser. - if not self.flow_level: - if self.add_indent(self.column): - mark = self.get_mark() - self.tokens.append(BlockMappingStartToken(mark, mark)) - - # Simple keys are allowed after ':' in the block context. - self.allow_simple_key = not self.flow_level - - # Reset possible simple key on the current level. - self.remove_possible_simple_key() - - # Add VALUE. - start_mark = self.get_mark() - self.forward() - end_mark = self.get_mark() - self.tokens.append(ValueToken(start_mark, end_mark)) - - def fetch_alias(self): - - # ALIAS could be a simple key. - self.save_possible_simple_key() - - # No simple keys after ALIAS. - self.allow_simple_key = False - - # Scan and add ALIAS. - self.tokens.append(self.scan_anchor(AliasToken)) - - def fetch_anchor(self): - - # ANCHOR could start a simple key. - self.save_possible_simple_key() - - # No simple keys after ANCHOR. - self.allow_simple_key = False - - # Scan and add ANCHOR. - self.tokens.append(self.scan_anchor(AnchorToken)) - - def fetch_tag(self): - - # TAG could start a simple key. - self.save_possible_simple_key() - - # No simple keys after TAG. - self.allow_simple_key = False - - # Scan and add TAG. - self.tokens.append(self.scan_tag()) - - def fetch_literal(self): - self.fetch_block_scalar(style='|') - - def fetch_folded(self): - self.fetch_block_scalar(style='>') - - def fetch_block_scalar(self, style): - - # A simple key may follow a block scalar. - self.allow_simple_key = True - - # Reset possible simple key on the current level. - self.remove_possible_simple_key() - - # Scan and add SCALAR. - self.tokens.append(self.scan_block_scalar(style)) - - def fetch_single(self): - self.fetch_flow_scalar(style='\'') - - def fetch_double(self): - self.fetch_flow_scalar(style='"') - - def fetch_flow_scalar(self, style): - - # A flow scalar could be a simple key. - self.save_possible_simple_key() - - # No simple keys after flow scalars. - self.allow_simple_key = False - - # Scan and add SCALAR. - self.tokens.append(self.scan_flow_scalar(style)) - - def fetch_plain(self): - - # A plain scalar could be a simple key. - self.save_possible_simple_key() - - # No simple keys after plain scalars. But note that `scan_plain` will - # change this flag if the scan is finished at the beginning of the - # line. - self.allow_simple_key = False - - # Scan and add SCALAR. May change `allow_simple_key`. - self.tokens.append(self.scan_plain()) - - # Checkers. - - def check_directive(self): - - # DIRECTIVE: ^ '%' ... - # The '%' indicator is already checked. - if self.column == 0: - return True - - def check_document_start(self): - - # DOCUMENT-START: ^ '---' (' '|'\n') - if self.column == 0: - if self.prefix(3) == u'---' \ - and self.peek(3) in u'\0 \t\r\n\x85\u2028\u2029': - return True - - def check_document_end(self): - - # DOCUMENT-END: ^ '...' (' '|'\n') - if self.column == 0: - if self.prefix(3) == u'...' \ - and self.peek(3) in u'\0 \t\r\n\x85\u2028\u2029': - return True - - def check_block_entry(self): - - # BLOCK-ENTRY: '-' (' '|'\n') - return self.peek(1) in u'\0 \t\r\n\x85\u2028\u2029' - - def check_key(self): - - # KEY(flow context): '?' - if self.flow_level: - return True - - # KEY(block context): '?' (' '|'\n') - else: - return self.peek(1) in u'\0 \t\r\n\x85\u2028\u2029' - - def check_value(self): - - # VALUE(flow context): ':' - if self.flow_level: - return True - - # VALUE(block context): ':' (' '|'\n') - else: - return self.peek(1) in u'\0 \t\r\n\x85\u2028\u2029' - - def check_plain(self): - - # A plain scalar may start with any non-space character except: - # '-', '?', ':', ',', '[', ']', '{', '}', - # '#', '&', '*', '!', '|', '>', '\'', '\"', - # '%', '@', '`'. - # - # It may also start with - # '-', '?', ':' - # if it is followed by a non-space character. - # - # Note that we limit the last rule to the block context (except the - # '-' character) because we want the flow context to be space - # independent. - ch = self.peek() - return ch not in u'\0 \t\r\n\x85\u2028\u2029-?:,[]{}#&*!|>\'\"%@`' \ - or (self.peek(1) not in u'\0 \t\r\n\x85\u2028\u2029' - and (ch == u'-' or (not self.flow_level and ch in u'?:'))) - - # Scanners. - - def scan_to_next_token(self): - # We ignore spaces, line breaks and comments. - # If we find a line break in the block context, we set the flag - # `allow_simple_key` on. - # The byte order mark is stripped if it's the first character in the - # stream. We do not yet support BOM inside the stream as the - # specification requires. Any such mark will be considered as a part - # of the document. - # - # TODO: We need to make tab handling rules more sane. A good rule is - # Tabs cannot precede tokens - # BLOCK-SEQUENCE-START, BLOCK-MAPPING-START, BLOCK-END, - # KEY(block), VALUE(block), BLOCK-ENTRY - # So the checking code is - # if <TAB>: - # self.allow_simple_keys = False - # We also need to add the check for `allow_simple_keys == True` to - # `unwind_indent` before issuing BLOCK-END. - # Scanners for block, flow, and plain scalars need to be modified. - - if self.index == 0 and self.peek() == u'\uFEFF': - self.forward() - found = False - while not found: - while self.peek() == u' ': - self.forward() - if self.peek() == u'#': - while self.peek() not in u'\0\r\n\x85\u2028\u2029': - self.forward() - if self.scan_line_break(): - if not self.flow_level: - self.allow_simple_key = True - else: - found = True - - def scan_directive(self): - # See the specification for details. - start_mark = self.get_mark() - self.forward() - name = self.scan_directive_name(start_mark) - value = None - if name == u'YAML': - value = self.scan_yaml_directive_value(start_mark) - end_mark = self.get_mark() - elif name == u'TAG': - value = self.scan_tag_directive_value(start_mark) - end_mark = self.get_mark() - else: - end_mark = self.get_mark() - while self.peek() not in u'\0\r\n\x85\u2028\u2029': - self.forward() - self.scan_directive_ignored_line(start_mark) - return DirectiveToken(name, value, start_mark, end_mark) - - def scan_directive_name(self, start_mark): - # See the specification for details. - length = 0 - ch = self.peek(length) - while u'0' <= ch <= u'9' or u'A' <= ch <= u'Z' or u'a' <= ch <= u'z' \ - or ch in u'-_': - length += 1 - ch = self.peek(length) - if not length: - raise ScannerError("while scanning a directive", start_mark, - "expected alphabetic or numeric character, but found %r" - % ch.encode('utf-8'), self.get_mark()) - value = self.prefix(length) - self.forward(length) - ch = self.peek() - if ch not in u'\0 \r\n\x85\u2028\u2029': - raise ScannerError("while scanning a directive", start_mark, - "expected alphabetic or numeric character, but found %r" - % ch.encode('utf-8'), self.get_mark()) - return value - - def scan_yaml_directive_value(self, start_mark): - # See the specification for details. - while self.peek() == u' ': - self.forward() - major = self.scan_yaml_directive_number(start_mark) - if self.peek() != '.': - raise ScannerError("while scanning a directive", start_mark, - "expected a digit or '.', but found %r" - % self.peek().encode('utf-8'), - self.get_mark()) - self.forward() - minor = self.scan_yaml_directive_number(start_mark) - if self.peek() not in u'\0 \r\n\x85\u2028\u2029': - raise ScannerError("while scanning a directive", start_mark, - "expected a digit or ' ', but found %r" - % self.peek().encode('utf-8'), - self.get_mark()) - return (major, minor) - - def scan_yaml_directive_number(self, start_mark): - # See the specification for details. - ch = self.peek() - if not (u'0' <= ch <= u'9'): - raise ScannerError("while scanning a directive", start_mark, - "expected a digit, but found %r" % ch.encode('utf-8'), - self.get_mark()) - length = 0 - while u'0' <= self.peek(length) <= u'9': - length += 1 - value = int(self.prefix(length)) - self.forward(length) - return value - - def scan_tag_directive_value(self, start_mark): - # See the specification for details. - while self.peek() == u' ': - self.forward() - handle = self.scan_tag_directive_handle(start_mark) - while self.peek() == u' ': - self.forward() - prefix = self.scan_tag_directive_prefix(start_mark) - return (handle, prefix) - - def scan_tag_directive_handle(self, start_mark): - # See the specification for details. - value = self.scan_tag_handle('directive', start_mark) - ch = self.peek() - if ch != u' ': - raise ScannerError("while scanning a directive", start_mark, - "expected ' ', but found %r" % ch.encode('utf-8'), - self.get_mark()) - return value - - def scan_tag_directive_prefix(self, start_mark): - # See the specification for details. - value = self.scan_tag_uri('directive', start_mark) - ch = self.peek() - if ch not in u'\0 \r\n\x85\u2028\u2029': - raise ScannerError("while scanning a directive", start_mark, - "expected ' ', but found %r" % ch.encode('utf-8'), - self.get_mark()) - return value - - def scan_directive_ignored_line(self, start_mark): - # See the specification for details. - while self.peek() == u' ': - self.forward() - if self.peek() == u'#': - while self.peek() not in u'\0\r\n\x85\u2028\u2029': - self.forward() - ch = self.peek() - if ch not in u'\0\r\n\x85\u2028\u2029': - raise ScannerError("while scanning a directive", start_mark, - "expected a comment or a line break, but found %r" - % ch.encode('utf-8'), self.get_mark()) - self.scan_line_break() - - def scan_anchor(self, TokenClass): - # The specification does not restrict characters for anchors and - # aliases. This may lead to problems, for instance, the document: - # [ *alias, value ] - # can be interpteted in two ways, as - # [ "value" ] - # and - # [ *alias , "value" ] - # Therefore we restrict aliases to numbers and ASCII letters. - start_mark = self.get_mark() - indicator = self.peek() - if indicator == u'*': - name = 'alias' - else: - name = 'anchor' - self.forward() - length = 0 - ch = self.peek(length) - while u'0' <= ch <= u'9' or u'A' <= ch <= u'Z' or u'a' <= ch <= u'z' \ - or ch in u'-_': - length += 1 - ch = self.peek(length) - if not length: - raise ScannerError("while scanning an %s" % name, start_mark, - "expected alphabetic or numeric character, but found %r" - % ch.encode('utf-8'), self.get_mark()) - value = self.prefix(length) - self.forward(length) - ch = self.peek() - if ch not in u'\0 \t\r\n\x85\u2028\u2029?:,]}%@`': - raise ScannerError("while scanning an %s" % name, start_mark, - "expected alphabetic or numeric character, but found %r" - % ch.encode('utf-8'), self.get_mark()) - end_mark = self.get_mark() - return TokenClass(value, start_mark, end_mark) - - def scan_tag(self): - # See the specification for details. - start_mark = self.get_mark() - ch = self.peek(1) - if ch == u'<': - handle = None - self.forward(2) - suffix = self.scan_tag_uri('tag', start_mark) - if self.peek() != u'>': - raise ScannerError("while parsing a tag", start_mark, - "expected '>', but found %r" % self.peek().encode('utf-8'), - self.get_mark()) - self.forward() - elif ch in u'\0 \t\r\n\x85\u2028\u2029': - handle = None - suffix = u'!' - self.forward() - else: - length = 1 - use_handle = False - while ch not in u'\0 \r\n\x85\u2028\u2029': - if ch == u'!': - use_handle = True - break - length += 1 - ch = self.peek(length) - handle = u'!' - if use_handle: - handle = self.scan_tag_handle('tag', start_mark) - else: - handle = u'!' - self.forward() - suffix = self.scan_tag_uri('tag', start_mark) - ch = self.peek() - if ch not in u'\0 \r\n\x85\u2028\u2029': - raise ScannerError("while scanning a tag", start_mark, - "expected ' ', but found %r" % ch.encode('utf-8'), - self.get_mark()) - value = (handle, suffix) - end_mark = self.get_mark() - return TagToken(value, start_mark, end_mark) - - def scan_block_scalar(self, style): - # See the specification for details. - - if style == '>': - folded = True - else: - folded = False - - chunks = [] - start_mark = self.get_mark() - - # Scan the header. - self.forward() - chomping, increment = self.scan_block_scalar_indicators(start_mark) - self.scan_block_scalar_ignored_line(start_mark) - - # Determine the indentation level and go to the first non-empty line. - min_indent = self.indent+1 - if min_indent < 1: - min_indent = 1 - if increment is None: - breaks, max_indent, end_mark = self.scan_block_scalar_indentation() - indent = max(min_indent, max_indent) - else: - indent = min_indent+increment-1 - breaks, end_mark = self.scan_block_scalar_breaks(indent) - line_break = u'' - - # Scan the inner part of the block scalar. - while self.column == indent and self.peek() != u'\0': - chunks.extend(breaks) - leading_non_space = self.peek() not in u' \t' - length = 0 - while self.peek(length) not in u'\0\r\n\x85\u2028\u2029': - length += 1 - chunks.append(self.prefix(length)) - self.forward(length) - line_break = self.scan_line_break() - breaks, end_mark = self.scan_block_scalar_breaks(indent) - if self.column == indent and self.peek() != u'\0': - - # Unfortunately, folding rules are ambiguous. - # - # This is the folding according to the specification: - - if folded and line_break == u'\n' \ - and leading_non_space and self.peek() not in u' \t': - if not breaks: - chunks.append(u' ') - else: - chunks.append(line_break) - - # This is Clark Evans's interpretation (also in the spec - # examples): - # - #if folded and line_break == u'\n': - # if not breaks: - # if self.peek() not in ' \t': - # chunks.append(u' ') - # else: - # chunks.append(line_break) - #else: - # chunks.append(line_break) - else: - break - - # Chomp the tail. - if chomping is not False: - chunks.append(line_break) - if chomping is True: - chunks.extend(breaks) - - # We are done. - return ScalarToken(u''.join(chunks), False, start_mark, end_mark, - style) - - def scan_block_scalar_indicators(self, start_mark): - # See the specification for details. - chomping = None - increment = None - ch = self.peek() - if ch in u'+-': - if ch == '+': - chomping = True - else: - chomping = False - self.forward() - ch = self.peek() - if ch in u'0123456789': - increment = int(ch) - if increment == 0: - raise ScannerError("while scanning a block scalar", start_mark, - "expected indentation indicator in the range 1-9, but found 0", - self.get_mark()) - self.forward() - elif ch in u'0123456789': - increment = int(ch) - if increment == 0: - raise ScannerError("while scanning a block scalar", start_mark, - "expected indentation indicator in the range 1-9, but found 0", - self.get_mark()) - self.forward() - ch = self.peek() - if ch in u'+-': - if ch == '+': - chomping = True - else: - chomping = False - self.forward() - ch = self.peek() - if ch not in u'\0 \r\n\x85\u2028\u2029': - raise ScannerError("while scanning a block scalar", start_mark, - "expected chomping or indentation indicators, but found %r" - % ch.encode('utf-8'), self.get_mark()) - return chomping, increment - - def scan_block_scalar_ignored_line(self, start_mark): - # See the specification for details. - while self.peek() == u' ': - self.forward() - if self.peek() == u'#': - while self.peek() not in u'\0\r\n\x85\u2028\u2029': - self.forward() - ch = self.peek() - if ch not in u'\0\r\n\x85\u2028\u2029': - raise ScannerError("while scanning a block scalar", start_mark, - "expected a comment or a line break, but found %r" - % ch.encode('utf-8'), self.get_mark()) - self.scan_line_break() - - def scan_block_scalar_indentation(self): - # See the specification for details. - chunks = [] - max_indent = 0 - end_mark = self.get_mark() - while self.peek() in u' \r\n\x85\u2028\u2029': - if self.peek() != u' ': - chunks.append(self.scan_line_break()) - end_mark = self.get_mark() - else: - self.forward() - if self.column > max_indent: - max_indent = self.column - return chunks, max_indent, end_mark - - def scan_block_scalar_breaks(self, indent): - # See the specification for details. - chunks = [] - end_mark = self.get_mark() - while self.column < indent and self.peek() == u' ': - self.forward() - while self.peek() in u'\r\n\x85\u2028\u2029': - chunks.append(self.scan_line_break()) - end_mark = self.get_mark() - while self.column < indent and self.peek() == u' ': - self.forward() - return chunks, end_mark - - def scan_flow_scalar(self, style): - # See the specification for details. - # Note that we loose indentation rules for quoted scalars. Quoted - # scalars don't need to adhere indentation because " and ' clearly - # mark the beginning and the end of them. Therefore we are less - # restrictive then the specification requires. We only need to check - # that document separators are not included in scalars. - if style == '"': - double = True - else: - double = False - chunks = [] - start_mark = self.get_mark() - quote = self.peek() - self.forward() - chunks.extend(self.scan_flow_scalar_non_spaces(double, start_mark)) - while self.peek() != quote: - chunks.extend(self.scan_flow_scalar_spaces(double, start_mark)) - chunks.extend(self.scan_flow_scalar_non_spaces(double, start_mark)) - self.forward() - end_mark = self.get_mark() - return ScalarToken(u''.join(chunks), False, start_mark, end_mark, - style) - - ESCAPE_REPLACEMENTS = { - u'0': u'\0', - u'a': u'\x07', - u'b': u'\x08', - u't': u'\x09', - u'\t': u'\x09', - u'n': u'\x0A', - u'v': u'\x0B', - u'f': u'\x0C', - u'r': u'\x0D', - u'e': u'\x1B', - u' ': u'\x20', - u'\"': u'\"', - u'\\': u'\\', - u'N': u'\x85', - u'_': u'\xA0', - u'L': u'\u2028', - u'P': u'\u2029', - } - - ESCAPE_CODES = { - u'x': 2, - u'u': 4, - u'U': 8, - } - - def scan_flow_scalar_non_spaces(self, double, start_mark): - # See the specification for details. - chunks = [] - while True: - length = 0 - while self.peek(length) not in u'\'\"\\\0 \t\r\n\x85\u2028\u2029': - length += 1 - if length: - chunks.append(self.prefix(length)) - self.forward(length) - ch = self.peek() - if not double and ch == u'\'' and self.peek(1) == u'\'': - chunks.append(u'\'') - self.forward(2) - elif (double and ch == u'\'') or (not double and ch in u'\"\\'): - chunks.append(ch) - self.forward() - elif double and ch == u'\\': - self.forward() - ch = self.peek() - if ch in self.ESCAPE_REPLACEMENTS: - chunks.append(self.ESCAPE_REPLACEMENTS[ch]) - self.forward() - elif ch in self.ESCAPE_CODES: - length = self.ESCAPE_CODES[ch] - self.forward() - for k in range(length): - if self.peek(k) not in u'0123456789ABCDEFabcdef': - raise ScannerError("while scanning a double-quoted scalar", start_mark, - "expected escape sequence of %d hexdecimal numbers, but found %r" % - (length, self.peek(k).encode('utf-8')), self.get_mark()) - code = int(self.prefix(length), 16) - chunks.append(unichr(code)) - self.forward(length) - elif ch in u'\r\n\x85\u2028\u2029': - self.scan_line_break() - chunks.extend(self.scan_flow_scalar_breaks(double, start_mark)) - else: - raise ScannerError("while scanning a double-quoted scalar", start_mark, - "found unknown escape character %r" % ch.encode('utf-8'), self.get_mark()) - else: - return chunks - - def scan_flow_scalar_spaces(self, double, start_mark): - # See the specification for details. - chunks = [] - length = 0 - while self.peek(length) in u' \t': - length += 1 - whitespaces = self.prefix(length) - self.forward(length) - ch = self.peek() - if ch == u'\0': - raise ScannerError("while scanning a quoted scalar", start_mark, - "found unexpected end of stream", self.get_mark()) - elif ch in u'\r\n\x85\u2028\u2029': - line_break = self.scan_line_break() - breaks = self.scan_flow_scalar_breaks(double, start_mark) - if line_break != u'\n': - chunks.append(line_break) - elif not breaks: - chunks.append(u' ') - chunks.extend(breaks) - else: - chunks.append(whitespaces) - return chunks - - def scan_flow_scalar_breaks(self, double, start_mark): - # See the specification for details. - chunks = [] - while True: - # Instead of checking indentation, we check for document - # separators. - prefix = self.prefix(3) - if (prefix == u'---' or prefix == u'...') \ - and self.peek(3) in u'\0 \t\r\n\x85\u2028\u2029': - raise ScannerError("while scanning a quoted scalar", start_mark, - "found unexpected document separator", self.get_mark()) - while self.peek() in u' \t': - self.forward() - if self.peek() in u'\r\n\x85\u2028\u2029': - chunks.append(self.scan_line_break()) - else: - return chunks - - def scan_plain(self): - # See the specification for details. - # We add an additional restriction for the flow context: - # plain scalars in the flow context cannot contain ',', ':' and '?'. - # We also keep track of the `allow_simple_key` flag here. - # Indentation rules are loosed for the flow context. - chunks = [] - start_mark = self.get_mark() - end_mark = start_mark - indent = self.indent+1 - # We allow zero indentation for scalars, but then we need to check for - # document separators at the beginning of the line. - #if indent == 0: - # indent = 1 - spaces = [] - while True: - length = 0 - if self.peek() == u'#': - break - while True: - ch = self.peek(length) - if ch in u'\0 \t\r\n\x85\u2028\u2029' \ - or (not self.flow_level and ch == u':' and - self.peek(length+1) in u'\0 \t\r\n\x85\u2028\u2029') \ - or (self.flow_level and ch in u',:?[]{}'): - break - length += 1 - # It's not clear what we should do with ':' in the flow context. - if (self.flow_level and ch == u':' - and self.peek(length+1) not in u'\0 \t\r\n\x85\u2028\u2029,[]{}'): - self.forward(length) - raise ScannerError("while scanning a plain scalar", start_mark, - "found unexpected ':'", self.get_mark(), - "Please check http://pyyaml.org/wiki/YAMLColonInFlowContext for details.") - if length == 0: - break - self.allow_simple_key = False - chunks.extend(spaces) - chunks.append(self.prefix(length)) - self.forward(length) - end_mark = self.get_mark() - spaces = self.scan_plain_spaces(indent, start_mark) - if not spaces or self.peek() == u'#' \ - or (not self.flow_level and self.column < indent): - break - return ScalarToken(u''.join(chunks), True, start_mark, end_mark) - - def scan_plain_spaces(self, indent, start_mark): - # See the specification for details. - # The specification is really confusing about tabs in plain scalars. - # We just forbid them completely. Do not use tabs in YAML! - chunks = [] - length = 0 - while self.peek(length) in u' ': - length += 1 - whitespaces = self.prefix(length) - self.forward(length) - ch = self.peek() - if ch in u'\r\n\x85\u2028\u2029': - line_break = self.scan_line_break() - self.allow_simple_key = True - prefix = self.prefix(3) - if (prefix == u'---' or prefix == u'...') \ - and self.peek(3) in u'\0 \t\r\n\x85\u2028\u2029': - return - breaks = [] - while self.peek() in u' \r\n\x85\u2028\u2029': - if self.peek() == ' ': - self.forward() - else: - breaks.append(self.scan_line_break()) - prefix = self.prefix(3) - if (prefix == u'---' or prefix == u'...') \ - and self.peek(3) in u'\0 \t\r\n\x85\u2028\u2029': - return - if line_break != u'\n': - chunks.append(line_break) - elif not breaks: - chunks.append(u' ') - chunks.extend(breaks) - elif whitespaces: - chunks.append(whitespaces) - return chunks - - def scan_tag_handle(self, name, start_mark): - # See the specification for details. - # For some strange reasons, the specification does not allow '_' in - # tag handles. I have allowed it anyway. - ch = self.peek() - if ch != u'!': - raise ScannerError("while scanning a %s" % name, start_mark, - "expected '!', but found %r" % ch.encode('utf-8'), - self.get_mark()) - length = 1 - ch = self.peek(length) - if ch != u' ': - while u'0' <= ch <= u'9' or u'A' <= ch <= u'Z' or u'a' <= ch <= u'z' \ - or ch in u'-_': - length += 1 - ch = self.peek(length) - if ch != u'!': - self.forward(length) - raise ScannerError("while scanning a %s" % name, start_mark, - "expected '!', but found %r" % ch.encode('utf-8'), - self.get_mark()) - length += 1 - value = self.prefix(length) - self.forward(length) - return value - - def scan_tag_uri(self, name, start_mark): - # See the specification for details. - # Note: we do not check if URI is well-formed. - chunks = [] - length = 0 - ch = self.peek(length) - while u'0' <= ch <= u'9' or u'A' <= ch <= u'Z' or u'a' <= ch <= u'z' \ - or ch in u'-;/?:@&=+$,_.!~*\'()[]%': - if ch == u'%': - chunks.append(self.prefix(length)) - self.forward(length) - length = 0 - chunks.append(self.scan_uri_escapes(name, start_mark)) - else: - length += 1 - ch = self.peek(length) - if length: - chunks.append(self.prefix(length)) - self.forward(length) - length = 0 - if not chunks: - raise ScannerError("while parsing a %s" % name, start_mark, - "expected URI, but found %r" % ch.encode('utf-8'), - self.get_mark()) - return u''.join(chunks) - - def scan_uri_escapes(self, name, start_mark): - # See the specification for details. - bytes = [] - mark = self.get_mark() - while self.peek() == u'%': - self.forward() - for k in range(2): - if self.peek(k) not in u'0123456789ABCDEFabcdef': - raise ScannerError("while scanning a %s" % name, start_mark, - "expected URI escape sequence of 2 hexdecimal numbers, but found %r" % - (self.peek(k).encode('utf-8')), self.get_mark()) - bytes.append(chr(int(self.prefix(2), 16))) - self.forward(2) - try: - value = unicode(''.join(bytes), 'utf-8') - except UnicodeDecodeError, exc: - raise ScannerError("while scanning a %s" % name, start_mark, str(exc), mark) - return value - - def scan_line_break(self): - # Transforms: - # '\r\n' : '\n' - # '\r' : '\n' - # '\n' : '\n' - # '\x85' : '\n' - # '\u2028' : '\u2028' - # '\u2029 : '\u2029' - # default : '' - ch = self.peek() - if ch in u'\r\n\x85': - if self.prefix(2) == u'\r\n': - self.forward(2) - else: - self.forward() - return u'\n' - elif ch in u'\u2028\u2029': - self.forward() - return ch - return u'' - -#try: -# import psyco -# psyco.bind(Scanner) -#except ImportError: -# pass - diff --git a/python.d/python_modules/pyyaml2/serializer.py b/python.d/python_modules/pyyaml2/serializer.py deleted file mode 100644 index 0bf1e96dc..000000000 --- a/python.d/python_modules/pyyaml2/serializer.py +++ /dev/null @@ -1,111 +0,0 @@ - -__all__ = ['Serializer', 'SerializerError'] - -from error import YAMLError -from events import * -from nodes import * - -class SerializerError(YAMLError): - pass - -class Serializer(object): - - ANCHOR_TEMPLATE = u'id%03d' - - def __init__(self, encoding=None, - explicit_start=None, explicit_end=None, version=None, tags=None): - self.use_encoding = encoding - self.use_explicit_start = explicit_start - self.use_explicit_end = explicit_end - self.use_version = version - self.use_tags = tags - self.serialized_nodes = {} - self.anchors = {} - self.last_anchor_id = 0 - self.closed = None - - def open(self): - if self.closed is None: - self.emit(StreamStartEvent(encoding=self.use_encoding)) - self.closed = False - elif self.closed: - raise SerializerError("serializer is closed") - else: - raise SerializerError("serializer is already opened") - - def close(self): - if self.closed is None: - raise SerializerError("serializer is not opened") - elif not self.closed: - self.emit(StreamEndEvent()) - self.closed = True - - #def __del__(self): - # self.close() - - def serialize(self, node): - if self.closed is None: - raise SerializerError("serializer is not opened") - elif self.closed: - raise SerializerError("serializer is closed") - self.emit(DocumentStartEvent(explicit=self.use_explicit_start, - version=self.use_version, tags=self.use_tags)) - self.anchor_node(node) - self.serialize_node(node, None, None) - self.emit(DocumentEndEvent(explicit=self.use_explicit_end)) - self.serialized_nodes = {} - self.anchors = {} - self.last_anchor_id = 0 - - def anchor_node(self, node): - if node in self.anchors: - if self.anchors[node] is None: - self.anchors[node] = self.generate_anchor(node) - else: - self.anchors[node] = None - if isinstance(node, SequenceNode): - for item in node.value: - self.anchor_node(item) - elif isinstance(node, MappingNode): - for key, value in node.value: - self.anchor_node(key) - self.anchor_node(value) - - def generate_anchor(self, node): - self.last_anchor_id += 1 - return self.ANCHOR_TEMPLATE % self.last_anchor_id - - def serialize_node(self, node, parent, index): - alias = self.anchors[node] - if node in self.serialized_nodes: - self.emit(AliasEvent(alias)) - else: - self.serialized_nodes[node] = True - self.descend_resolver(parent, index) - if isinstance(node, ScalarNode): - detected_tag = self.resolve(ScalarNode, node.value, (True, False)) - default_tag = self.resolve(ScalarNode, node.value, (False, True)) - implicit = (node.tag == detected_tag), (node.tag == default_tag) - self.emit(ScalarEvent(alias, node.tag, implicit, node.value, - style=node.style)) - elif isinstance(node, SequenceNode): - implicit = (node.tag - == self.resolve(SequenceNode, node.value, True)) - self.emit(SequenceStartEvent(alias, node.tag, implicit, - flow_style=node.flow_style)) - index = 0 - for item in node.value: - self.serialize_node(item, node, index) - index += 1 - self.emit(SequenceEndEvent()) - elif isinstance(node, MappingNode): - implicit = (node.tag - == self.resolve(MappingNode, node.value, True)) - self.emit(MappingStartEvent(alias, node.tag, implicit, - flow_style=node.flow_style)) - for key, value in node.value: - self.serialize_node(key, node, None) - self.serialize_node(value, node, key) - self.emit(MappingEndEvent()) - self.ascend_resolver() - diff --git a/python.d/python_modules/pyyaml2/tokens.py b/python.d/python_modules/pyyaml2/tokens.py deleted file mode 100644 index 4d0b48a39..000000000 --- a/python.d/python_modules/pyyaml2/tokens.py +++ /dev/null @@ -1,104 +0,0 @@ - -class Token(object): - def __init__(self, start_mark, end_mark): - self.start_mark = start_mark - self.end_mark = end_mark - def __repr__(self): - attributes = [key for key in self.__dict__ - if not key.endswith('_mark')] - attributes.sort() - arguments = ', '.join(['%s=%r' % (key, getattr(self, key)) - for key in attributes]) - return '%s(%s)' % (self.__class__.__name__, arguments) - -#class BOMToken(Token): -# id = '<byte order mark>' - -class DirectiveToken(Token): - id = '<directive>' - def __init__(self, name, value, start_mark, end_mark): - self.name = name - self.value = value - self.start_mark = start_mark - self.end_mark = end_mark - -class DocumentStartToken(Token): - id = '<document start>' - -class DocumentEndToken(Token): - id = '<document end>' - -class StreamStartToken(Token): - id = '<stream start>' - def __init__(self, start_mark=None, end_mark=None, - encoding=None): - self.start_mark = start_mark - self.end_mark = end_mark - self.encoding = encoding - -class StreamEndToken(Token): - id = '<stream end>' - -class BlockSequenceStartToken(Token): - id = '<block sequence start>' - -class BlockMappingStartToken(Token): - id = '<block mapping start>' - -class BlockEndToken(Token): - id = '<block end>' - -class FlowSequenceStartToken(Token): - id = '[' - -class FlowMappingStartToken(Token): - id = '{' - -class FlowSequenceEndToken(Token): - id = ']' - -class FlowMappingEndToken(Token): - id = '}' - -class KeyToken(Token): - id = '?' - -class ValueToken(Token): - id = ':' - -class BlockEntryToken(Token): - id = '-' - -class FlowEntryToken(Token): - id = ',' - -class AliasToken(Token): - id = '<alias>' - def __init__(self, value, start_mark, end_mark): - self.value = value - self.start_mark = start_mark - self.end_mark = end_mark - -class AnchorToken(Token): - id = '<anchor>' - def __init__(self, value, start_mark, end_mark): - self.value = value - self.start_mark = start_mark - self.end_mark = end_mark - -class TagToken(Token): - id = '<tag>' - def __init__(self, value, start_mark, end_mark): - self.value = value - self.start_mark = start_mark - self.end_mark = end_mark - -class ScalarToken(Token): - id = '<scalar>' - def __init__(self, value, plain, start_mark, end_mark, style=None): - self.value = value - self.plain = plain - self.start_mark = start_mark - self.end_mark = end_mark - self.style = style - diff --git a/python.d/python_modules/pyyaml3/__init__.py b/python.d/python_modules/pyyaml3/__init__.py deleted file mode 100644 index a5e20f94d..000000000 --- a/python.d/python_modules/pyyaml3/__init__.py +++ /dev/null @@ -1,312 +0,0 @@ - -from .error import * - -from .tokens import * -from .events import * -from .nodes import * - -from .loader import * -from .dumper import * - -__version__ = '3.11' -try: - from .cyaml import * - __with_libyaml__ = True -except ImportError: - __with_libyaml__ = False - -import io - -def scan(stream, Loader=Loader): - """ - Scan a YAML stream and produce scanning tokens. - """ - loader = Loader(stream) - try: - while loader.check_token(): - yield loader.get_token() - finally: - loader.dispose() - -def parse(stream, Loader=Loader): - """ - Parse a YAML stream and produce parsing events. - """ - loader = Loader(stream) - try: - while loader.check_event(): - yield loader.get_event() - finally: - loader.dispose() - -def compose(stream, Loader=Loader): - """ - Parse the first YAML document in a stream - and produce the corresponding representation tree. - """ - loader = Loader(stream) - try: - return loader.get_single_node() - finally: - loader.dispose() - -def compose_all(stream, Loader=Loader): - """ - Parse all YAML documents in a stream - and produce corresponding representation trees. - """ - loader = Loader(stream) - try: - while loader.check_node(): - yield loader.get_node() - finally: - loader.dispose() - -def load(stream, Loader=Loader): - """ - Parse the first YAML document in a stream - and produce the corresponding Python object. - """ - loader = Loader(stream) - try: - return loader.get_single_data() - finally: - loader.dispose() - -def load_all(stream, Loader=Loader): - """ - Parse all YAML documents in a stream - and produce corresponding Python objects. - """ - loader = Loader(stream) - try: - while loader.check_data(): - yield loader.get_data() - finally: - loader.dispose() - -def safe_load(stream): - """ - Parse the first YAML document in a stream - and produce the corresponding Python object. - Resolve only basic YAML tags. - """ - return load(stream, SafeLoader) - -def safe_load_all(stream): - """ - Parse all YAML documents in a stream - and produce corresponding Python objects. - Resolve only basic YAML tags. - """ - return load_all(stream, SafeLoader) - -def emit(events, stream=None, Dumper=Dumper, - canonical=None, indent=None, width=None, - allow_unicode=None, line_break=None): - """ - Emit YAML parsing events into a stream. - If stream is None, return the produced string instead. - """ - getvalue = None - if stream is None: - stream = io.StringIO() - getvalue = stream.getvalue - dumper = Dumper(stream, canonical=canonical, indent=indent, width=width, - allow_unicode=allow_unicode, line_break=line_break) - try: - for event in events: - dumper.emit(event) - finally: - dumper.dispose() - if getvalue: - return getvalue() - -def serialize_all(nodes, stream=None, Dumper=Dumper, - canonical=None, indent=None, width=None, - allow_unicode=None, line_break=None, - encoding=None, explicit_start=None, explicit_end=None, - version=None, tags=None): - """ - Serialize a sequence of representation trees into a YAML stream. - If stream is None, return the produced string instead. - """ - getvalue = None - if stream is None: - if encoding is None: - stream = io.StringIO() - else: - stream = io.BytesIO() - getvalue = stream.getvalue - dumper = Dumper(stream, canonical=canonical, indent=indent, width=width, - allow_unicode=allow_unicode, line_break=line_break, - encoding=encoding, version=version, tags=tags, - explicit_start=explicit_start, explicit_end=explicit_end) - try: - dumper.open() - for node in nodes: - dumper.serialize(node) - dumper.close() - finally: - dumper.dispose() - if getvalue: - return getvalue() - -def serialize(node, stream=None, Dumper=Dumper, **kwds): - """ - Serialize a representation tree into a YAML stream. - If stream is None, return the produced string instead. - """ - return serialize_all([node], stream, Dumper=Dumper, **kwds) - -def dump_all(documents, stream=None, Dumper=Dumper, - default_style=None, default_flow_style=None, - canonical=None, indent=None, width=None, - allow_unicode=None, line_break=None, - encoding=None, explicit_start=None, explicit_end=None, - version=None, tags=None): - """ - Serialize a sequence of Python objects into a YAML stream. - If stream is None, return the produced string instead. - """ - getvalue = None - if stream is None: - if encoding is None: - stream = io.StringIO() - else: - stream = io.BytesIO() - getvalue = stream.getvalue - dumper = Dumper(stream, default_style=default_style, - default_flow_style=default_flow_style, - canonical=canonical, indent=indent, width=width, - allow_unicode=allow_unicode, line_break=line_break, - encoding=encoding, version=version, tags=tags, - explicit_start=explicit_start, explicit_end=explicit_end) - try: - dumper.open() - for data in documents: - dumper.represent(data) - dumper.close() - finally: - dumper.dispose() - if getvalue: - return getvalue() - -def dump(data, stream=None, Dumper=Dumper, **kwds): - """ - Serialize a Python object into a YAML stream. - If stream is None, return the produced string instead. - """ - return dump_all([data], stream, Dumper=Dumper, **kwds) - -def safe_dump_all(documents, stream=None, **kwds): - """ - Serialize a sequence of Python objects into a YAML stream. - Produce only basic YAML tags. - If stream is None, return the produced string instead. - """ - return dump_all(documents, stream, Dumper=SafeDumper, **kwds) - -def safe_dump(data, stream=None, **kwds): - """ - Serialize a Python object into a YAML stream. - Produce only basic YAML tags. - If stream is None, return the produced string instead. - """ - return dump_all([data], stream, Dumper=SafeDumper, **kwds) - -def add_implicit_resolver(tag, regexp, first=None, - Loader=Loader, Dumper=Dumper): - """ - Add an implicit scalar detector. - If an implicit scalar value matches the given regexp, - the corresponding tag is assigned to the scalar. - first is a sequence of possible initial characters or None. - """ - Loader.add_implicit_resolver(tag, regexp, first) - Dumper.add_implicit_resolver(tag, regexp, first) - -def add_path_resolver(tag, path, kind=None, Loader=Loader, Dumper=Dumper): - """ - Add a path based resolver for the given tag. - A path is a list of keys that forms a path - to a node in the representation tree. - Keys can be string values, integers, or None. - """ - Loader.add_path_resolver(tag, path, kind) - Dumper.add_path_resolver(tag, path, kind) - -def add_constructor(tag, constructor, Loader=Loader): - """ - Add a constructor for the given tag. - Constructor is a function that accepts a Loader instance - and a node object and produces the corresponding Python object. - """ - Loader.add_constructor(tag, constructor) - -def add_multi_constructor(tag_prefix, multi_constructor, Loader=Loader): - """ - Add a multi-constructor for the given tag prefix. - Multi-constructor is called for a node if its tag starts with tag_prefix. - Multi-constructor accepts a Loader instance, a tag suffix, - and a node object and produces the corresponding Python object. - """ - Loader.add_multi_constructor(tag_prefix, multi_constructor) - -def add_representer(data_type, representer, Dumper=Dumper): - """ - Add a representer for the given type. - Representer is a function accepting a Dumper instance - and an instance of the given data type - and producing the corresponding representation node. - """ - Dumper.add_representer(data_type, representer) - -def add_multi_representer(data_type, multi_representer, Dumper=Dumper): - """ - Add a representer for the given type. - Multi-representer is a function accepting a Dumper instance - and an instance of the given data type or subtype - and producing the corresponding representation node. - """ - Dumper.add_multi_representer(data_type, multi_representer) - -class YAMLObjectMetaclass(type): - """ - The metaclass for YAMLObject. - """ - def __init__(cls, name, bases, kwds): - super(YAMLObjectMetaclass, cls).__init__(name, bases, kwds) - if 'yaml_tag' in kwds and kwds['yaml_tag'] is not None: - cls.yaml_loader.add_constructor(cls.yaml_tag, cls.from_yaml) - cls.yaml_dumper.add_representer(cls, cls.to_yaml) - -class YAMLObject(metaclass=YAMLObjectMetaclass): - """ - An object that can dump itself to a YAML stream - and load itself from a YAML stream. - """ - - __slots__ = () # no direct instantiation, so allow immutable subclasses - - yaml_loader = Loader - yaml_dumper = Dumper - - yaml_tag = None - yaml_flow_style = None - - @classmethod - def from_yaml(cls, loader, node): - """ - Convert a representation node to a Python object. - """ - return loader.construct_yaml_object(node, cls) - - @classmethod - def to_yaml(cls, dumper, data): - """ - Convert a Python object to a representation node. - """ - return dumper.represent_yaml_object(cls.yaml_tag, data, cls, - flow_style=cls.yaml_flow_style) - diff --git a/python.d/python_modules/pyyaml3/composer.py b/python.d/python_modules/pyyaml3/composer.py deleted file mode 100644 index d5c6a7acd..000000000 --- a/python.d/python_modules/pyyaml3/composer.py +++ /dev/null @@ -1,139 +0,0 @@ - -__all__ = ['Composer', 'ComposerError'] - -from .error import MarkedYAMLError -from .events import * -from .nodes import * - -class ComposerError(MarkedYAMLError): - pass - -class Composer: - - def __init__(self): - self.anchors = {} - - def check_node(self): - # Drop the STREAM-START event. - if self.check_event(StreamStartEvent): - self.get_event() - - # If there are more documents available? - return not self.check_event(StreamEndEvent) - - def get_node(self): - # Get the root node of the next document. - if not self.check_event(StreamEndEvent): - return self.compose_document() - - def get_single_node(self): - # Drop the STREAM-START event. - self.get_event() - - # Compose a document if the stream is not empty. - document = None - if not self.check_event(StreamEndEvent): - document = self.compose_document() - - # Ensure that the stream contains no more documents. - if not self.check_event(StreamEndEvent): - event = self.get_event() - raise ComposerError("expected a single document in the stream", - document.start_mark, "but found another document", - event.start_mark) - - # Drop the STREAM-END event. - self.get_event() - - return document - - def compose_document(self): - # Drop the DOCUMENT-START event. - self.get_event() - - # Compose the root node. - node = self.compose_node(None, None) - - # Drop the DOCUMENT-END event. - self.get_event() - - self.anchors = {} - return node - - def compose_node(self, parent, index): - if self.check_event(AliasEvent): - event = self.get_event() - anchor = event.anchor - if anchor not in self.anchors: - raise ComposerError(None, None, "found undefined alias %r" - % anchor, event.start_mark) - return self.anchors[anchor] - event = self.peek_event() - anchor = event.anchor - if anchor is not None: - if anchor in self.anchors: - raise ComposerError("found duplicate anchor %r; first occurence" - % anchor, self.anchors[anchor].start_mark, - "second occurence", event.start_mark) - self.descend_resolver(parent, index) - if self.check_event(ScalarEvent): - node = self.compose_scalar_node(anchor) - elif self.check_event(SequenceStartEvent): - node = self.compose_sequence_node(anchor) - elif self.check_event(MappingStartEvent): - node = self.compose_mapping_node(anchor) - self.ascend_resolver() - return node - - def compose_scalar_node(self, anchor): - event = self.get_event() - tag = event.tag - if tag is None or tag == '!': - tag = self.resolve(ScalarNode, event.value, event.implicit) - node = ScalarNode(tag, event.value, - event.start_mark, event.end_mark, style=event.style) - if anchor is not None: - self.anchors[anchor] = node - return node - - def compose_sequence_node(self, anchor): - start_event = self.get_event() - tag = start_event.tag - if tag is None or tag == '!': - tag = self.resolve(SequenceNode, None, start_event.implicit) - node = SequenceNode(tag, [], - start_event.start_mark, None, - flow_style=start_event.flow_style) - if anchor is not None: - self.anchors[anchor] = node - index = 0 - while not self.check_event(SequenceEndEvent): - node.value.append(self.compose_node(node, index)) - index += 1 - end_event = self.get_event() - node.end_mark = end_event.end_mark - return node - - def compose_mapping_node(self, anchor): - start_event = self.get_event() - tag = start_event.tag - if tag is None or tag == '!': - tag = self.resolve(MappingNode, None, start_event.implicit) - node = MappingNode(tag, [], - start_event.start_mark, None, - flow_style=start_event.flow_style) - if anchor is not None: - self.anchors[anchor] = node - while not self.check_event(MappingEndEvent): - #key_event = self.peek_event() - item_key = self.compose_node(node, None) - #if item_key in node.value: - # raise ComposerError("while composing a mapping", start_event.start_mark, - # "found duplicate key", key_event.start_mark) - item_value = self.compose_node(node, item_key) - #node.value[item_key] = item_value - node.value.append((item_key, item_value)) - end_event = self.get_event() - node.end_mark = end_event.end_mark - return node - diff --git a/python.d/python_modules/pyyaml3/constructor.py b/python.d/python_modules/pyyaml3/constructor.py deleted file mode 100644 index 981543aeb..000000000 --- a/python.d/python_modules/pyyaml3/constructor.py +++ /dev/null @@ -1,686 +0,0 @@ - -__all__ = ['BaseConstructor', 'SafeConstructor', 'Constructor', - 'ConstructorError'] - -from .error import * -from .nodes import * - -import collections, datetime, base64, binascii, re, sys, types - -class ConstructorError(MarkedYAMLError): - pass - -class BaseConstructor: - - yaml_constructors = {} - yaml_multi_constructors = {} - - def __init__(self): - self.constructed_objects = {} - self.recursive_objects = {} - self.state_generators = [] - self.deep_construct = False - - def check_data(self): - # If there are more documents available? - return self.check_node() - - def get_data(self): - # Construct and return the next document. - if self.check_node(): - return self.construct_document(self.get_node()) - - def get_single_data(self): - # Ensure that the stream contains a single document and construct it. - node = self.get_single_node() - if node is not None: - return self.construct_document(node) - return None - - def construct_document(self, node): - data = self.construct_object(node) - while self.state_generators: - state_generators = self.state_generators - self.state_generators = [] - for generator in state_generators: - for dummy in generator: - pass - self.constructed_objects = {} - self.recursive_objects = {} - self.deep_construct = False - return data - - def construct_object(self, node, deep=False): - if node in self.constructed_objects: - return self.constructed_objects[node] - if deep: - old_deep = self.deep_construct - self.deep_construct = True - if node in self.recursive_objects: - raise ConstructorError(None, None, - "found unconstructable recursive node", node.start_mark) - self.recursive_objects[node] = None - constructor = None - tag_suffix = None - if node.tag in self.yaml_constructors: - constructor = self.yaml_constructors[node.tag] - else: - for tag_prefix in self.yaml_multi_constructors: - if node.tag.startswith(tag_prefix): - tag_suffix = node.tag[len(tag_prefix):] - constructor = self.yaml_multi_constructors[tag_prefix] - break - else: - if None in self.yaml_multi_constructors: - tag_suffix = node.tag - constructor = self.yaml_multi_constructors[None] - elif None in self.yaml_constructors: - constructor = self.yaml_constructors[None] - elif isinstance(node, ScalarNode): - constructor = self.__class__.construct_scalar - elif isinstance(node, SequenceNode): - constructor = self.__class__.construct_sequence - elif isinstance(node, MappingNode): - constructor = self.__class__.construct_mapping - if tag_suffix is None: - data = constructor(self, node) - else: - data = constructor(self, tag_suffix, node) - if isinstance(data, types.GeneratorType): - generator = data - data = next(generator) - if self.deep_construct: - for dummy in generator: - pass - else: - self.state_generators.append(generator) - self.constructed_objects[node] = data - del self.recursive_objects[node] - if deep: - self.deep_construct = old_deep - return data - - def construct_scalar(self, node): - if not isinstance(node, ScalarNode): - raise ConstructorError(None, None, - "expected a scalar node, but found %s" % node.id, - node.start_mark) - return node.value - - def construct_sequence(self, node, deep=False): - if not isinstance(node, SequenceNode): - raise ConstructorError(None, None, - "expected a sequence node, but found %s" % node.id, - node.start_mark) - return [self.construct_object(child, deep=deep) - for child in node.value] - - def construct_mapping(self, node, deep=False): - if not isinstance(node, MappingNode): - raise ConstructorError(None, None, - "expected a mapping node, but found %s" % node.id, - node.start_mark) - mapping = {} - for key_node, value_node in node.value: - key = self.construct_object(key_node, deep=deep) - if not isinstance(key, collections.Hashable): - raise ConstructorError("while constructing a mapping", node.start_mark, - "found unhashable key", key_node.start_mark) - value = self.construct_object(value_node, deep=deep) - mapping[key] = value - return mapping - - def construct_pairs(self, node, deep=False): - if not isinstance(node, MappingNode): - raise ConstructorError(None, None, - "expected a mapping node, but found %s" % node.id, - node.start_mark) - pairs = [] - for key_node, value_node in node.value: - key = self.construct_object(key_node, deep=deep) - value = self.construct_object(value_node, deep=deep) - pairs.append((key, value)) - return pairs - - @classmethod - def add_constructor(cls, tag, constructor): - if not 'yaml_constructors' in cls.__dict__: - cls.yaml_constructors = cls.yaml_constructors.copy() - cls.yaml_constructors[tag] = constructor - - @classmethod - def add_multi_constructor(cls, tag_prefix, multi_constructor): - if not 'yaml_multi_constructors' in cls.__dict__: - cls.yaml_multi_constructors = cls.yaml_multi_constructors.copy() - cls.yaml_multi_constructors[tag_prefix] = multi_constructor - -class SafeConstructor(BaseConstructor): - - def construct_scalar(self, node): - if isinstance(node, MappingNode): - for key_node, value_node in node.value: - if key_node.tag == 'tag:yaml.org,2002:value': - return self.construct_scalar(value_node) - return super().construct_scalar(node) - - def flatten_mapping(self, node): - merge = [] - index = 0 - while index < len(node.value): - key_node, value_node = node.value[index] - if key_node.tag == 'tag:yaml.org,2002:merge': - del node.value[index] - if isinstance(value_node, MappingNode): - self.flatten_mapping(value_node) - merge.extend(value_node.value) - elif isinstance(value_node, SequenceNode): - submerge = [] - for subnode in value_node.value: - if not isinstance(subnode, MappingNode): - raise ConstructorError("while constructing a mapping", - node.start_mark, - "expected a mapping for merging, but found %s" - % subnode.id, subnode.start_mark) - self.flatten_mapping(subnode) - submerge.append(subnode.value) - submerge.reverse() - for value in submerge: - merge.extend(value) - else: - raise ConstructorError("while constructing a mapping", node.start_mark, - "expected a mapping or list of mappings for merging, but found %s" - % value_node.id, value_node.start_mark) - elif key_node.tag == 'tag:yaml.org,2002:value': - key_node.tag = 'tag:yaml.org,2002:str' - index += 1 - else: - index += 1 - if merge: - node.value = merge + node.value - - def construct_mapping(self, node, deep=False): - if isinstance(node, MappingNode): - self.flatten_mapping(node) - return super().construct_mapping(node, deep=deep) - - def construct_yaml_null(self, node): - self.construct_scalar(node) - return None - - bool_values = { - 'yes': True, - 'no': False, - 'true': True, - 'false': False, - 'on': True, - 'off': False, - } - - def construct_yaml_bool(self, node): - value = self.construct_scalar(node) - return self.bool_values[value.lower()] - - def construct_yaml_int(self, node): - value = self.construct_scalar(node) - value = value.replace('_', '') - sign = +1 - if value[0] == '-': - sign = -1 - if value[0] in '+-': - value = value[1:] - if value == '0': - return 0 - elif value.startswith('0b'): - return sign*int(value[2:], 2) - elif value.startswith('0x'): - return sign*int(value[2:], 16) - elif value[0] == '0': - return sign*int(value, 8) - elif ':' in value: - digits = [int(part) for part in value.split(':')] - digits.reverse() - base = 1 - value = 0 - for digit in digits: - value += digit*base - base *= 60 - return sign*value - else: - return sign*int(value) - - inf_value = 1e300 - while inf_value != inf_value*inf_value: - inf_value *= inf_value - nan_value = -inf_value/inf_value # Trying to make a quiet NaN (like C99). - - def construct_yaml_float(self, node): - value = self.construct_scalar(node) - value = value.replace('_', '').lower() - sign = +1 - if value[0] == '-': - sign = -1 - if value[0] in '+-': - value = value[1:] - if value == '.inf': - return sign*self.inf_value - elif value == '.nan': - return self.nan_value - elif ':' in value: - digits = [float(part) for part in value.split(':')] - digits.reverse() - base = 1 - value = 0.0 - for digit in digits: - value += digit*base - base *= 60 - return sign*value - else: - return sign*float(value) - - def construct_yaml_binary(self, node): - try: - value = self.construct_scalar(node).encode('ascii') - except UnicodeEncodeError as exc: - raise ConstructorError(None, None, - "failed to convert base64 data into ascii: %s" % exc, - node.start_mark) - try: - if hasattr(base64, 'decodebytes'): - return base64.decodebytes(value) - else: - return base64.decodestring(value) - except binascii.Error as exc: - raise ConstructorError(None, None, - "failed to decode base64 data: %s" % exc, node.start_mark) - - timestamp_regexp = re.compile( - r'''^(?P<year>[0-9][0-9][0-9][0-9]) - -(?P<month>[0-9][0-9]?) - -(?P<day>[0-9][0-9]?) - (?:(?:[Tt]|[ \t]+) - (?P<hour>[0-9][0-9]?) - :(?P<minute>[0-9][0-9]) - :(?P<second>[0-9][0-9]) - (?:\.(?P<fraction>[0-9]*))? - (?:[ \t]*(?P<tz>Z|(?P<tz_sign>[-+])(?P<tz_hour>[0-9][0-9]?) - (?::(?P<tz_minute>[0-9][0-9]))?))?)?$''', re.X) - - def construct_yaml_timestamp(self, node): - value = self.construct_scalar(node) - match = self.timestamp_regexp.match(node.value) - values = match.groupdict() - year = int(values['year']) - month = int(values['month']) - day = int(values['day']) - if not values['hour']: - return datetime.date(year, month, day) - hour = int(values['hour']) - minute = int(values['minute']) - second = int(values['second']) - fraction = 0 - if values['fraction']: - fraction = values['fraction'][:6] - while len(fraction) < 6: - fraction += '0' - fraction = int(fraction) - delta = None - if values['tz_sign']: - tz_hour = int(values['tz_hour']) - tz_minute = int(values['tz_minute'] or 0) - delta = datetime.timedelta(hours=tz_hour, minutes=tz_minute) - if values['tz_sign'] == '-': - delta = -delta - data = datetime.datetime(year, month, day, hour, minute, second, fraction) - if delta: - data -= delta - return data - - def construct_yaml_omap(self, node): - # Note: we do not check for duplicate keys, because it's too - # CPU-expensive. - omap = [] - yield omap - if not isinstance(node, SequenceNode): - raise ConstructorError("while constructing an ordered map", node.start_mark, - "expected a sequence, but found %s" % node.id, node.start_mark) - for subnode in node.value: - if not isinstance(subnode, MappingNode): - raise ConstructorError("while constructing an ordered map", node.start_mark, - "expected a mapping of length 1, but found %s" % subnode.id, - subnode.start_mark) - if len(subnode.value) != 1: - raise ConstructorError("while constructing an ordered map", node.start_mark, - "expected a single mapping item, but found %d items" % len(subnode.value), - subnode.start_mark) - key_node, value_node = subnode.value[0] - key = self.construct_object(key_node) - value = self.construct_object(value_node) - omap.append((key, value)) - - def construct_yaml_pairs(self, node): - # Note: the same code as `construct_yaml_omap`. - pairs = [] - yield pairs - if not isinstance(node, SequenceNode): - raise ConstructorError("while constructing pairs", node.start_mark, - "expected a sequence, but found %s" % node.id, node.start_mark) - for subnode in node.value: - if not isinstance(subnode, MappingNode): - raise ConstructorError("while constructing pairs", node.start_mark, - "expected a mapping of length 1, but found %s" % subnode.id, - subnode.start_mark) - if len(subnode.value) != 1: - raise ConstructorError("while constructing pairs", node.start_mark, - "expected a single mapping item, but found %d items" % len(subnode.value), - subnode.start_mark) - key_node, value_node = subnode.value[0] - key = self.construct_object(key_node) - value = self.construct_object(value_node) - pairs.append((key, value)) - - def construct_yaml_set(self, node): - data = set() - yield data - value = self.construct_mapping(node) - data.update(value) - - def construct_yaml_str(self, node): - return self.construct_scalar(node) - - def construct_yaml_seq(self, node): - data = [] - yield data - data.extend(self.construct_sequence(node)) - - def construct_yaml_map(self, node): - data = {} - yield data - value = self.construct_mapping(node) - data.update(value) - - def construct_yaml_object(self, node, cls): - data = cls.__new__(cls) - yield data - if hasattr(data, '__setstate__'): - state = self.construct_mapping(node, deep=True) - data.__setstate__(state) - else: - state = self.construct_mapping(node) - data.__dict__.update(state) - - def construct_undefined(self, node): - raise ConstructorError(None, None, - "could not determine a constructor for the tag %r" % node.tag, - node.start_mark) - -SafeConstructor.add_constructor( - 'tag:yaml.org,2002:null', - SafeConstructor.construct_yaml_null) - -SafeConstructor.add_constructor( - 'tag:yaml.org,2002:bool', - SafeConstructor.construct_yaml_bool) - -SafeConstructor.add_constructor( - 'tag:yaml.org,2002:int', - SafeConstructor.construct_yaml_int) - -SafeConstructor.add_constructor( - 'tag:yaml.org,2002:float', - SafeConstructor.construct_yaml_float) - -SafeConstructor.add_constructor( - 'tag:yaml.org,2002:binary', - SafeConstructor.construct_yaml_binary) - -SafeConstructor.add_constructor( - 'tag:yaml.org,2002:timestamp', - SafeConstructor.construct_yaml_timestamp) - -SafeConstructor.add_constructor( - 'tag:yaml.org,2002:omap', - SafeConstructor.construct_yaml_omap) - -SafeConstructor.add_constructor( - 'tag:yaml.org,2002:pairs', - SafeConstructor.construct_yaml_pairs) - -SafeConstructor.add_constructor( - 'tag:yaml.org,2002:set', - SafeConstructor.construct_yaml_set) - -SafeConstructor.add_constructor( - 'tag:yaml.org,2002:str', - SafeConstructor.construct_yaml_str) - -SafeConstructor.add_constructor( - 'tag:yaml.org,2002:seq', - SafeConstructor.construct_yaml_seq) - -SafeConstructor.add_constructor( - 'tag:yaml.org,2002:map', - SafeConstructor.construct_yaml_map) - -SafeConstructor.add_constructor(None, - SafeConstructor.construct_undefined) - -class Constructor(SafeConstructor): - - def construct_python_str(self, node): - return self.construct_scalar(node) - - def construct_python_unicode(self, node): - return self.construct_scalar(node) - - def construct_python_bytes(self, node): - try: - value = self.construct_scalar(node).encode('ascii') - except UnicodeEncodeError as exc: - raise ConstructorError(None, None, - "failed to convert base64 data into ascii: %s" % exc, - node.start_mark) - try: - if hasattr(base64, 'decodebytes'): - return base64.decodebytes(value) - else: - return base64.decodestring(value) - except binascii.Error as exc: - raise ConstructorError(None, None, - "failed to decode base64 data: %s" % exc, node.start_mark) - - def construct_python_long(self, node): - return self.construct_yaml_int(node) - - def construct_python_complex(self, node): - return complex(self.construct_scalar(node)) - - def construct_python_tuple(self, node): - return tuple(self.construct_sequence(node)) - - def find_python_module(self, name, mark): - if not name: - raise ConstructorError("while constructing a Python module", mark, - "expected non-empty name appended to the tag", mark) - try: - __import__(name) - except ImportError as exc: - raise ConstructorError("while constructing a Python module", mark, - "cannot find module %r (%s)" % (name, exc), mark) - return sys.modules[name] - - def find_python_name(self, name, mark): - if not name: - raise ConstructorError("while constructing a Python object", mark, - "expected non-empty name appended to the tag", mark) - if '.' in name: - module_name, object_name = name.rsplit('.', 1) - else: - module_name = 'builtins' - object_name = name - try: - __import__(module_name) - except ImportError as exc: - raise ConstructorError("while constructing a Python object", mark, - "cannot find module %r (%s)" % (module_name, exc), mark) - module = sys.modules[module_name] - if not hasattr(module, object_name): - raise ConstructorError("while constructing a Python object", mark, - "cannot find %r in the module %r" - % (object_name, module.__name__), mark) - return getattr(module, object_name) - - def construct_python_name(self, suffix, node): - value = self.construct_scalar(node) - if value: - raise ConstructorError("while constructing a Python name", node.start_mark, - "expected the empty value, but found %r" % value, node.start_mark) - return self.find_python_name(suffix, node.start_mark) - - def construct_python_module(self, suffix, node): - value = self.construct_scalar(node) - if value: - raise ConstructorError("while constructing a Python module", node.start_mark, - "expected the empty value, but found %r" % value, node.start_mark) - return self.find_python_module(suffix, node.start_mark) - - def make_python_instance(self, suffix, node, - args=None, kwds=None, newobj=False): - if not args: - args = [] - if not kwds: - kwds = {} - cls = self.find_python_name(suffix, node.start_mark) - if newobj and isinstance(cls, type): - return cls.__new__(cls, *args, **kwds) - else: - return cls(*args, **kwds) - - def set_python_instance_state(self, instance, state): - if hasattr(instance, '__setstate__'): - instance.__setstate__(state) - else: - slotstate = {} - if isinstance(state, tuple) and len(state) == 2: - state, slotstate = state - if hasattr(instance, '__dict__'): - instance.__dict__.update(state) - elif state: - slotstate.update(state) - for key, value in slotstate.items(): - setattr(object, key, value) - - def construct_python_object(self, suffix, node): - # Format: - # !!python/object:module.name { ... state ... } - instance = self.make_python_instance(suffix, node, newobj=True) - yield instance - deep = hasattr(instance, '__setstate__') - state = self.construct_mapping(node, deep=deep) - self.set_python_instance_state(instance, state) - - def construct_python_object_apply(self, suffix, node, newobj=False): - # Format: - # !!python/object/apply # (or !!python/object/new) - # args: [ ... arguments ... ] - # kwds: { ... keywords ... } - # state: ... state ... - # listitems: [ ... listitems ... ] - # dictitems: { ... dictitems ... } - # or short format: - # !!python/object/apply [ ... arguments ... ] - # The difference between !!python/object/apply and !!python/object/new - # is how an object is created, check make_python_instance for details. - if isinstance(node, SequenceNode): - args = self.construct_sequence(node, deep=True) - kwds = {} - state = {} - listitems = [] - dictitems = {} - else: - value = self.construct_mapping(node, deep=True) - args = value.get('args', []) - kwds = value.get('kwds', {}) - state = value.get('state', {}) - listitems = value.get('listitems', []) - dictitems = value.get('dictitems', {}) - instance = self.make_python_instance(suffix, node, args, kwds, newobj) - if state: - self.set_python_instance_state(instance, state) - if listitems: - instance.extend(listitems) - if dictitems: - for key in dictitems: - instance[key] = dictitems[key] - return instance - - def construct_python_object_new(self, suffix, node): - return self.construct_python_object_apply(suffix, node, newobj=True) - -Constructor.add_constructor( - 'tag:yaml.org,2002:python/none', - Constructor.construct_yaml_null) - -Constructor.add_constructor( - 'tag:yaml.org,2002:python/bool', - Constructor.construct_yaml_bool) - -Constructor.add_constructor( - 'tag:yaml.org,2002:python/str', - Constructor.construct_python_str) - -Constructor.add_constructor( - 'tag:yaml.org,2002:python/unicode', - Constructor.construct_python_unicode) - -Constructor.add_constructor( - 'tag:yaml.org,2002:python/bytes', - Constructor.construct_python_bytes) - -Constructor.add_constructor( - 'tag:yaml.org,2002:python/int', - Constructor.construct_yaml_int) - -Constructor.add_constructor( - 'tag:yaml.org,2002:python/long', - Constructor.construct_python_long) - -Constructor.add_constructor( - 'tag:yaml.org,2002:python/float', - Constructor.construct_yaml_float) - -Constructor.add_constructor( - 'tag:yaml.org,2002:python/complex', - Constructor.construct_python_complex) - -Constructor.add_constructor( - 'tag:yaml.org,2002:python/list', - Constructor.construct_yaml_seq) - -Constructor.add_constructor( - 'tag:yaml.org,2002:python/tuple', - Constructor.construct_python_tuple) - -Constructor.add_constructor( - 'tag:yaml.org,2002:python/dict', - Constructor.construct_yaml_map) - -Constructor.add_multi_constructor( - 'tag:yaml.org,2002:python/name:', - Constructor.construct_python_name) - -Constructor.add_multi_constructor( - 'tag:yaml.org,2002:python/module:', - Constructor.construct_python_module) - -Constructor.add_multi_constructor( - 'tag:yaml.org,2002:python/object:', - Constructor.construct_python_object) - -Constructor.add_multi_constructor( - 'tag:yaml.org,2002:python/object/apply:', - Constructor.construct_python_object_apply) - -Constructor.add_multi_constructor( - 'tag:yaml.org,2002:python/object/new:', - Constructor.construct_python_object_new) - diff --git a/python.d/python_modules/pyyaml3/cyaml.py b/python.d/python_modules/pyyaml3/cyaml.py deleted file mode 100644 index d5cb87e99..000000000 --- a/python.d/python_modules/pyyaml3/cyaml.py +++ /dev/null @@ -1,85 +0,0 @@ - -__all__ = ['CBaseLoader', 'CSafeLoader', 'CLoader', - 'CBaseDumper', 'CSafeDumper', 'CDumper'] - -from _yaml import CParser, CEmitter - -from .constructor import * - -from .serializer import * -from .representer import * - -from .resolver import * - -class CBaseLoader(CParser, BaseConstructor, BaseResolver): - - def __init__(self, stream): - CParser.__init__(self, stream) - BaseConstructor.__init__(self) - BaseResolver.__init__(self) - -class CSafeLoader(CParser, SafeConstructor, Resolver): - - def __init__(self, stream): - CParser.__init__(self, stream) - SafeConstructor.__init__(self) - Resolver.__init__(self) - -class CLoader(CParser, Constructor, Resolver): - - def __init__(self, stream): - CParser.__init__(self, stream) - Constructor.__init__(self) - Resolver.__init__(self) - -class CBaseDumper(CEmitter, BaseRepresenter, BaseResolver): - - def __init__(self, stream, - default_style=None, default_flow_style=None, - canonical=None, indent=None, width=None, - allow_unicode=None, line_break=None, - encoding=None, explicit_start=None, explicit_end=None, - version=None, tags=None): - CEmitter.__init__(self, stream, canonical=canonical, - indent=indent, width=width, encoding=encoding, - allow_unicode=allow_unicode, line_break=line_break, - explicit_start=explicit_start, explicit_end=explicit_end, - version=version, tags=tags) - Representer.__init__(self, default_style=default_style, - default_flow_style=default_flow_style) - Resolver.__init__(self) - -class CSafeDumper(CEmitter, SafeRepresenter, Resolver): - - def __init__(self, stream, - default_style=None, default_flow_style=None, - canonical=None, indent=None, width=None, - allow_unicode=None, line_break=None, - encoding=None, explicit_start=None, explicit_end=None, - version=None, tags=None): - CEmitter.__init__(self, stream, canonical=canonical, - indent=indent, width=width, encoding=encoding, - allow_unicode=allow_unicode, line_break=line_break, - explicit_start=explicit_start, explicit_end=explicit_end, - version=version, tags=tags) - SafeRepresenter.__init__(self, default_style=default_style, - default_flow_style=default_flow_style) - Resolver.__init__(self) - -class CDumper(CEmitter, Serializer, Representer, Resolver): - - def __init__(self, stream, - default_style=None, default_flow_style=None, - canonical=None, indent=None, width=None, - allow_unicode=None, line_break=None, - encoding=None, explicit_start=None, explicit_end=None, - version=None, tags=None): - CEmitter.__init__(self, stream, canonical=canonical, - indent=indent, width=width, encoding=encoding, - allow_unicode=allow_unicode, line_break=line_break, - explicit_start=explicit_start, explicit_end=explicit_end, - version=version, tags=tags) - Representer.__init__(self, default_style=default_style, - default_flow_style=default_flow_style) - Resolver.__init__(self) - diff --git a/python.d/python_modules/pyyaml3/dumper.py b/python.d/python_modules/pyyaml3/dumper.py deleted file mode 100644 index 0b6912877..000000000 --- a/python.d/python_modules/pyyaml3/dumper.py +++ /dev/null @@ -1,62 +0,0 @@ - -__all__ = ['BaseDumper', 'SafeDumper', 'Dumper'] - -from .emitter import * -from .serializer import * -from .representer import * -from .resolver import * - -class BaseDumper(Emitter, Serializer, BaseRepresenter, BaseResolver): - - def __init__(self, stream, - default_style=None, default_flow_style=None, - canonical=None, indent=None, width=None, - allow_unicode=None, line_break=None, - encoding=None, explicit_start=None, explicit_end=None, - version=None, tags=None): - Emitter.__init__(self, stream, canonical=canonical, - indent=indent, width=width, - allow_unicode=allow_unicode, line_break=line_break) - Serializer.__init__(self, encoding=encoding, - explicit_start=explicit_start, explicit_end=explicit_end, - version=version, tags=tags) - Representer.__init__(self, default_style=default_style, - default_flow_style=default_flow_style) - Resolver.__init__(self) - -class SafeDumper(Emitter, Serializer, SafeRepresenter, Resolver): - - def __init__(self, stream, - default_style=None, default_flow_style=None, - canonical=None, indent=None, width=None, - allow_unicode=None, line_break=None, - encoding=None, explicit_start=None, explicit_end=None, - version=None, tags=None): - Emitter.__init__(self, stream, canonical=canonical, - indent=indent, width=width, - allow_unicode=allow_unicode, line_break=line_break) - Serializer.__init__(self, encoding=encoding, - explicit_start=explicit_start, explicit_end=explicit_end, - version=version, tags=tags) - SafeRepresenter.__init__(self, default_style=default_style, - default_flow_style=default_flow_style) - Resolver.__init__(self) - -class Dumper(Emitter, Serializer, Representer, Resolver): - - def __init__(self, stream, - default_style=None, default_flow_style=None, - canonical=None, indent=None, width=None, - allow_unicode=None, line_break=None, - encoding=None, explicit_start=None, explicit_end=None, - version=None, tags=None): - Emitter.__init__(self, stream, canonical=canonical, - indent=indent, width=width, - allow_unicode=allow_unicode, line_break=line_break) - Serializer.__init__(self, encoding=encoding, - explicit_start=explicit_start, explicit_end=explicit_end, - version=version, tags=tags) - Representer.__init__(self, default_style=default_style, - default_flow_style=default_flow_style) - Resolver.__init__(self) - diff --git a/python.d/python_modules/pyyaml3/emitter.py b/python.d/python_modules/pyyaml3/emitter.py deleted file mode 100644 index 34cb145a5..000000000 --- a/python.d/python_modules/pyyaml3/emitter.py +++ /dev/null @@ -1,1137 +0,0 @@ - -# Emitter expects events obeying the following grammar: -# stream ::= STREAM-START document* STREAM-END -# document ::= DOCUMENT-START node DOCUMENT-END -# node ::= SCALAR | sequence | mapping -# sequence ::= SEQUENCE-START node* SEQUENCE-END -# mapping ::= MAPPING-START (node node)* MAPPING-END - -__all__ = ['Emitter', 'EmitterError'] - -from .error import YAMLError -from .events import * - -class EmitterError(YAMLError): - pass - -class ScalarAnalysis: - def __init__(self, scalar, empty, multiline, - allow_flow_plain, allow_block_plain, - allow_single_quoted, allow_double_quoted, - allow_block): - self.scalar = scalar - self.empty = empty - self.multiline = multiline - self.allow_flow_plain = allow_flow_plain - self.allow_block_plain = allow_block_plain - self.allow_single_quoted = allow_single_quoted - self.allow_double_quoted = allow_double_quoted - self.allow_block = allow_block - -class Emitter: - - DEFAULT_TAG_PREFIXES = { - '!' : '!', - 'tag:yaml.org,2002:' : '!!', - } - - def __init__(self, stream, canonical=None, indent=None, width=None, - allow_unicode=None, line_break=None): - - # The stream should have the methods `write` and possibly `flush`. - self.stream = stream - - # Encoding can be overriden by STREAM-START. - self.encoding = None - - # Emitter is a state machine with a stack of states to handle nested - # structures. - self.states = [] - self.state = self.expect_stream_start - - # Current event and the event queue. - self.events = [] - self.event = None - - # The current indentation level and the stack of previous indents. - self.indents = [] - self.indent = None - - # Flow level. - self.flow_level = 0 - - # Contexts. - self.root_context = False - self.sequence_context = False - self.mapping_context = False - self.simple_key_context = False - - # Characteristics of the last emitted character: - # - current position. - # - is it a whitespace? - # - is it an indention character - # (indentation space, '-', '?', or ':')? - self.line = 0 - self.column = 0 - self.whitespace = True - self.indention = True - - # Whether the document requires an explicit document indicator - self.open_ended = False - - # Formatting details. - self.canonical = canonical - self.allow_unicode = allow_unicode - self.best_indent = 2 - if indent and 1 < indent < 10: - self.best_indent = indent - self.best_width = 80 - if width and width > self.best_indent*2: - self.best_width = width - self.best_line_break = '\n' - if line_break in ['\r', '\n', '\r\n']: - self.best_line_break = line_break - - # Tag prefixes. - self.tag_prefixes = None - - # Prepared anchor and tag. - self.prepared_anchor = None - self.prepared_tag = None - - # Scalar analysis and style. - self.analysis = None - self.style = None - - def dispose(self): - # Reset the state attributes (to clear self-references) - self.states = [] - self.state = None - - def emit(self, event): - self.events.append(event) - while not self.need_more_events(): - self.event = self.events.pop(0) - self.state() - self.event = None - - # In some cases, we wait for a few next events before emitting. - - def need_more_events(self): - if not self.events: - return True - event = self.events[0] - if isinstance(event, DocumentStartEvent): - return self.need_events(1) - elif isinstance(event, SequenceStartEvent): - return self.need_events(2) - elif isinstance(event, MappingStartEvent): - return self.need_events(3) - else: - return False - - def need_events(self, count): - level = 0 - for event in self.events[1:]: - if isinstance(event, (DocumentStartEvent, CollectionStartEvent)): - level += 1 - elif isinstance(event, (DocumentEndEvent, CollectionEndEvent)): - level -= 1 - elif isinstance(event, StreamEndEvent): - level = -1 - if level < 0: - return False - return (len(self.events) < count+1) - - def increase_indent(self, flow=False, indentless=False): - self.indents.append(self.indent) - if self.indent is None: - if flow: - self.indent = self.best_indent - else: - self.indent = 0 - elif not indentless: - self.indent += self.best_indent - - # States. - - # Stream handlers. - - def expect_stream_start(self): - if isinstance(self.event, StreamStartEvent): - if self.event.encoding and not hasattr(self.stream, 'encoding'): - self.encoding = self.event.encoding - self.write_stream_start() - self.state = self.expect_first_document_start - else: - raise EmitterError("expected StreamStartEvent, but got %s" - % self.event) - - def expect_nothing(self): - raise EmitterError("expected nothing, but got %s" % self.event) - - # Document handlers. - - def expect_first_document_start(self): - return self.expect_document_start(first=True) - - def expect_document_start(self, first=False): - if isinstance(self.event, DocumentStartEvent): - if (self.event.version or self.event.tags) and self.open_ended: - self.write_indicator('...', True) - self.write_indent() - if self.event.version: - version_text = self.prepare_version(self.event.version) - self.write_version_directive(version_text) - self.tag_prefixes = self.DEFAULT_TAG_PREFIXES.copy() - if self.event.tags: - handles = sorted(self.event.tags.keys()) - for handle in handles: - prefix = self.event.tags[handle] - self.tag_prefixes[prefix] = handle - handle_text = self.prepare_tag_handle(handle) - prefix_text = self.prepare_tag_prefix(prefix) - self.write_tag_directive(handle_text, prefix_text) - implicit = (first and not self.event.explicit and not self.canonical - and not self.event.version and not self.event.tags - and not self.check_empty_document()) - if not implicit: - self.write_indent() - self.write_indicator('---', True) - if self.canonical: - self.write_indent() - self.state = self.expect_document_root - elif isinstance(self.event, StreamEndEvent): - if self.open_ended: - self.write_indicator('...', True) - self.write_indent() - self.write_stream_end() - self.state = self.expect_nothing - else: - raise EmitterError("expected DocumentStartEvent, but got %s" - % self.event) - - def expect_document_end(self): - if isinstance(self.event, DocumentEndEvent): - self.write_indent() - if self.event.explicit: - self.write_indicator('...', True) - self.write_indent() - self.flush_stream() - self.state = self.expect_document_start - else: - raise EmitterError("expected DocumentEndEvent, but got %s" - % self.event) - - def expect_document_root(self): - self.states.append(self.expect_document_end) - self.expect_node(root=True) - - # Node handlers. - - def expect_node(self, root=False, sequence=False, mapping=False, - simple_key=False): - self.root_context = root - self.sequence_context = sequence - self.mapping_context = mapping - self.simple_key_context = simple_key - if isinstance(self.event, AliasEvent): - self.expect_alias() - elif isinstance(self.event, (ScalarEvent, CollectionStartEvent)): - self.process_anchor('&') - self.process_tag() - if isinstance(self.event, ScalarEvent): - self.expect_scalar() - elif isinstance(self.event, SequenceStartEvent): - if self.flow_level or self.canonical or self.event.flow_style \ - or self.check_empty_sequence(): - self.expect_flow_sequence() - else: - self.expect_block_sequence() - elif isinstance(self.event, MappingStartEvent): - if self.flow_level or self.canonical or self.event.flow_style \ - or self.check_empty_mapping(): - self.expect_flow_mapping() - else: - self.expect_block_mapping() - else: - raise EmitterError("expected NodeEvent, but got %s" % self.event) - - def expect_alias(self): - if self.event.anchor is None: - raise EmitterError("anchor is not specified for alias") - self.process_anchor('*') - self.state = self.states.pop() - - def expect_scalar(self): - self.increase_indent(flow=True) - self.process_scalar() - self.indent = self.indents.pop() - self.state = self.states.pop() - - # Flow sequence handlers. - - def expect_flow_sequence(self): - self.write_indicator('[', True, whitespace=True) - self.flow_level += 1 - self.increase_indent(flow=True) - self.state = self.expect_first_flow_sequence_item - - def expect_first_flow_sequence_item(self): - if isinstance(self.event, SequenceEndEvent): - self.indent = self.indents.pop() - self.flow_level -= 1 - self.write_indicator(']', False) - self.state = self.states.pop() - else: - if self.canonical or self.column > self.best_width: - self.write_indent() - self.states.append(self.expect_flow_sequence_item) - self.expect_node(sequence=True) - - def expect_flow_sequence_item(self): - if isinstance(self.event, SequenceEndEvent): - self.indent = self.indents.pop() - self.flow_level -= 1 - if self.canonical: - self.write_indicator(',', False) - self.write_indent() - self.write_indicator(']', False) - self.state = self.states.pop() - else: - self.write_indicator(',', False) - if self.canonical or self.column > self.best_width: - self.write_indent() - self.states.append(self.expect_flow_sequence_item) - self.expect_node(sequence=True) - - # Flow mapping handlers. - - def expect_flow_mapping(self): - self.write_indicator('{', True, whitespace=True) - self.flow_level += 1 - self.increase_indent(flow=True) - self.state = self.expect_first_flow_mapping_key - - def expect_first_flow_mapping_key(self): - if isinstance(self.event, MappingEndEvent): - self.indent = self.indents.pop() - self.flow_level -= 1 - self.write_indicator('}', False) - self.state = self.states.pop() - else: - if self.canonical or self.column > self.best_width: - self.write_indent() - if not self.canonical and self.check_simple_key(): - self.states.append(self.expect_flow_mapping_simple_value) - self.expect_node(mapping=True, simple_key=True) - else: - self.write_indicator('?', True) - self.states.append(self.expect_flow_mapping_value) - self.expect_node(mapping=True) - - def expect_flow_mapping_key(self): - if isinstance(self.event, MappingEndEvent): - self.indent = self.indents.pop() - self.flow_level -= 1 - if self.canonical: - self.write_indicator(',', False) - self.write_indent() - self.write_indicator('}', False) - self.state = self.states.pop() - else: - self.write_indicator(',', False) - if self.canonical or self.column > self.best_width: - self.write_indent() - if not self.canonical and self.check_simple_key(): - self.states.append(self.expect_flow_mapping_simple_value) - self.expect_node(mapping=True, simple_key=True) - else: - self.write_indicator('?', True) - self.states.append(self.expect_flow_mapping_value) - self.expect_node(mapping=True) - - def expect_flow_mapping_simple_value(self): - self.write_indicator(':', False) - self.states.append(self.expect_flow_mapping_key) - self.expect_node(mapping=True) - - def expect_flow_mapping_value(self): - if self.canonical or self.column > self.best_width: - self.write_indent() - self.write_indicator(':', True) - self.states.append(self.expect_flow_mapping_key) - self.expect_node(mapping=True) - - # Block sequence handlers. - - def expect_block_sequence(self): - indentless = (self.mapping_context and not self.indention) - self.increase_indent(flow=False, indentless=indentless) - self.state = self.expect_first_block_sequence_item - - def expect_first_block_sequence_item(self): - return self.expect_block_sequence_item(first=True) - - def expect_block_sequence_item(self, first=False): - if not first and isinstance(self.event, SequenceEndEvent): - self.indent = self.indents.pop() - self.state = self.states.pop() - else: - self.write_indent() - self.write_indicator('-', True, indention=True) - self.states.append(self.expect_block_sequence_item) - self.expect_node(sequence=True) - - # Block mapping handlers. - - def expect_block_mapping(self): - self.increase_indent(flow=False) - self.state = self.expect_first_block_mapping_key - - def expect_first_block_mapping_key(self): - return self.expect_block_mapping_key(first=True) - - def expect_block_mapping_key(self, first=False): - if not first and isinstance(self.event, MappingEndEvent): - self.indent = self.indents.pop() - self.state = self.states.pop() - else: - self.write_indent() - if self.check_simple_key(): - self.states.append(self.expect_block_mapping_simple_value) - self.expect_node(mapping=True, simple_key=True) - else: - self.write_indicator('?', True, indention=True) - self.states.append(self.expect_block_mapping_value) - self.expect_node(mapping=True) - - def expect_block_mapping_simple_value(self): - self.write_indicator(':', False) - self.states.append(self.expect_block_mapping_key) - self.expect_node(mapping=True) - - def expect_block_mapping_value(self): - self.write_indent() - self.write_indicator(':', True, indention=True) - self.states.append(self.expect_block_mapping_key) - self.expect_node(mapping=True) - - # Checkers. - - def check_empty_sequence(self): - return (isinstance(self.event, SequenceStartEvent) and self.events - and isinstance(self.events[0], SequenceEndEvent)) - - def check_empty_mapping(self): - return (isinstance(self.event, MappingStartEvent) and self.events - and isinstance(self.events[0], MappingEndEvent)) - - def check_empty_document(self): - if not isinstance(self.event, DocumentStartEvent) or not self.events: - return False - event = self.events[0] - return (isinstance(event, ScalarEvent) and event.anchor is None - and event.tag is None and event.implicit and event.value == '') - - def check_simple_key(self): - length = 0 - if isinstance(self.event, NodeEvent) and self.event.anchor is not None: - if self.prepared_anchor is None: - self.prepared_anchor = self.prepare_anchor(self.event.anchor) - length += len(self.prepared_anchor) - if isinstance(self.event, (ScalarEvent, CollectionStartEvent)) \ - and self.event.tag is not None: - if self.prepared_tag is None: - self.prepared_tag = self.prepare_tag(self.event.tag) - length += len(self.prepared_tag) - if isinstance(self.event, ScalarEvent): - if self.analysis is None: - self.analysis = self.analyze_scalar(self.event.value) - length += len(self.analysis.scalar) - return (length < 128 and (isinstance(self.event, AliasEvent) - or (isinstance(self.event, ScalarEvent) - and not self.analysis.empty and not self.analysis.multiline) - or self.check_empty_sequence() or self.check_empty_mapping())) - - # Anchor, Tag, and Scalar processors. - - def process_anchor(self, indicator): - if self.event.anchor is None: - self.prepared_anchor = None - return - if self.prepared_anchor is None: - self.prepared_anchor = self.prepare_anchor(self.event.anchor) - if self.prepared_anchor: - self.write_indicator(indicator+self.prepared_anchor, True) - self.prepared_anchor = None - - def process_tag(self): - tag = self.event.tag - if isinstance(self.event, ScalarEvent): - if self.style is None: - self.style = self.choose_scalar_style() - if ((not self.canonical or tag is None) and - ((self.style == '' and self.event.implicit[0]) - or (self.style != '' and self.event.implicit[1]))): - self.prepared_tag = None - return - if self.event.implicit[0] and tag is None: - tag = '!' - self.prepared_tag = None - else: - if (not self.canonical or tag is None) and self.event.implicit: - self.prepared_tag = None - return - if tag is None: - raise EmitterError("tag is not specified") - if self.prepared_tag is None: - self.prepared_tag = self.prepare_tag(tag) - if self.prepared_tag: - self.write_indicator(self.prepared_tag, True) - self.prepared_tag = None - - def choose_scalar_style(self): - if self.analysis is None: - self.analysis = self.analyze_scalar(self.event.value) - if self.event.style == '"' or self.canonical: - return '"' - if not self.event.style and self.event.implicit[0]: - if (not (self.simple_key_context and - (self.analysis.empty or self.analysis.multiline)) - and (self.flow_level and self.analysis.allow_flow_plain - or (not self.flow_level and self.analysis.allow_block_plain))): - return '' - if self.event.style and self.event.style in '|>': - if (not self.flow_level and not self.simple_key_context - and self.analysis.allow_block): - return self.event.style - if not self.event.style or self.event.style == '\'': - if (self.analysis.allow_single_quoted and - not (self.simple_key_context and self.analysis.multiline)): - return '\'' - return '"' - - def process_scalar(self): - if self.analysis is None: - self.analysis = self.analyze_scalar(self.event.value) - if self.style is None: - self.style = self.choose_scalar_style() - split = (not self.simple_key_context) - #if self.analysis.multiline and split \ - # and (not self.style or self.style in '\'\"'): - # self.write_indent() - if self.style == '"': - self.write_double_quoted(self.analysis.scalar, split) - elif self.style == '\'': - self.write_single_quoted(self.analysis.scalar, split) - elif self.style == '>': - self.write_folded(self.analysis.scalar) - elif self.style == '|': - self.write_literal(self.analysis.scalar) - else: - self.write_plain(self.analysis.scalar, split) - self.analysis = None - self.style = None - - # Analyzers. - - def prepare_version(self, version): - major, minor = version - if major != 1: - raise EmitterError("unsupported YAML version: %d.%d" % (major, minor)) - return '%d.%d' % (major, minor) - - def prepare_tag_handle(self, handle): - if not handle: - raise EmitterError("tag handle must not be empty") - if handle[0] != '!' or handle[-1] != '!': - raise EmitterError("tag handle must start and end with '!': %r" % handle) - for ch in handle[1:-1]: - if not ('0' <= ch <= '9' or 'A' <= ch <= 'Z' or 'a' <= ch <= 'z' \ - or ch in '-_'): - raise EmitterError("invalid character %r in the tag handle: %r" - % (ch, handle)) - return handle - - def prepare_tag_prefix(self, prefix): - if not prefix: - raise EmitterError("tag prefix must not be empty") - chunks = [] - start = end = 0 - if prefix[0] == '!': - end = 1 - while end < len(prefix): - ch = prefix[end] - if '0' <= ch <= '9' or 'A' <= ch <= 'Z' or 'a' <= ch <= 'z' \ - or ch in '-;/?!:@&=+$,_.~*\'()[]': - end += 1 - else: - if start < end: - chunks.append(prefix[start:end]) - start = end = end+1 - data = ch.encode('utf-8') - for ch in data: - chunks.append('%%%02X' % ord(ch)) - if start < end: - chunks.append(prefix[start:end]) - return ''.join(chunks) - - def prepare_tag(self, tag): - if not tag: - raise EmitterError("tag must not be empty") - if tag == '!': - return tag - handle = None - suffix = tag - prefixes = sorted(self.tag_prefixes.keys()) - for prefix in prefixes: - if tag.startswith(prefix) \ - and (prefix == '!' or len(prefix) < len(tag)): - handle = self.tag_prefixes[prefix] - suffix = tag[len(prefix):] - chunks = [] - start = end = 0 - while end < len(suffix): - ch = suffix[end] - if '0' <= ch <= '9' or 'A' <= ch <= 'Z' or 'a' <= ch <= 'z' \ - or ch in '-;/?:@&=+$,_.~*\'()[]' \ - or (ch == '!' and handle != '!'): - end += 1 - else: - if start < end: - chunks.append(suffix[start:end]) - start = end = end+1 - data = ch.encode('utf-8') - for ch in data: - chunks.append('%%%02X' % ord(ch)) - if start < end: - chunks.append(suffix[start:end]) - suffix_text = ''.join(chunks) - if handle: - return '%s%s' % (handle, suffix_text) - else: - return '!<%s>' % suffix_text - - def prepare_anchor(self, anchor): - if not anchor: - raise EmitterError("anchor must not be empty") - for ch in anchor: - if not ('0' <= ch <= '9' or 'A' <= ch <= 'Z' or 'a' <= ch <= 'z' \ - or ch in '-_'): - raise EmitterError("invalid character %r in the anchor: %r" - % (ch, anchor)) - return anchor - - def analyze_scalar(self, scalar): - - # Empty scalar is a special case. - if not scalar: - return ScalarAnalysis(scalar=scalar, empty=True, multiline=False, - allow_flow_plain=False, allow_block_plain=True, - allow_single_quoted=True, allow_double_quoted=True, - allow_block=False) - - # Indicators and special characters. - block_indicators = False - flow_indicators = False - line_breaks = False - special_characters = False - - # Important whitespace combinations. - leading_space = False - leading_break = False - trailing_space = False - trailing_break = False - break_space = False - space_break = False - - # Check document indicators. - if scalar.startswith('---') or scalar.startswith('...'): - block_indicators = True - flow_indicators = True - - # First character or preceded by a whitespace. - preceeded_by_whitespace = True - - # Last character or followed by a whitespace. - followed_by_whitespace = (len(scalar) == 1 or - scalar[1] in '\0 \t\r\n\x85\u2028\u2029') - - # The previous character is a space. - previous_space = False - - # The previous character is a break. - previous_break = False - - index = 0 - while index < len(scalar): - ch = scalar[index] - - # Check for indicators. - if index == 0: - # Leading indicators are special characters. - if ch in '#,[]{}&*!|>\'\"%@`': - flow_indicators = True - block_indicators = True - if ch in '?:': - flow_indicators = True - if followed_by_whitespace: - block_indicators = True - if ch == '-' and followed_by_whitespace: - flow_indicators = True - block_indicators = True - else: - # Some indicators cannot appear within a scalar as well. - if ch in ',?[]{}': - flow_indicators = True - if ch == ':': - flow_indicators = True - if followed_by_whitespace: - block_indicators = True - if ch == '#' and preceeded_by_whitespace: - flow_indicators = True - block_indicators = True - - # Check for line breaks, special, and unicode characters. - if ch in '\n\x85\u2028\u2029': - line_breaks = True - if not (ch == '\n' or '\x20' <= ch <= '\x7E'): - if (ch == '\x85' or '\xA0' <= ch <= '\uD7FF' - or '\uE000' <= ch <= '\uFFFD') and ch != '\uFEFF': - unicode_characters = True - if not self.allow_unicode: - special_characters = True - else: - special_characters = True - - # Detect important whitespace combinations. - if ch == ' ': - if index == 0: - leading_space = True - if index == len(scalar)-1: - trailing_space = True - if previous_break: - break_space = True - previous_space = True - previous_break = False - elif ch in '\n\x85\u2028\u2029': - if index == 0: - leading_break = True - if index == len(scalar)-1: - trailing_break = True - if previous_space: - space_break = True - previous_space = False - previous_break = True - else: - previous_space = False - previous_break = False - - # Prepare for the next character. - index += 1 - preceeded_by_whitespace = (ch in '\0 \t\r\n\x85\u2028\u2029') - followed_by_whitespace = (index+1 >= len(scalar) or - scalar[index+1] in '\0 \t\r\n\x85\u2028\u2029') - - # Let's decide what styles are allowed. - allow_flow_plain = True - allow_block_plain = True - allow_single_quoted = True - allow_double_quoted = True - allow_block = True - - # Leading and trailing whitespaces are bad for plain scalars. - if (leading_space or leading_break - or trailing_space or trailing_break): - allow_flow_plain = allow_block_plain = False - - # We do not permit trailing spaces for block scalars. - if trailing_space: - allow_block = False - - # Spaces at the beginning of a new line are only acceptable for block - # scalars. - if break_space: - allow_flow_plain = allow_block_plain = allow_single_quoted = False - - # Spaces followed by breaks, as well as special character are only - # allowed for double quoted scalars. - if space_break or special_characters: - allow_flow_plain = allow_block_plain = \ - allow_single_quoted = allow_block = False - - # Although the plain scalar writer supports breaks, we never emit - # multiline plain scalars. - if line_breaks: - allow_flow_plain = allow_block_plain = False - - # Flow indicators are forbidden for flow plain scalars. - if flow_indicators: - allow_flow_plain = False - - # Block indicators are forbidden for block plain scalars. - if block_indicators: - allow_block_plain = False - - return ScalarAnalysis(scalar=scalar, - empty=False, multiline=line_breaks, - allow_flow_plain=allow_flow_plain, - allow_block_plain=allow_block_plain, - allow_single_quoted=allow_single_quoted, - allow_double_quoted=allow_double_quoted, - allow_block=allow_block) - - # Writers. - - def flush_stream(self): - if hasattr(self.stream, 'flush'): - self.stream.flush() - - def write_stream_start(self): - # Write BOM if needed. - if self.encoding and self.encoding.startswith('utf-16'): - self.stream.write('\uFEFF'.encode(self.encoding)) - - def write_stream_end(self): - self.flush_stream() - - def write_indicator(self, indicator, need_whitespace, - whitespace=False, indention=False): - if self.whitespace or not need_whitespace: - data = indicator - else: - data = ' '+indicator - self.whitespace = whitespace - self.indention = self.indention and indention - self.column += len(data) - self.open_ended = False - if self.encoding: - data = data.encode(self.encoding) - self.stream.write(data) - - def write_indent(self): - indent = self.indent or 0 - if not self.indention or self.column > indent \ - or (self.column == indent and not self.whitespace): - self.write_line_break() - if self.column < indent: - self.whitespace = True - data = ' '*(indent-self.column) - self.column = indent - if self.encoding: - data = data.encode(self.encoding) - self.stream.write(data) - - def write_line_break(self, data=None): - if data is None: - data = self.best_line_break - self.whitespace = True - self.indention = True - self.line += 1 - self.column = 0 - if self.encoding: - data = data.encode(self.encoding) - self.stream.write(data) - - def write_version_directive(self, version_text): - data = '%%YAML %s' % version_text - if self.encoding: - data = data.encode(self.encoding) - self.stream.write(data) - self.write_line_break() - - def write_tag_directive(self, handle_text, prefix_text): - data = '%%TAG %s %s' % (handle_text, prefix_text) - if self.encoding: - data = data.encode(self.encoding) - self.stream.write(data) - self.write_line_break() - - # Scalar streams. - - def write_single_quoted(self, text, split=True): - self.write_indicator('\'', True) - spaces = False - breaks = False - start = end = 0 - while end <= len(text): - ch = None - if end < len(text): - ch = text[end] - if spaces: - if ch is None or ch != ' ': - if start+1 == end and self.column > self.best_width and split \ - and start != 0 and end != len(text): - self.write_indent() - else: - data = text[start:end] - self.column += len(data) - if self.encoding: - data = data.encode(self.encoding) - self.stream.write(data) - start = end - elif breaks: - if ch is None or ch not in '\n\x85\u2028\u2029': - if text[start] == '\n': - self.write_line_break() - for br in text[start:end]: - if br == '\n': - self.write_line_break() - else: - self.write_line_break(br) - self.write_indent() - start = end - else: - if ch is None or ch in ' \n\x85\u2028\u2029' or ch == '\'': - if start < end: - data = text[start:end] - self.column += len(data) - if self.encoding: - data = data.encode(self.encoding) - self.stream.write(data) - start = end - if ch == '\'': - data = '\'\'' - self.column += 2 - if self.encoding: - data = data.encode(self.encoding) - self.stream.write(data) - start = end + 1 - if ch is not None: - spaces = (ch == ' ') - breaks = (ch in '\n\x85\u2028\u2029') - end += 1 - self.write_indicator('\'', False) - - ESCAPE_REPLACEMENTS = { - '\0': '0', - '\x07': 'a', - '\x08': 'b', - '\x09': 't', - '\x0A': 'n', - '\x0B': 'v', - '\x0C': 'f', - '\x0D': 'r', - '\x1B': 'e', - '\"': '\"', - '\\': '\\', - '\x85': 'N', - '\xA0': '_', - '\u2028': 'L', - '\u2029': 'P', - } - - def write_double_quoted(self, text, split=True): - self.write_indicator('"', True) - start = end = 0 - while end <= len(text): - ch = None - if end < len(text): - ch = text[end] - if ch is None or ch in '"\\\x85\u2028\u2029\uFEFF' \ - or not ('\x20' <= ch <= '\x7E' - or (self.allow_unicode - and ('\xA0' <= ch <= '\uD7FF' - or '\uE000' <= ch <= '\uFFFD'))): - if start < end: - data = text[start:end] - self.column += len(data) - if self.encoding: - data = data.encode(self.encoding) - self.stream.write(data) - start = end - if ch is not None: - if ch in self.ESCAPE_REPLACEMENTS: - data = '\\'+self.ESCAPE_REPLACEMENTS[ch] - elif ch <= '\xFF': - data = '\\x%02X' % ord(ch) - elif ch <= '\uFFFF': - data = '\\u%04X' % ord(ch) - else: - data = '\\U%08X' % ord(ch) - self.column += len(data) - if self.encoding: - data = data.encode(self.encoding) - self.stream.write(data) - start = end+1 - if 0 < end < len(text)-1 and (ch == ' ' or start >= end) \ - and self.column+(end-start) > self.best_width and split: - data = text[start:end]+'\\' - if start < end: - start = end - self.column += len(data) - if self.encoding: - data = data.encode(self.encoding) - self.stream.write(data) - self.write_indent() - self.whitespace = False - self.indention = False - if text[start] == ' ': - data = '\\' - self.column += len(data) - if self.encoding: - data = data.encode(self.encoding) - self.stream.write(data) - end += 1 - self.write_indicator('"', False) - - def determine_block_hints(self, text): - hints = '' - if text: - if text[0] in ' \n\x85\u2028\u2029': - hints += str(self.best_indent) - if text[-1] not in '\n\x85\u2028\u2029': - hints += '-' - elif len(text) == 1 or text[-2] in '\n\x85\u2028\u2029': - hints += '+' - return hints - - def write_folded(self, text): - hints = self.determine_block_hints(text) - self.write_indicator('>'+hints, True) - if hints[-1:] == '+': - self.open_ended = True - self.write_line_break() - leading_space = True - spaces = False - breaks = True - start = end = 0 - while end <= len(text): - ch = None - if end < len(text): - ch = text[end] - if breaks: - if ch is None or ch not in '\n\x85\u2028\u2029': - if not leading_space and ch is not None and ch != ' ' \ - and text[start] == '\n': - self.write_line_break() - leading_space = (ch == ' ') - for br in text[start:end]: - if br == '\n': - self.write_line_break() - else: - self.write_line_break(br) - if ch is not None: - self.write_indent() - start = end - elif spaces: - if ch != ' ': - if start+1 == end and self.column > self.best_width: - self.write_indent() - else: - data = text[start:end] - self.column += len(data) - if self.encoding: - data = data.encode(self.encoding) - self.stream.write(data) - start = end - else: - if ch is None or ch in ' \n\x85\u2028\u2029': - data = text[start:end] - self.column += len(data) - if self.encoding: - data = data.encode(self.encoding) - self.stream.write(data) - if ch is None: - self.write_line_break() - start = end - if ch is not None: - breaks = (ch in '\n\x85\u2028\u2029') - spaces = (ch == ' ') - end += 1 - - def write_literal(self, text): - hints = self.determine_block_hints(text) - self.write_indicator('|'+hints, True) - if hints[-1:] == '+': - self.open_ended = True - self.write_line_break() - breaks = True - start = end = 0 - while end <= len(text): - ch = None - if end < len(text): - ch = text[end] - if breaks: - if ch is None or ch not in '\n\x85\u2028\u2029': - for br in text[start:end]: - if br == '\n': - self.write_line_break() - else: - self.write_line_break(br) - if ch is not None: - self.write_indent() - start = end - else: - if ch is None or ch in '\n\x85\u2028\u2029': - data = text[start:end] - if self.encoding: - data = data.encode(self.encoding) - self.stream.write(data) - if ch is None: - self.write_line_break() - start = end - if ch is not None: - breaks = (ch in '\n\x85\u2028\u2029') - end += 1 - - def write_plain(self, text, split=True): - if self.root_context: - self.open_ended = True - if not text: - return - if not self.whitespace: - data = ' ' - self.column += len(data) - if self.encoding: - data = data.encode(self.encoding) - self.stream.write(data) - self.whitespace = False - self.indention = False - spaces = False - breaks = False - start = end = 0 - while end <= len(text): - ch = None - if end < len(text): - ch = text[end] - if spaces: - if ch != ' ': - if start+1 == end and self.column > self.best_width and split: - self.write_indent() - self.whitespace = False - self.indention = False - else: - data = text[start:end] - self.column += len(data) - if self.encoding: - data = data.encode(self.encoding) - self.stream.write(data) - start = end - elif breaks: - if ch not in '\n\x85\u2028\u2029': - if text[start] == '\n': - self.write_line_break() - for br in text[start:end]: - if br == '\n': - self.write_line_break() - else: - self.write_line_break(br) - self.write_indent() - self.whitespace = False - self.indention = False - start = end - else: - if ch is None or ch in ' \n\x85\u2028\u2029': - data = text[start:end] - self.column += len(data) - if self.encoding: - data = data.encode(self.encoding) - self.stream.write(data) - start = end - if ch is not None: - spaces = (ch == ' ') - breaks = (ch in '\n\x85\u2028\u2029') - end += 1 - diff --git a/python.d/python_modules/pyyaml3/error.py b/python.d/python_modules/pyyaml3/error.py deleted file mode 100644 index b796b4dc5..000000000 --- a/python.d/python_modules/pyyaml3/error.py +++ /dev/null @@ -1,75 +0,0 @@ - -__all__ = ['Mark', 'YAMLError', 'MarkedYAMLError'] - -class Mark: - - def __init__(self, name, index, line, column, buffer, pointer): - self.name = name - self.index = index - self.line = line - self.column = column - self.buffer = buffer - self.pointer = pointer - - def get_snippet(self, indent=4, max_length=75): - if self.buffer is None: - return None - head = '' - start = self.pointer - while start > 0 and self.buffer[start-1] not in '\0\r\n\x85\u2028\u2029': - start -= 1 - if self.pointer-start > max_length/2-1: - head = ' ... ' - start += 5 - break - tail = '' - end = self.pointer - while end < len(self.buffer) and self.buffer[end] not in '\0\r\n\x85\u2028\u2029': - end += 1 - if end-self.pointer > max_length/2-1: - tail = ' ... ' - end -= 5 - break - snippet = self.buffer[start:end] - return ' '*indent + head + snippet + tail + '\n' \ - + ' '*(indent+self.pointer-start+len(head)) + '^' - - def __str__(self): - snippet = self.get_snippet() - where = " in \"%s\", line %d, column %d" \ - % (self.name, self.line+1, self.column+1) - if snippet is not None: - where += ":\n"+snippet - return where - -class YAMLError(Exception): - pass - -class MarkedYAMLError(YAMLError): - - def __init__(self, context=None, context_mark=None, - problem=None, problem_mark=None, note=None): - self.context = context - self.context_mark = context_mark - self.problem = problem - self.problem_mark = problem_mark - self.note = note - - def __str__(self): - lines = [] - if self.context is not None: - lines.append(self.context) - if self.context_mark is not None \ - and (self.problem is None or self.problem_mark is None - or self.context_mark.name != self.problem_mark.name - or self.context_mark.line != self.problem_mark.line - or self.context_mark.column != self.problem_mark.column): - lines.append(str(self.context_mark)) - if self.problem is not None: - lines.append(self.problem) - if self.problem_mark is not None: - lines.append(str(self.problem_mark)) - if self.note is not None: - lines.append(self.note) - return '\n'.join(lines) - diff --git a/python.d/python_modules/pyyaml3/events.py b/python.d/python_modules/pyyaml3/events.py deleted file mode 100644 index f79ad389c..000000000 --- a/python.d/python_modules/pyyaml3/events.py +++ /dev/null @@ -1,86 +0,0 @@ - -# Abstract classes. - -class Event(object): - def __init__(self, start_mark=None, end_mark=None): - self.start_mark = start_mark - self.end_mark = end_mark - def __repr__(self): - attributes = [key for key in ['anchor', 'tag', 'implicit', 'value'] - if hasattr(self, key)] - arguments = ', '.join(['%s=%r' % (key, getattr(self, key)) - for key in attributes]) - return '%s(%s)' % (self.__class__.__name__, arguments) - -class NodeEvent(Event): - def __init__(self, anchor, start_mark=None, end_mark=None): - self.anchor = anchor - self.start_mark = start_mark - self.end_mark = end_mark - -class CollectionStartEvent(NodeEvent): - def __init__(self, anchor, tag, implicit, start_mark=None, end_mark=None, - flow_style=None): - self.anchor = anchor - self.tag = tag - self.implicit = implicit - self.start_mark = start_mark - self.end_mark = end_mark - self.flow_style = flow_style - -class CollectionEndEvent(Event): - pass - -# Implementations. - -class StreamStartEvent(Event): - def __init__(self, start_mark=None, end_mark=None, encoding=None): - self.start_mark = start_mark - self.end_mark = end_mark - self.encoding = encoding - -class StreamEndEvent(Event): - pass - -class DocumentStartEvent(Event): - def __init__(self, start_mark=None, end_mark=None, - explicit=None, version=None, tags=None): - self.start_mark = start_mark - self.end_mark = end_mark - self.explicit = explicit - self.version = version - self.tags = tags - -class DocumentEndEvent(Event): - def __init__(self, start_mark=None, end_mark=None, - explicit=None): - self.start_mark = start_mark - self.end_mark = end_mark - self.explicit = explicit - -class AliasEvent(NodeEvent): - pass - -class ScalarEvent(NodeEvent): - def __init__(self, anchor, tag, implicit, value, - start_mark=None, end_mark=None, style=None): - self.anchor = anchor - self.tag = tag - self.implicit = implicit - self.value = value - self.start_mark = start_mark - self.end_mark = end_mark - self.style = style - -class SequenceStartEvent(CollectionStartEvent): - pass - -class SequenceEndEvent(CollectionEndEvent): - pass - -class MappingStartEvent(CollectionStartEvent): - pass - -class MappingEndEvent(CollectionEndEvent): - pass - diff --git a/python.d/python_modules/pyyaml3/loader.py b/python.d/python_modules/pyyaml3/loader.py deleted file mode 100644 index 08c8f01b3..000000000 --- a/python.d/python_modules/pyyaml3/loader.py +++ /dev/null @@ -1,40 +0,0 @@ - -__all__ = ['BaseLoader', 'SafeLoader', 'Loader'] - -from .reader import * -from .scanner import * -from .parser import * -from .composer import * -from .constructor import * -from .resolver import * - -class BaseLoader(Reader, Scanner, Parser, Composer, BaseConstructor, BaseResolver): - - def __init__(self, stream): - Reader.__init__(self, stream) - Scanner.__init__(self) - Parser.__init__(self) - Composer.__init__(self) - BaseConstructor.__init__(self) - BaseResolver.__init__(self) - -class SafeLoader(Reader, Scanner, Parser, Composer, SafeConstructor, Resolver): - - def __init__(self, stream): - Reader.__init__(self, stream) - Scanner.__init__(self) - Parser.__init__(self) - Composer.__init__(self) - SafeConstructor.__init__(self) - Resolver.__init__(self) - -class Loader(Reader, Scanner, Parser, Composer, Constructor, Resolver): - - def __init__(self, stream): - Reader.__init__(self, stream) - Scanner.__init__(self) - Parser.__init__(self) - Composer.__init__(self) - Constructor.__init__(self) - Resolver.__init__(self) - diff --git a/python.d/python_modules/pyyaml3/nodes.py b/python.d/python_modules/pyyaml3/nodes.py deleted file mode 100644 index c4f070c41..000000000 --- a/python.d/python_modules/pyyaml3/nodes.py +++ /dev/null @@ -1,49 +0,0 @@ - -class Node(object): - def __init__(self, tag, value, start_mark, end_mark): - self.tag = tag - self.value = value - self.start_mark = start_mark - self.end_mark = end_mark - def __repr__(self): - value = self.value - #if isinstance(value, list): - # if len(value) == 0: - # value = '<empty>' - # elif len(value) == 1: - # value = '<1 item>' - # else: - # value = '<%d items>' % len(value) - #else: - # if len(value) > 75: - # value = repr(value[:70]+u' ... ') - # else: - # value = repr(value) - value = repr(value) - return '%s(tag=%r, value=%s)' % (self.__class__.__name__, self.tag, value) - -class ScalarNode(Node): - id = 'scalar' - def __init__(self, tag, value, - start_mark=None, end_mark=None, style=None): - self.tag = tag - self.value = value - self.start_mark = start_mark - self.end_mark = end_mark - self.style = style - -class CollectionNode(Node): - def __init__(self, tag, value, - start_mark=None, end_mark=None, flow_style=None): - self.tag = tag - self.value = value - self.start_mark = start_mark - self.end_mark = end_mark - self.flow_style = flow_style - -class SequenceNode(CollectionNode): - id = 'sequence' - -class MappingNode(CollectionNode): - id = 'mapping' - diff --git a/python.d/python_modules/pyyaml3/parser.py b/python.d/python_modules/pyyaml3/parser.py deleted file mode 100644 index 13a5995d2..000000000 --- a/python.d/python_modules/pyyaml3/parser.py +++ /dev/null @@ -1,589 +0,0 @@ - -# The following YAML grammar is LL(1) and is parsed by a recursive descent -# parser. -# -# stream ::= STREAM-START implicit_document? explicit_document* STREAM-END -# implicit_document ::= block_node DOCUMENT-END* -# explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END* -# block_node_or_indentless_sequence ::= -# ALIAS -# | properties (block_content | indentless_block_sequence)? -# | block_content -# | indentless_block_sequence -# block_node ::= ALIAS -# | properties block_content? -# | block_content -# flow_node ::= ALIAS -# | properties flow_content? -# | flow_content -# properties ::= TAG ANCHOR? | ANCHOR TAG? -# block_content ::= block_collection | flow_collection | SCALAR -# flow_content ::= flow_collection | SCALAR -# block_collection ::= block_sequence | block_mapping -# flow_collection ::= flow_sequence | flow_mapping -# block_sequence ::= BLOCK-SEQUENCE-START (BLOCK-ENTRY block_node?)* BLOCK-END -# indentless_sequence ::= (BLOCK-ENTRY block_node?)+ -# block_mapping ::= BLOCK-MAPPING_START -# ((KEY block_node_or_indentless_sequence?)? -# (VALUE block_node_or_indentless_sequence?)?)* -# BLOCK-END -# flow_sequence ::= FLOW-SEQUENCE-START -# (flow_sequence_entry FLOW-ENTRY)* -# flow_sequence_entry? -# FLOW-SEQUENCE-END -# flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? -# flow_mapping ::= FLOW-MAPPING-START -# (flow_mapping_entry FLOW-ENTRY)* -# flow_mapping_entry? -# FLOW-MAPPING-END -# flow_mapping_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? -# -# FIRST sets: -# -# stream: { STREAM-START } -# explicit_document: { DIRECTIVE DOCUMENT-START } -# implicit_document: FIRST(block_node) -# block_node: { ALIAS TAG ANCHOR SCALAR BLOCK-SEQUENCE-START BLOCK-MAPPING-START FLOW-SEQUENCE-START FLOW-MAPPING-START } -# flow_node: { ALIAS ANCHOR TAG SCALAR FLOW-SEQUENCE-START FLOW-MAPPING-START } -# block_content: { BLOCK-SEQUENCE-START BLOCK-MAPPING-START FLOW-SEQUENCE-START FLOW-MAPPING-START SCALAR } -# flow_content: { FLOW-SEQUENCE-START FLOW-MAPPING-START SCALAR } -# block_collection: { BLOCK-SEQUENCE-START BLOCK-MAPPING-START } -# flow_collection: { FLOW-SEQUENCE-START FLOW-MAPPING-START } -# block_sequence: { BLOCK-SEQUENCE-START } -# block_mapping: { BLOCK-MAPPING-START } -# block_node_or_indentless_sequence: { ALIAS ANCHOR TAG SCALAR BLOCK-SEQUENCE-START BLOCK-MAPPING-START FLOW-SEQUENCE-START FLOW-MAPPING-START BLOCK-ENTRY } -# indentless_sequence: { ENTRY } -# flow_collection: { FLOW-SEQUENCE-START FLOW-MAPPING-START } -# flow_sequence: { FLOW-SEQUENCE-START } -# flow_mapping: { FLOW-MAPPING-START } -# flow_sequence_entry: { ALIAS ANCHOR TAG SCALAR FLOW-SEQUENCE-START FLOW-MAPPING-START KEY } -# flow_mapping_entry: { ALIAS ANCHOR TAG SCALAR FLOW-SEQUENCE-START FLOW-MAPPING-START KEY } - -__all__ = ['Parser', 'ParserError'] - -from .error import MarkedYAMLError -from .tokens import * -from .events import * -from .scanner import * - -class ParserError(MarkedYAMLError): - pass - -class Parser: - # Since writing a recursive-descendant parser is a straightforward task, we - # do not give many comments here. - - DEFAULT_TAGS = { - '!': '!', - '!!': 'tag:yaml.org,2002:', - } - - def __init__(self): - self.current_event = None - self.yaml_version = None - self.tag_handles = {} - self.states = [] - self.marks = [] - self.state = self.parse_stream_start - - def dispose(self): - # Reset the state attributes (to clear self-references) - self.states = [] - self.state = None - - def check_event(self, *choices): - # Check the type of the next event. - if self.current_event is None: - if self.state: - self.current_event = self.state() - if self.current_event is not None: - if not choices: - return True - for choice in choices: - if isinstance(self.current_event, choice): - return True - return False - - def peek_event(self): - # Get the next event. - if self.current_event is None: - if self.state: - self.current_event = self.state() - return self.current_event - - def get_event(self): - # Get the next event and proceed further. - if self.current_event is None: - if self.state: - self.current_event = self.state() - value = self.current_event - self.current_event = None - return value - - # stream ::= STREAM-START implicit_document? explicit_document* STREAM-END - # implicit_document ::= block_node DOCUMENT-END* - # explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END* - - def parse_stream_start(self): - - # Parse the stream start. - token = self.get_token() - event = StreamStartEvent(token.start_mark, token.end_mark, - encoding=token.encoding) - - # Prepare the next state. - self.state = self.parse_implicit_document_start - - return event - - def parse_implicit_document_start(self): - - # Parse an implicit document. - if not self.check_token(DirectiveToken, DocumentStartToken, - StreamEndToken): - self.tag_handles = self.DEFAULT_TAGS - token = self.peek_token() - start_mark = end_mark = token.start_mark - event = DocumentStartEvent(start_mark, end_mark, - explicit=False) - - # Prepare the next state. - self.states.append(self.parse_document_end) - self.state = self.parse_block_node - - return event - - else: - return self.parse_document_start() - - def parse_document_start(self): - - # Parse any extra document end indicators. - while self.check_token(DocumentEndToken): - self.get_token() - - # Parse an explicit document. - if not self.check_token(StreamEndToken): - token = self.peek_token() - start_mark = token.start_mark - version, tags = self.process_directives() - if not self.check_token(DocumentStartToken): - raise ParserError(None, None, - "expected '<document start>', but found %r" - % self.peek_token().id, - self.peek_token().start_mark) - token = self.get_token() - end_mark = token.end_mark - event = DocumentStartEvent(start_mark, end_mark, - explicit=True, version=version, tags=tags) - self.states.append(self.parse_document_end) - self.state = self.parse_document_content - else: - # Parse the end of the stream. - token = self.get_token() - event = StreamEndEvent(token.start_mark, token.end_mark) - assert not self.states - assert not self.marks - self.state = None - return event - - def parse_document_end(self): - - # Parse the document end. - token = self.peek_token() - start_mark = end_mark = token.start_mark - explicit = False - if self.check_token(DocumentEndToken): - token = self.get_token() - end_mark = token.end_mark - explicit = True - event = DocumentEndEvent(start_mark, end_mark, - explicit=explicit) - - # Prepare the next state. - self.state = self.parse_document_start - - return event - - def parse_document_content(self): - if self.check_token(DirectiveToken, - DocumentStartToken, DocumentEndToken, StreamEndToken): - event = self.process_empty_scalar(self.peek_token().start_mark) - self.state = self.states.pop() - return event - else: - return self.parse_block_node() - - def process_directives(self): - self.yaml_version = None - self.tag_handles = {} - while self.check_token(DirectiveToken): - token = self.get_token() - if token.name == 'YAML': - if self.yaml_version is not None: - raise ParserError(None, None, - "found duplicate YAML directive", token.start_mark) - major, minor = token.value - if major != 1: - raise ParserError(None, None, - "found incompatible YAML document (version 1.* is required)", - token.start_mark) - self.yaml_version = token.value - elif token.name == 'TAG': - handle, prefix = token.value - if handle in self.tag_handles: - raise ParserError(None, None, - "duplicate tag handle %r" % handle, - token.start_mark) - self.tag_handles[handle] = prefix - if self.tag_handles: - value = self.yaml_version, self.tag_handles.copy() - else: - value = self.yaml_version, None - for key in self.DEFAULT_TAGS: - if key not in self.tag_handles: - self.tag_handles[key] = self.DEFAULT_TAGS[key] - return value - - # block_node_or_indentless_sequence ::= ALIAS - # | properties (block_content | indentless_block_sequence)? - # | block_content - # | indentless_block_sequence - # block_node ::= ALIAS - # | properties block_content? - # | block_content - # flow_node ::= ALIAS - # | properties flow_content? - # | flow_content - # properties ::= TAG ANCHOR? | ANCHOR TAG? - # block_content ::= block_collection | flow_collection | SCALAR - # flow_content ::= flow_collection | SCALAR - # block_collection ::= block_sequence | block_mapping - # flow_collection ::= flow_sequence | flow_mapping - - def parse_block_node(self): - return self.parse_node(block=True) - - def parse_flow_node(self): - return self.parse_node() - - def parse_block_node_or_indentless_sequence(self): - return self.parse_node(block=True, indentless_sequence=True) - - def parse_node(self, block=False, indentless_sequence=False): - if self.check_token(AliasToken): - token = self.get_token() - event = AliasEvent(token.value, token.start_mark, token.end_mark) - self.state = self.states.pop() - else: - anchor = None - tag = None - start_mark = end_mark = tag_mark = None - if self.check_token(AnchorToken): - token = self.get_token() - start_mark = token.start_mark - end_mark = token.end_mark - anchor = token.value - if self.check_token(TagToken): - token = self.get_token() - tag_mark = token.start_mark - end_mark = token.end_mark - tag = token.value - elif self.check_token(TagToken): - token = self.get_token() - start_mark = tag_mark = token.start_mark - end_mark = token.end_mark - tag = token.value - if self.check_token(AnchorToken): - token = self.get_token() - end_mark = token.end_mark - anchor = token.value - if tag is not None: - handle, suffix = tag - if handle is not None: - if handle not in self.tag_handles: - raise ParserError("while parsing a node", start_mark, - "found undefined tag handle %r" % handle, - tag_mark) - tag = self.tag_handles[handle]+suffix - else: - tag = suffix - #if tag == '!': - # raise ParserError("while parsing a node", start_mark, - # "found non-specific tag '!'", tag_mark, - # "Please check 'http://pyyaml.org/wiki/YAMLNonSpecificTag' and share your opinion.") - if start_mark is None: - start_mark = end_mark = self.peek_token().start_mark - event = None - implicit = (tag is None or tag == '!') - if indentless_sequence and self.check_token(BlockEntryToken): - end_mark = self.peek_token().end_mark - event = SequenceStartEvent(anchor, tag, implicit, - start_mark, end_mark) - self.state = self.parse_indentless_sequence_entry - else: - if self.check_token(ScalarToken): - token = self.get_token() - end_mark = token.end_mark - if (token.plain and tag is None) or tag == '!': - implicit = (True, False) - elif tag is None: - implicit = (False, True) - else: - implicit = (False, False) - event = ScalarEvent(anchor, tag, implicit, token.value, - start_mark, end_mark, style=token.style) - self.state = self.states.pop() - elif self.check_token(FlowSequenceStartToken): - end_mark = self.peek_token().end_mark - event = SequenceStartEvent(anchor, tag, implicit, - start_mark, end_mark, flow_style=True) - self.state = self.parse_flow_sequence_first_entry - elif self.check_token(FlowMappingStartToken): - end_mark = self.peek_token().end_mark - event = MappingStartEvent(anchor, tag, implicit, - start_mark, end_mark, flow_style=True) - self.state = self.parse_flow_mapping_first_key - elif block and self.check_token(BlockSequenceStartToken): - end_mark = self.peek_token().start_mark - event = SequenceStartEvent(anchor, tag, implicit, - start_mark, end_mark, flow_style=False) - self.state = self.parse_block_sequence_first_entry - elif block and self.check_token(BlockMappingStartToken): - end_mark = self.peek_token().start_mark - event = MappingStartEvent(anchor, tag, implicit, - start_mark, end_mark, flow_style=False) - self.state = self.parse_block_mapping_first_key - elif anchor is not None or tag is not None: - # Empty scalars are allowed even if a tag or an anchor is - # specified. - event = ScalarEvent(anchor, tag, (implicit, False), '', - start_mark, end_mark) - self.state = self.states.pop() - else: - if block: - node = 'block' - else: - node = 'flow' - token = self.peek_token() - raise ParserError("while parsing a %s node" % node, start_mark, - "expected the node content, but found %r" % token.id, - token.start_mark) - return event - - # block_sequence ::= BLOCK-SEQUENCE-START (BLOCK-ENTRY block_node?)* BLOCK-END - - def parse_block_sequence_first_entry(self): - token = self.get_token() - self.marks.append(token.start_mark) - return self.parse_block_sequence_entry() - - def parse_block_sequence_entry(self): - if self.check_token(BlockEntryToken): - token = self.get_token() - if not self.check_token(BlockEntryToken, BlockEndToken): - self.states.append(self.parse_block_sequence_entry) - return self.parse_block_node() - else: - self.state = self.parse_block_sequence_entry - return self.process_empty_scalar(token.end_mark) - if not self.check_token(BlockEndToken): - token = self.peek_token() - raise ParserError("while parsing a block collection", self.marks[-1], - "expected <block end>, but found %r" % token.id, token.start_mark) - token = self.get_token() - event = SequenceEndEvent(token.start_mark, token.end_mark) - self.state = self.states.pop() - self.marks.pop() - return event - - # indentless_sequence ::= (BLOCK-ENTRY block_node?)+ - - def parse_indentless_sequence_entry(self): - if self.check_token(BlockEntryToken): - token = self.get_token() - if not self.check_token(BlockEntryToken, - KeyToken, ValueToken, BlockEndToken): - self.states.append(self.parse_indentless_sequence_entry) - return self.parse_block_node() - else: - self.state = self.parse_indentless_sequence_entry - return self.process_empty_scalar(token.end_mark) - token = self.peek_token() - event = SequenceEndEvent(token.start_mark, token.start_mark) - self.state = self.states.pop() - return event - - # block_mapping ::= BLOCK-MAPPING_START - # ((KEY block_node_or_indentless_sequence?)? - # (VALUE block_node_or_indentless_sequence?)?)* - # BLOCK-END - - def parse_block_mapping_first_key(self): - token = self.get_token() - self.marks.append(token.start_mark) - return self.parse_block_mapping_key() - - def parse_block_mapping_key(self): - if self.check_token(KeyToken): - token = self.get_token() - if not self.check_token(KeyToken, ValueToken, BlockEndToken): - self.states.append(self.parse_block_mapping_value) - return self.parse_block_node_or_indentless_sequence() - else: - self.state = self.parse_block_mapping_value - return self.process_empty_scalar(token.end_mark) - if not self.check_token(BlockEndToken): - token = self.peek_token() - raise ParserError("while parsing a block mapping", self.marks[-1], - "expected <block end>, but found %r" % token.id, token.start_mark) - token = self.get_token() - event = MappingEndEvent(token.start_mark, token.end_mark) - self.state = self.states.pop() - self.marks.pop() - return event - - def parse_block_mapping_value(self): - if self.check_token(ValueToken): - token = self.get_token() - if not self.check_token(KeyToken, ValueToken, BlockEndToken): - self.states.append(self.parse_block_mapping_key) - return self.parse_block_node_or_indentless_sequence() - else: - self.state = self.parse_block_mapping_key - return self.process_empty_scalar(token.end_mark) - else: - self.state = self.parse_block_mapping_key - token = self.peek_token() - return self.process_empty_scalar(token.start_mark) - - # flow_sequence ::= FLOW-SEQUENCE-START - # (flow_sequence_entry FLOW-ENTRY)* - # flow_sequence_entry? - # FLOW-SEQUENCE-END - # flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? - # - # Note that while production rules for both flow_sequence_entry and - # flow_mapping_entry are equal, their interpretations are different. - # For `flow_sequence_entry`, the part `KEY flow_node? (VALUE flow_node?)?` - # generate an inline mapping (set syntax). - - def parse_flow_sequence_first_entry(self): - token = self.get_token() - self.marks.append(token.start_mark) - return self.parse_flow_sequence_entry(first=True) - - def parse_flow_sequence_entry(self, first=False): - if not self.check_token(FlowSequenceEndToken): - if not first: - if self.check_token(FlowEntryToken): - self.get_token() - else: - token = self.peek_token() - raise ParserError("while parsing a flow sequence", self.marks[-1], - "expected ',' or ']', but got %r" % token.id, token.start_mark) - - if self.check_token(KeyToken): - token = self.peek_token() - event = MappingStartEvent(None, None, True, - token.start_mark, token.end_mark, - flow_style=True) - self.state = self.parse_flow_sequence_entry_mapping_key - return event - elif not self.check_token(FlowSequenceEndToken): - self.states.append(self.parse_flow_sequence_entry) - return self.parse_flow_node() - token = self.get_token() - event = SequenceEndEvent(token.start_mark, token.end_mark) - self.state = self.states.pop() - self.marks.pop() - return event - - def parse_flow_sequence_entry_mapping_key(self): - token = self.get_token() - if not self.check_token(ValueToken, - FlowEntryToken, FlowSequenceEndToken): - self.states.append(self.parse_flow_sequence_entry_mapping_value) - return self.parse_flow_node() - else: - self.state = self.parse_flow_sequence_entry_mapping_value - return self.process_empty_scalar(token.end_mark) - - def parse_flow_sequence_entry_mapping_value(self): - if self.check_token(ValueToken): - token = self.get_token() - if not self.check_token(FlowEntryToken, FlowSequenceEndToken): - self.states.append(self.parse_flow_sequence_entry_mapping_end) - return self.parse_flow_node() - else: - self.state = self.parse_flow_sequence_entry_mapping_end - return self.process_empty_scalar(token.end_mark) - else: - self.state = self.parse_flow_sequence_entry_mapping_end - token = self.peek_token() - return self.process_empty_scalar(token.start_mark) - - def parse_flow_sequence_entry_mapping_end(self): - self.state = self.parse_flow_sequence_entry - token = self.peek_token() - return MappingEndEvent(token.start_mark, token.start_mark) - - # flow_mapping ::= FLOW-MAPPING-START - # (flow_mapping_entry FLOW-ENTRY)* - # flow_mapping_entry? - # FLOW-MAPPING-END - # flow_mapping_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? - - def parse_flow_mapping_first_key(self): - token = self.get_token() - self.marks.append(token.start_mark) - return self.parse_flow_mapping_key(first=True) - - def parse_flow_mapping_key(self, first=False): - if not self.check_token(FlowMappingEndToken): - if not first: - if self.check_token(FlowEntryToken): - self.get_token() - else: - token = self.peek_token() - raise ParserError("while parsing a flow mapping", self.marks[-1], - "expected ',' or '}', but got %r" % token.id, token.start_mark) - if self.check_token(KeyToken): - token = self.get_token() - if not self.check_token(ValueToken, - FlowEntryToken, FlowMappingEndToken): - self.states.append(self.parse_flow_mapping_value) - return self.parse_flow_node() - else: - self.state = self.parse_flow_mapping_value - return self.process_empty_scalar(token.end_mark) - elif not self.check_token(FlowMappingEndToken): - self.states.append(self.parse_flow_mapping_empty_value) - return self.parse_flow_node() - token = self.get_token() - event = MappingEndEvent(token.start_mark, token.end_mark) - self.state = self.states.pop() - self.marks.pop() - return event - - def parse_flow_mapping_value(self): - if self.check_token(ValueToken): - token = self.get_token() - if not self.check_token(FlowEntryToken, FlowMappingEndToken): - self.states.append(self.parse_flow_mapping_key) - return self.parse_flow_node() - else: - self.state = self.parse_flow_mapping_key - return self.process_empty_scalar(token.end_mark) - else: - self.state = self.parse_flow_mapping_key - token = self.peek_token() - return self.process_empty_scalar(token.start_mark) - - def parse_flow_mapping_empty_value(self): - self.state = self.parse_flow_mapping_key - return self.process_empty_scalar(self.peek_token().start_mark) - - def process_empty_scalar(self, mark): - return ScalarEvent(None, None, (True, False), '', mark, mark) - diff --git a/python.d/python_modules/pyyaml3/reader.py b/python.d/python_modules/pyyaml3/reader.py deleted file mode 100644 index f70e920f4..000000000 --- a/python.d/python_modules/pyyaml3/reader.py +++ /dev/null @@ -1,192 +0,0 @@ -# This module contains abstractions for the input stream. You don't have to -# looks further, there are no pretty code. -# -# We define two classes here. -# -# Mark(source, line, column) -# It's just a record and its only use is producing nice error messages. -# Parser does not use it for any other purposes. -# -# Reader(source, data) -# Reader determines the encoding of `data` and converts it to unicode. -# Reader provides the following methods and attributes: -# reader.peek(length=1) - return the next `length` characters -# reader.forward(length=1) - move the current position to `length` characters. -# reader.index - the number of the current character. -# reader.line, stream.column - the line and the column of the current character. - -__all__ = ['Reader', 'ReaderError'] - -from .error import YAMLError, Mark - -import codecs, re - -class ReaderError(YAMLError): - - def __init__(self, name, position, character, encoding, reason): - self.name = name - self.character = character - self.position = position - self.encoding = encoding - self.reason = reason - - def __str__(self): - if isinstance(self.character, bytes): - return "'%s' codec can't decode byte #x%02x: %s\n" \ - " in \"%s\", position %d" \ - % (self.encoding, ord(self.character), self.reason, - self.name, self.position) - else: - return "unacceptable character #x%04x: %s\n" \ - " in \"%s\", position %d" \ - % (self.character, self.reason, - self.name, self.position) - -class Reader(object): - # Reader: - # - determines the data encoding and converts it to a unicode string, - # - checks if characters are in allowed range, - # - adds '\0' to the end. - - # Reader accepts - # - a `bytes` object, - # - a `str` object, - # - a file-like object with its `read` method returning `str`, - # - a file-like object with its `read` method returning `unicode`. - - # Yeah, it's ugly and slow. - - def __init__(self, stream): - self.name = None - self.stream = None - self.stream_pointer = 0 - self.eof = True - self.buffer = '' - self.pointer = 0 - self.raw_buffer = None - self.raw_decode = None - self.encoding = None - self.index = 0 - self.line = 0 - self.column = 0 - if isinstance(stream, str): - self.name = "<unicode string>" - self.check_printable(stream) - self.buffer = stream+'\0' - elif isinstance(stream, bytes): - self.name = "<byte string>" - self.raw_buffer = stream - self.determine_encoding() - else: - self.stream = stream - self.name = getattr(stream, 'name', "<file>") - self.eof = False - self.raw_buffer = None - self.determine_encoding() - - def peek(self, index=0): - try: - return self.buffer[self.pointer+index] - except IndexError: - self.update(index+1) - return self.buffer[self.pointer+index] - - def prefix(self, length=1): - if self.pointer+length >= len(self.buffer): - self.update(length) - return self.buffer[self.pointer:self.pointer+length] - - def forward(self, length=1): - if self.pointer+length+1 >= len(self.buffer): - self.update(length+1) - while length: - ch = self.buffer[self.pointer] - self.pointer += 1 - self.index += 1 - if ch in '\n\x85\u2028\u2029' \ - or (ch == '\r' and self.buffer[self.pointer] != '\n'): - self.line += 1 - self.column = 0 - elif ch != '\uFEFF': - self.column += 1 - length -= 1 - - def get_mark(self): - if self.stream is None: - return Mark(self.name, self.index, self.line, self.column, - self.buffer, self.pointer) - else: - return Mark(self.name, self.index, self.line, self.column, - None, None) - - def determine_encoding(self): - while not self.eof and (self.raw_buffer is None or len(self.raw_buffer) < 2): - self.update_raw() - if isinstance(self.raw_buffer, bytes): - if self.raw_buffer.startswith(codecs.BOM_UTF16_LE): - self.raw_decode = codecs.utf_16_le_decode - self.encoding = 'utf-16-le' - elif self.raw_buffer.startswith(codecs.BOM_UTF16_BE): - self.raw_decode = codecs.utf_16_be_decode - self.encoding = 'utf-16-be' - else: - self.raw_decode = codecs.utf_8_decode - self.encoding = 'utf-8' - self.update(1) - - NON_PRINTABLE = re.compile('[^\x09\x0A\x0D\x20-\x7E\x85\xA0-\uD7FF\uE000-\uFFFD]') - def check_printable(self, data): - match = self.NON_PRINTABLE.search(data) - if match: - character = match.group() - position = self.index+(len(self.buffer)-self.pointer)+match.start() - raise ReaderError(self.name, position, ord(character), - 'unicode', "special characters are not allowed") - - def update(self, length): - if self.raw_buffer is None: - return - self.buffer = self.buffer[self.pointer:] - self.pointer = 0 - while len(self.buffer) < length: - if not self.eof: - self.update_raw() - if self.raw_decode is not None: - try: - data, converted = self.raw_decode(self.raw_buffer, - 'strict', self.eof) - except UnicodeDecodeError as exc: - character = self.raw_buffer[exc.start] - if self.stream is not None: - position = self.stream_pointer-len(self.raw_buffer)+exc.start - else: - position = exc.start - raise ReaderError(self.name, position, character, - exc.encoding, exc.reason) - else: - data = self.raw_buffer - converted = len(data) - self.check_printable(data) - self.buffer += data - self.raw_buffer = self.raw_buffer[converted:] - if self.eof: - self.buffer += '\0' - self.raw_buffer = None - break - - def update_raw(self, size=4096): - data = self.stream.read(size) - if self.raw_buffer is None: - self.raw_buffer = data - else: - self.raw_buffer += data - self.stream_pointer += len(data) - if not data: - self.eof = True - -#try: -# import psyco -# psyco.bind(Reader) -#except ImportError: -# pass - diff --git a/python.d/python_modules/pyyaml3/representer.py b/python.d/python_modules/pyyaml3/representer.py deleted file mode 100644 index 67cd6fd25..000000000 --- a/python.d/python_modules/pyyaml3/representer.py +++ /dev/null @@ -1,374 +0,0 @@ - -__all__ = ['BaseRepresenter', 'SafeRepresenter', 'Representer', - 'RepresenterError'] - -from .error import * -from .nodes import * - -import datetime, sys, copyreg, types, base64 - -class RepresenterError(YAMLError): - pass - -class BaseRepresenter: - - yaml_representers = {} - yaml_multi_representers = {} - - def __init__(self, default_style=None, default_flow_style=None): - self.default_style = default_style - self.default_flow_style = default_flow_style - self.represented_objects = {} - self.object_keeper = [] - self.alias_key = None - - def represent(self, data): - node = self.represent_data(data) - self.serialize(node) - self.represented_objects = {} - self.object_keeper = [] - self.alias_key = None - - def represent_data(self, data): - if self.ignore_aliases(data): - self.alias_key = None - else: - self.alias_key = id(data) - if self.alias_key is not None: - if self.alias_key in self.represented_objects: - node = self.represented_objects[self.alias_key] - #if node is None: - # raise RepresenterError("recursive objects are not allowed: %r" % data) - return node - #self.represented_objects[alias_key] = None - self.object_keeper.append(data) - data_types = type(data).__mro__ - if data_types[0] in self.yaml_representers: - node = self.yaml_representers[data_types[0]](self, data) - else: - for data_type in data_types: - if data_type in self.yaml_multi_representers: - node = self.yaml_multi_representers[data_type](self, data) - break - else: - if None in self.yaml_multi_representers: - node = self.yaml_multi_representers[None](self, data) - elif None in self.yaml_representers: - node = self.yaml_representers[None](self, data) - else: - node = ScalarNode(None, str(data)) - #if alias_key is not None: - # self.represented_objects[alias_key] = node - return node - - @classmethod - def add_representer(cls, data_type, representer): - if not 'yaml_representers' in cls.__dict__: - cls.yaml_representers = cls.yaml_representers.copy() - cls.yaml_representers[data_type] = representer - - @classmethod - def add_multi_representer(cls, data_type, representer): - if not 'yaml_multi_representers' in cls.__dict__: - cls.yaml_multi_representers = cls.yaml_multi_representers.copy() - cls.yaml_multi_representers[data_type] = representer - - def represent_scalar(self, tag, value, style=None): - if style is None: - style = self.default_style - node = ScalarNode(tag, value, style=style) - if self.alias_key is not None: - self.represented_objects[self.alias_key] = node - return node - - def represent_sequence(self, tag, sequence, flow_style=None): - value = [] - node = SequenceNode(tag, value, flow_style=flow_style) - if self.alias_key is not None: - self.represented_objects[self.alias_key] = node - best_style = True - for item in sequence: - node_item = self.represent_data(item) - if not (isinstance(node_item, ScalarNode) and not node_item.style): - best_style = False - value.append(node_item) - if flow_style is None: - if self.default_flow_style is not None: - node.flow_style = self.default_flow_style - else: - node.flow_style = best_style - return node - - def represent_mapping(self, tag, mapping, flow_style=None): - value = [] - node = MappingNode(tag, value, flow_style=flow_style) - if self.alias_key is not None: - self.represented_objects[self.alias_key] = node - best_style = True - if hasattr(mapping, 'items'): - mapping = list(mapping.items()) - try: - mapping = sorted(mapping) - except TypeError: - pass - for item_key, item_value in mapping: - node_key = self.represent_data(item_key) - node_value = self.represent_data(item_value) - if not (isinstance(node_key, ScalarNode) and not node_key.style): - best_style = False - if not (isinstance(node_value, ScalarNode) and not node_value.style): - best_style = False - value.append((node_key, node_value)) - if flow_style is None: - if self.default_flow_style is not None: - node.flow_style = self.default_flow_style - else: - node.flow_style = best_style - return node - - def ignore_aliases(self, data): - return False - -class SafeRepresenter(BaseRepresenter): - - def ignore_aliases(self, data): - if data in [None, ()]: - return True - if isinstance(data, (str, bytes, bool, int, float)): - return True - - def represent_none(self, data): - return self.represent_scalar('tag:yaml.org,2002:null', 'null') - - def represent_str(self, data): - return self.represent_scalar('tag:yaml.org,2002:str', data) - - def represent_binary(self, data): - if hasattr(base64, 'encodebytes'): - data = base64.encodebytes(data).decode('ascii') - else: - data = base64.encodestring(data).decode('ascii') - return self.represent_scalar('tag:yaml.org,2002:binary', data, style='|') - - def represent_bool(self, data): - if data: - value = 'true' - else: - value = 'false' - return self.represent_scalar('tag:yaml.org,2002:bool', value) - - def represent_int(self, data): - return self.represent_scalar('tag:yaml.org,2002:int', str(data)) - - inf_value = 1e300 - while repr(inf_value) != repr(inf_value*inf_value): - inf_value *= inf_value - - def represent_float(self, data): - if data != data or (data == 0.0 and data == 1.0): - value = '.nan' - elif data == self.inf_value: - value = '.inf' - elif data == -self.inf_value: - value = '-.inf' - else: - value = repr(data).lower() - # Note that in some cases `repr(data)` represents a float number - # without the decimal parts. For instance: - # >>> repr(1e17) - # '1e17' - # Unfortunately, this is not a valid float representation according - # to the definition of the `!!float` tag. We fix this by adding - # '.0' before the 'e' symbol. - if '.' not in value and 'e' in value: - value = value.replace('e', '.0e', 1) - return self.represent_scalar('tag:yaml.org,2002:float', value) - - def represent_list(self, data): - #pairs = (len(data) > 0 and isinstance(data, list)) - #if pairs: - # for item in data: - # if not isinstance(item, tuple) or len(item) != 2: - # pairs = False - # break - #if not pairs: - return self.represent_sequence('tag:yaml.org,2002:seq', data) - #value = [] - #for item_key, item_value in data: - # value.append(self.represent_mapping(u'tag:yaml.org,2002:map', - # [(item_key, item_value)])) - #return SequenceNode(u'tag:yaml.org,2002:pairs', value) - - def represent_dict(self, data): - return self.represent_mapping('tag:yaml.org,2002:map', data) - - def represent_set(self, data): - value = {} - for key in data: - value[key] = None - return self.represent_mapping('tag:yaml.org,2002:set', value) - - def represent_date(self, data): - value = data.isoformat() - return self.represent_scalar('tag:yaml.org,2002:timestamp', value) - - def represent_datetime(self, data): - value = data.isoformat(' ') - return self.represent_scalar('tag:yaml.org,2002:timestamp', value) - - def represent_yaml_object(self, tag, data, cls, flow_style=None): - if hasattr(data, '__getstate__'): - state = data.__getstate__() - else: - state = data.__dict__.copy() - return self.represent_mapping(tag, state, flow_style=flow_style) - - def represent_undefined(self, data): - raise RepresenterError("cannot represent an object: %s" % data) - -SafeRepresenter.add_representer(type(None), - SafeRepresenter.represent_none) - -SafeRepresenter.add_representer(str, - SafeRepresenter.represent_str) - -SafeRepresenter.add_representer(bytes, - SafeRepresenter.represent_binary) - -SafeRepresenter.add_representer(bool, - SafeRepresenter.represent_bool) - -SafeRepresenter.add_representer(int, - SafeRepresenter.represent_int) - -SafeRepresenter.add_representer(float, - SafeRepresenter.represent_float) - -SafeRepresenter.add_representer(list, - SafeRepresenter.represent_list) - -SafeRepresenter.add_representer(tuple, - SafeRepresenter.represent_list) - -SafeRepresenter.add_representer(dict, - SafeRepresenter.represent_dict) - -SafeRepresenter.add_representer(set, - SafeRepresenter.represent_set) - -SafeRepresenter.add_representer(datetime.date, - SafeRepresenter.represent_date) - -SafeRepresenter.add_representer(datetime.datetime, - SafeRepresenter.represent_datetime) - -SafeRepresenter.add_representer(None, - SafeRepresenter.represent_undefined) - -class Representer(SafeRepresenter): - - def represent_complex(self, data): - if data.imag == 0.0: - data = '%r' % data.real - elif data.real == 0.0: - data = '%rj' % data.imag - elif data.imag > 0: - data = '%r+%rj' % (data.real, data.imag) - else: - data = '%r%rj' % (data.real, data.imag) - return self.represent_scalar('tag:yaml.org,2002:python/complex', data) - - def represent_tuple(self, data): - return self.represent_sequence('tag:yaml.org,2002:python/tuple', data) - - def represent_name(self, data): - name = '%s.%s' % (data.__module__, data.__name__) - return self.represent_scalar('tag:yaml.org,2002:python/name:'+name, '') - - def represent_module(self, data): - return self.represent_scalar( - 'tag:yaml.org,2002:python/module:'+data.__name__, '') - - def represent_object(self, data): - # We use __reduce__ API to save the data. data.__reduce__ returns - # a tuple of length 2-5: - # (function, args, state, listitems, dictitems) - - # For reconstructing, we calls function(*args), then set its state, - # listitems, and dictitems if they are not None. - - # A special case is when function.__name__ == '__newobj__'. In this - # case we create the object with args[0].__new__(*args). - - # Another special case is when __reduce__ returns a string - we don't - # support it. - - # We produce a !!python/object, !!python/object/new or - # !!python/object/apply node. - - cls = type(data) - if cls in copyreg.dispatch_table: - reduce = copyreg.dispatch_table[cls](data) - elif hasattr(data, '__reduce_ex__'): - reduce = data.__reduce_ex__(2) - elif hasattr(data, '__reduce__'): - reduce = data.__reduce__() - else: - raise RepresenterError("cannot represent object: %r" % data) - reduce = (list(reduce)+[None]*5)[:5] - function, args, state, listitems, dictitems = reduce - args = list(args) - if state is None: - state = {} - if listitems is not None: - listitems = list(listitems) - if dictitems is not None: - dictitems = dict(dictitems) - if function.__name__ == '__newobj__': - function = args[0] - args = args[1:] - tag = 'tag:yaml.org,2002:python/object/new:' - newobj = True - else: - tag = 'tag:yaml.org,2002:python/object/apply:' - newobj = False - function_name = '%s.%s' % (function.__module__, function.__name__) - if not args and not listitems and not dictitems \ - and isinstance(state, dict) and newobj: - return self.represent_mapping( - 'tag:yaml.org,2002:python/object:'+function_name, state) - if not listitems and not dictitems \ - and isinstance(state, dict) and not state: - return self.represent_sequence(tag+function_name, args) - value = {} - if args: - value['args'] = args - if state or not isinstance(state, dict): - value['state'] = state - if listitems: - value['listitems'] = listitems - if dictitems: - value['dictitems'] = dictitems - return self.represent_mapping(tag+function_name, value) - -Representer.add_representer(complex, - Representer.represent_complex) - -Representer.add_representer(tuple, - Representer.represent_tuple) - -Representer.add_representer(type, - Representer.represent_name) - -Representer.add_representer(types.FunctionType, - Representer.represent_name) - -Representer.add_representer(types.BuiltinFunctionType, - Representer.represent_name) - -Representer.add_representer(types.ModuleType, - Representer.represent_module) - -Representer.add_multi_representer(object, - Representer.represent_object) - diff --git a/python.d/python_modules/pyyaml3/resolver.py b/python.d/python_modules/pyyaml3/resolver.py deleted file mode 100644 index 0eece2582..000000000 --- a/python.d/python_modules/pyyaml3/resolver.py +++ /dev/null @@ -1,224 +0,0 @@ - -__all__ = ['BaseResolver', 'Resolver'] - -from .error import * -from .nodes import * - -import re - -class ResolverError(YAMLError): - pass - -class BaseResolver: - - DEFAULT_SCALAR_TAG = 'tag:yaml.org,2002:str' - DEFAULT_SEQUENCE_TAG = 'tag:yaml.org,2002:seq' - DEFAULT_MAPPING_TAG = 'tag:yaml.org,2002:map' - - yaml_implicit_resolvers = {} - yaml_path_resolvers = {} - - def __init__(self): - self.resolver_exact_paths = [] - self.resolver_prefix_paths = [] - - @classmethod - def add_implicit_resolver(cls, tag, regexp, first): - if not 'yaml_implicit_resolvers' in cls.__dict__: - cls.yaml_implicit_resolvers = cls.yaml_implicit_resolvers.copy() - if first is None: - first = [None] - for ch in first: - cls.yaml_implicit_resolvers.setdefault(ch, []).append((tag, regexp)) - - @classmethod - def add_path_resolver(cls, tag, path, kind=None): - # Note: `add_path_resolver` is experimental. The API could be changed. - # `new_path` is a pattern that is matched against the path from the - # root to the node that is being considered. `node_path` elements are - # tuples `(node_check, index_check)`. `node_check` is a node class: - # `ScalarNode`, `SequenceNode`, `MappingNode` or `None`. `None` - # matches any kind of a node. `index_check` could be `None`, a boolean - # value, a string value, or a number. `None` and `False` match against - # any _value_ of sequence and mapping nodes. `True` matches against - # any _key_ of a mapping node. A string `index_check` matches against - # a mapping value that corresponds to a scalar key which content is - # equal to the `index_check` value. An integer `index_check` matches - # against a sequence value with the index equal to `index_check`. - if not 'yaml_path_resolvers' in cls.__dict__: - cls.yaml_path_resolvers = cls.yaml_path_resolvers.copy() - new_path = [] - for element in path: - if isinstance(element, (list, tuple)): - if len(element) == 2: - node_check, index_check = element - elif len(element) == 1: - node_check = element[0] - index_check = True - else: - raise ResolverError("Invalid path element: %s" % element) - else: - node_check = None - index_check = element - if node_check is str: - node_check = ScalarNode - elif node_check is list: - node_check = SequenceNode - elif node_check is dict: - node_check = MappingNode - elif node_check not in [ScalarNode, SequenceNode, MappingNode] \ - and not isinstance(node_check, str) \ - and node_check is not None: - raise ResolverError("Invalid node checker: %s" % node_check) - if not isinstance(index_check, (str, int)) \ - and index_check is not None: - raise ResolverError("Invalid index checker: %s" % index_check) - new_path.append((node_check, index_check)) - if kind is str: - kind = ScalarNode - elif kind is list: - kind = SequenceNode - elif kind is dict: - kind = MappingNode - elif kind not in [ScalarNode, SequenceNode, MappingNode] \ - and kind is not None: - raise ResolverError("Invalid node kind: %s" % kind) - cls.yaml_path_resolvers[tuple(new_path), kind] = tag - - def descend_resolver(self, current_node, current_index): - if not self.yaml_path_resolvers: - return - exact_paths = {} - prefix_paths = [] - if current_node: - depth = len(self.resolver_prefix_paths) - for path, kind in self.resolver_prefix_paths[-1]: - if self.check_resolver_prefix(depth, path, kind, - current_node, current_index): - if len(path) > depth: - prefix_paths.append((path, kind)) - else: - exact_paths[kind] = self.yaml_path_resolvers[path, kind] - else: - for path, kind in self.yaml_path_resolvers: - if not path: - exact_paths[kind] = self.yaml_path_resolvers[path, kind] - else: - prefix_paths.append((path, kind)) - self.resolver_exact_paths.append(exact_paths) - self.resolver_prefix_paths.append(prefix_paths) - - def ascend_resolver(self): - if not self.yaml_path_resolvers: - return - self.resolver_exact_paths.pop() - self.resolver_prefix_paths.pop() - - def check_resolver_prefix(self, depth, path, kind, - current_node, current_index): - node_check, index_check = path[depth-1] - if isinstance(node_check, str): - if current_node.tag != node_check: - return - elif node_check is not None: - if not isinstance(current_node, node_check): - return - if index_check is True and current_index is not None: - return - if (index_check is False or index_check is None) \ - and current_index is None: - return - if isinstance(index_check, str): - if not (isinstance(current_index, ScalarNode) - and index_check == current_index.value): - return - elif isinstance(index_check, int) and not isinstance(index_check, bool): - if index_check != current_index: - return - return True - - def resolve(self, kind, value, implicit): - if kind is ScalarNode and implicit[0]: - if value == '': - resolvers = self.yaml_implicit_resolvers.get('', []) - else: - resolvers = self.yaml_implicit_resolvers.get(value[0], []) - resolvers += self.yaml_implicit_resolvers.get(None, []) - for tag, regexp in resolvers: - if regexp.match(value): - return tag - implicit = implicit[1] - if self.yaml_path_resolvers: - exact_paths = self.resolver_exact_paths[-1] - if kind in exact_paths: - return exact_paths[kind] - if None in exact_paths: - return exact_paths[None] - if kind is ScalarNode: - return self.DEFAULT_SCALAR_TAG - elif kind is SequenceNode: - return self.DEFAULT_SEQUENCE_TAG - elif kind is MappingNode: - return self.DEFAULT_MAPPING_TAG - -class Resolver(BaseResolver): - pass - -Resolver.add_implicit_resolver( - 'tag:yaml.org,2002:bool', - re.compile(r'''^(?:yes|Yes|YES|no|No|NO - |true|True|TRUE|false|False|FALSE - |on|On|ON|off|Off|OFF)$''', re.X), - list('yYnNtTfFoO')) - -Resolver.add_implicit_resolver( - 'tag:yaml.org,2002:float', - re.compile(r'''^(?:[-+]?(?:[0-9][0-9_]*)\.[0-9_]*(?:[eE][-+][0-9]+)? - |\.[0-9_]+(?:[eE][-+][0-9]+)? - |[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+\.[0-9_]* - |[-+]?\.(?:inf|Inf|INF) - |\.(?:nan|NaN|NAN))$''', re.X), - list('-+0123456789.')) - -Resolver.add_implicit_resolver( - 'tag:yaml.org,2002:int', - re.compile(r'''^(?:[-+]?0b[0-1_]+ - |[-+]?0[0-7_]+ - |[-+]?(?:0|[1-9][0-9_]*) - |[-+]?0x[0-9a-fA-F_]+ - |[-+]?[1-9][0-9_]*(?::[0-5]?[0-9])+)$''', re.X), - list('-+0123456789')) - -Resolver.add_implicit_resolver( - 'tag:yaml.org,2002:merge', - re.compile(r'^(?:<<)$'), - ['<']) - -Resolver.add_implicit_resolver( - 'tag:yaml.org,2002:null', - re.compile(r'''^(?: ~ - |null|Null|NULL - | )$''', re.X), - ['~', 'n', 'N', '']) - -Resolver.add_implicit_resolver( - 'tag:yaml.org,2002:timestamp', - re.compile(r'''^(?:[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9] - |[0-9][0-9][0-9][0-9] -[0-9][0-9]? -[0-9][0-9]? - (?:[Tt]|[ \t]+)[0-9][0-9]? - :[0-9][0-9] :[0-9][0-9] (?:\.[0-9]*)? - (?:[ \t]*(?:Z|[-+][0-9][0-9]?(?::[0-9][0-9])?))?)$''', re.X), - list('0123456789')) - -Resolver.add_implicit_resolver( - 'tag:yaml.org,2002:value', - re.compile(r'^(?:=)$'), - ['=']) - -# The following resolver is only for documentation purposes. It cannot work -# because plain scalars cannot start with '!', '&', or '*'. -Resolver.add_implicit_resolver( - 'tag:yaml.org,2002:yaml', - re.compile(r'^(?:!|&|\*)$'), - list('!&*')) - diff --git a/python.d/python_modules/pyyaml3/scanner.py b/python.d/python_modules/pyyaml3/scanner.py deleted file mode 100644 index 494d975ba..000000000 --- a/python.d/python_modules/pyyaml3/scanner.py +++ /dev/null @@ -1,1448 +0,0 @@ - -# Scanner produces tokens of the following types: -# STREAM-START -# STREAM-END -# DIRECTIVE(name, value) -# DOCUMENT-START -# DOCUMENT-END -# BLOCK-SEQUENCE-START -# BLOCK-MAPPING-START -# BLOCK-END -# FLOW-SEQUENCE-START -# FLOW-MAPPING-START -# FLOW-SEQUENCE-END -# FLOW-MAPPING-END -# BLOCK-ENTRY -# FLOW-ENTRY -# KEY -# VALUE -# ALIAS(value) -# ANCHOR(value) -# TAG(value) -# SCALAR(value, plain, style) -# -# Read comments in the Scanner code for more details. -# - -__all__ = ['Scanner', 'ScannerError'] - -from .error import MarkedYAMLError -from .tokens import * - -class ScannerError(MarkedYAMLError): - pass - -class SimpleKey: - # See below simple keys treatment. - - def __init__(self, token_number, required, index, line, column, mark): - self.token_number = token_number - self.required = required - self.index = index - self.line = line - self.column = column - self.mark = mark - -class Scanner: - - def __init__(self): - """Initialize the scanner.""" - # It is assumed that Scanner and Reader will have a common descendant. - # Reader do the dirty work of checking for BOM and converting the - # input data to Unicode. It also adds NUL to the end. - # - # Reader supports the following methods - # self.peek(i=0) # peek the next i-th character - # self.prefix(l=1) # peek the next l characters - # self.forward(l=1) # read the next l characters and move the pointer. - - # Had we reached the end of the stream? - self.done = False - - # The number of unclosed '{' and '['. `flow_level == 0` means block - # context. - self.flow_level = 0 - - # List of processed tokens that are not yet emitted. - self.tokens = [] - - # Add the STREAM-START token. - self.fetch_stream_start() - - # Number of tokens that were emitted through the `get_token` method. - self.tokens_taken = 0 - - # The current indentation level. - self.indent = -1 - - # Past indentation levels. - self.indents = [] - - # Variables related to simple keys treatment. - - # A simple key is a key that is not denoted by the '?' indicator. - # Example of simple keys: - # --- - # block simple key: value - # ? not a simple key: - # : { flow simple key: value } - # We emit the KEY token before all keys, so when we find a potential - # simple key, we try to locate the corresponding ':' indicator. - # Simple keys should be limited to a single line and 1024 characters. - - # Can a simple key start at the current position? A simple key may - # start: - # - at the beginning of the line, not counting indentation spaces - # (in block context), - # - after '{', '[', ',' (in the flow context), - # - after '?', ':', '-' (in the block context). - # In the block context, this flag also signifies if a block collection - # may start at the current position. - self.allow_simple_key = True - - # Keep track of possible simple keys. This is a dictionary. The key - # is `flow_level`; there can be no more that one possible simple key - # for each level. The value is a SimpleKey record: - # (token_number, required, index, line, column, mark) - # A simple key may start with ALIAS, ANCHOR, TAG, SCALAR(flow), - # '[', or '{' tokens. - self.possible_simple_keys = {} - - # Public methods. - - def check_token(self, *choices): - # Check if the next token is one of the given types. - while self.need_more_tokens(): - self.fetch_more_tokens() - if self.tokens: - if not choices: - return True - for choice in choices: - if isinstance(self.tokens[0], choice): - return True - return False - - def peek_token(self): - # Return the next token, but do not delete if from the queue. - while self.need_more_tokens(): - self.fetch_more_tokens() - if self.tokens: - return self.tokens[0] - - def get_token(self): - # Return the next token. - while self.need_more_tokens(): - self.fetch_more_tokens() - if self.tokens: - self.tokens_taken += 1 - return self.tokens.pop(0) - - # Private methods. - - def need_more_tokens(self): - if self.done: - return False - if not self.tokens: - return True - # The current token may be a potential simple key, so we - # need to look further. - self.stale_possible_simple_keys() - if self.next_possible_simple_key() == self.tokens_taken: - return True - - def fetch_more_tokens(self): - - # Eat whitespaces and comments until we reach the next token. - self.scan_to_next_token() - - # Remove obsolete possible simple keys. - self.stale_possible_simple_keys() - - # Compare the current indentation and column. It may add some tokens - # and decrease the current indentation level. - self.unwind_indent(self.column) - - # Peek the next character. - ch = self.peek() - - # Is it the end of stream? - if ch == '\0': - return self.fetch_stream_end() - - # Is it a directive? - if ch == '%' and self.check_directive(): - return self.fetch_directive() - - # Is it the document start? - if ch == '-' and self.check_document_start(): - return self.fetch_document_start() - - # Is it the document end? - if ch == '.' and self.check_document_end(): - return self.fetch_document_end() - - # TODO: support for BOM within a stream. - #if ch == '\uFEFF': - # return self.fetch_bom() <-- issue BOMToken - - # Note: the order of the following checks is NOT significant. - - # Is it the flow sequence start indicator? - if ch == '[': - return self.fetch_flow_sequence_start() - - # Is it the flow mapping start indicator? - if ch == '{': - return self.fetch_flow_mapping_start() - - # Is it the flow sequence end indicator? - if ch == ']': - return self.fetch_flow_sequence_end() - - # Is it the flow mapping end indicator? - if ch == '}': - return self.fetch_flow_mapping_end() - - # Is it the flow entry indicator? - if ch == ',': - return self.fetch_flow_entry() - - # Is it the block entry indicator? - if ch == '-' and self.check_block_entry(): - return self.fetch_block_entry() - - # Is it the key indicator? - if ch == '?' and self.check_key(): - return self.fetch_key() - - # Is it the value indicator? - if ch == ':' and self.check_value(): - return self.fetch_value() - - # Is it an alias? - if ch == '*': - return self.fetch_alias() - - # Is it an anchor? - if ch == '&': - return self.fetch_anchor() - - # Is it a tag? - if ch == '!': - return self.fetch_tag() - - # Is it a literal scalar? - if ch == '|' and not self.flow_level: - return self.fetch_literal() - - # Is it a folded scalar? - if ch == '>' and not self.flow_level: - return self.fetch_folded() - - # Is it a single quoted scalar? - if ch == '\'': - return self.fetch_single() - - # Is it a double quoted scalar? - if ch == '\"': - return self.fetch_double() - - # It must be a plain scalar then. - if self.check_plain(): - return self.fetch_plain() - - # No? It's an error. Let's produce a nice error message. - raise ScannerError("while scanning for the next token", None, - "found character %r that cannot start any token" % ch, - self.get_mark()) - - # Simple keys treatment. - - def next_possible_simple_key(self): - # Return the number of the nearest possible simple key. Actually we - # don't need to loop through the whole dictionary. We may replace it - # with the following code: - # if not self.possible_simple_keys: - # return None - # return self.possible_simple_keys[ - # min(self.possible_simple_keys.keys())].token_number - min_token_number = None - for level in self.possible_simple_keys: - key = self.possible_simple_keys[level] - if min_token_number is None or key.token_number < min_token_number: - min_token_number = key.token_number - return min_token_number - - def stale_possible_simple_keys(self): - # Remove entries that are no longer possible simple keys. According to - # the YAML specification, simple keys - # - should be limited to a single line, - # - should be no longer than 1024 characters. - # Disabling this procedure will allow simple keys of any length and - # height (may cause problems if indentation is broken though). - for level in list(self.possible_simple_keys): - key = self.possible_simple_keys[level] - if key.line != self.line \ - or self.index-key.index > 1024: - if key.required: - raise ScannerError("while scanning a simple key", key.mark, - "could not found expected ':'", self.get_mark()) - del self.possible_simple_keys[level] - - def save_possible_simple_key(self): - # The next token may start a simple key. We check if it's possible - # and save its position. This function is called for - # ALIAS, ANCHOR, TAG, SCALAR(flow), '[', and '{'. - - # Check if a simple key is required at the current position. - required = not self.flow_level and self.indent == self.column - - # A simple key is required only if it is the first token in the current - # line. Therefore it is always allowed. - assert self.allow_simple_key or not required - - # The next token might be a simple key. Let's save it's number and - # position. - if self.allow_simple_key: - self.remove_possible_simple_key() - token_number = self.tokens_taken+len(self.tokens) - key = SimpleKey(token_number, required, - self.index, self.line, self.column, self.get_mark()) - self.possible_simple_keys[self.flow_level] = key - - def remove_possible_simple_key(self): - # Remove the saved possible key position at the current flow level. - if self.flow_level in self.possible_simple_keys: - key = self.possible_simple_keys[self.flow_level] - - if key.required: - raise ScannerError("while scanning a simple key", key.mark, - "could not found expected ':'", self.get_mark()) - - del self.possible_simple_keys[self.flow_level] - - # Indentation functions. - - def unwind_indent(self, column): - - ## In flow context, tokens should respect indentation. - ## Actually the condition should be `self.indent >= column` according to - ## the spec. But this condition will prohibit intuitively correct - ## constructions such as - ## key : { - ## } - #if self.flow_level and self.indent > column: - # raise ScannerError(None, None, - # "invalid intendation or unclosed '[' or '{'", - # self.get_mark()) - - # In the flow context, indentation is ignored. We make the scanner less - # restrictive then specification requires. - if self.flow_level: - return - - # In block context, we may need to issue the BLOCK-END tokens. - while self.indent > column: - mark = self.get_mark() - self.indent = self.indents.pop() - self.tokens.append(BlockEndToken(mark, mark)) - - def add_indent(self, column): - # Check if we need to increase indentation. - if self.indent < column: - self.indents.append(self.indent) - self.indent = column - return True - return False - - # Fetchers. - - def fetch_stream_start(self): - # We always add STREAM-START as the first token and STREAM-END as the - # last token. - - # Read the token. - mark = self.get_mark() - - # Add STREAM-START. - self.tokens.append(StreamStartToken(mark, mark, - encoding=self.encoding)) - - - def fetch_stream_end(self): - - # Set the current intendation to -1. - self.unwind_indent(-1) - - # Reset simple keys. - self.remove_possible_simple_key() - self.allow_simple_key = False - self.possible_simple_keys = {} - - # Read the token. - mark = self.get_mark() - - # Add STREAM-END. - self.tokens.append(StreamEndToken(mark, mark)) - - # The steam is finished. - self.done = True - - def fetch_directive(self): - - # Set the current intendation to -1. - self.unwind_indent(-1) - - # Reset simple keys. - self.remove_possible_simple_key() - self.allow_simple_key = False - - # Scan and add DIRECTIVE. - self.tokens.append(self.scan_directive()) - - def fetch_document_start(self): - self.fetch_document_indicator(DocumentStartToken) - - def fetch_document_end(self): - self.fetch_document_indicator(DocumentEndToken) - - def fetch_document_indicator(self, TokenClass): - - # Set the current intendation to -1. - self.unwind_indent(-1) - - # Reset simple keys. Note that there could not be a block collection - # after '---'. - self.remove_possible_simple_key() - self.allow_simple_key = False - - # Add DOCUMENT-START or DOCUMENT-END. - start_mark = self.get_mark() - self.forward(3) - end_mark = self.get_mark() - self.tokens.append(TokenClass(start_mark, end_mark)) - - def fetch_flow_sequence_start(self): - self.fetch_flow_collection_start(FlowSequenceStartToken) - - def fetch_flow_mapping_start(self): - self.fetch_flow_collection_start(FlowMappingStartToken) - - def fetch_flow_collection_start(self, TokenClass): - - # '[' and '{' may start a simple key. - self.save_possible_simple_key() - - # Increase the flow level. - self.flow_level += 1 - - # Simple keys are allowed after '[' and '{'. - self.allow_simple_key = True - - # Add FLOW-SEQUENCE-START or FLOW-MAPPING-START. - start_mark = self.get_mark() - self.forward() - end_mark = self.get_mark() - self.tokens.append(TokenClass(start_mark, end_mark)) - - def fetch_flow_sequence_end(self): - self.fetch_flow_collection_end(FlowSequenceEndToken) - - def fetch_flow_mapping_end(self): - self.fetch_flow_collection_end(FlowMappingEndToken) - - def fetch_flow_collection_end(self, TokenClass): - - # Reset possible simple key on the current level. - self.remove_possible_simple_key() - - # Decrease the flow level. - self.flow_level -= 1 - - # No simple keys after ']' or '}'. - self.allow_simple_key = False - - # Add FLOW-SEQUENCE-END or FLOW-MAPPING-END. - start_mark = self.get_mark() - self.forward() - end_mark = self.get_mark() - self.tokens.append(TokenClass(start_mark, end_mark)) - - def fetch_flow_entry(self): - - # Simple keys are allowed after ','. - self.allow_simple_key = True - - # Reset possible simple key on the current level. - self.remove_possible_simple_key() - - # Add FLOW-ENTRY. - start_mark = self.get_mark() - self.forward() - end_mark = self.get_mark() - self.tokens.append(FlowEntryToken(start_mark, end_mark)) - - def fetch_block_entry(self): - - # Block context needs additional checks. - if not self.flow_level: - - # Are we allowed to start a new entry? - if not self.allow_simple_key: - raise ScannerError(None, None, - "sequence entries are not allowed here", - self.get_mark()) - - # We may need to add BLOCK-SEQUENCE-START. - if self.add_indent(self.column): - mark = self.get_mark() - self.tokens.append(BlockSequenceStartToken(mark, mark)) - - # It's an error for the block entry to occur in the flow context, - # but we let the parser detect this. - else: - pass - - # Simple keys are allowed after '-'. - self.allow_simple_key = True - - # Reset possible simple key on the current level. - self.remove_possible_simple_key() - - # Add BLOCK-ENTRY. - start_mark = self.get_mark() - self.forward() - end_mark = self.get_mark() - self.tokens.append(BlockEntryToken(start_mark, end_mark)) - - def fetch_key(self): - - # Block context needs additional checks. - if not self.flow_level: - - # Are we allowed to start a key (not nessesary a simple)? - if not self.allow_simple_key: - raise ScannerError(None, None, - "mapping keys are not allowed here", - self.get_mark()) - - # We may need to add BLOCK-MAPPING-START. - if self.add_indent(self.column): - mark = self.get_mark() - self.tokens.append(BlockMappingStartToken(mark, mark)) - - # Simple keys are allowed after '?' in the block context. - self.allow_simple_key = not self.flow_level - - # Reset possible simple key on the current level. - self.remove_possible_simple_key() - - # Add KEY. - start_mark = self.get_mark() - self.forward() - end_mark = self.get_mark() - self.tokens.append(KeyToken(start_mark, end_mark)) - - def fetch_value(self): - - # Do we determine a simple key? - if self.flow_level in self.possible_simple_keys: - - # Add KEY. - key = self.possible_simple_keys[self.flow_level] - del self.possible_simple_keys[self.flow_level] - self.tokens.insert(key.token_number-self.tokens_taken, - KeyToken(key.mark, key.mark)) - - # If this key starts a new block mapping, we need to add - # BLOCK-MAPPING-START. - if not self.flow_level: - if self.add_indent(key.column): - self.tokens.insert(key.token_number-self.tokens_taken, - BlockMappingStartToken(key.mark, key.mark)) - - # There cannot be two simple keys one after another. - self.allow_simple_key = False - - # It must be a part of a complex key. - else: - - # Block context needs additional checks. - # (Do we really need them? They will be catched by the parser - # anyway.) - if not self.flow_level: - - # We are allowed to start a complex value if and only if - # we can start a simple key. - if not self.allow_simple_key: - raise ScannerError(None, None, - "mapping values are not allowed here", - self.get_mark()) - - # If this value starts a new block mapping, we need to add - # BLOCK-MAPPING-START. It will be detected as an error later by - # the parser. - if not self.flow_level: - if self.add_indent(self.column): - mark = self.get_mark() - self.tokens.append(BlockMappingStartToken(mark, mark)) - - # Simple keys are allowed after ':' in the block context. - self.allow_simple_key = not self.flow_level - - # Reset possible simple key on the current level. - self.remove_possible_simple_key() - - # Add VALUE. - start_mark = self.get_mark() - self.forward() - end_mark = self.get_mark() - self.tokens.append(ValueToken(start_mark, end_mark)) - - def fetch_alias(self): - - # ALIAS could be a simple key. - self.save_possible_simple_key() - - # No simple keys after ALIAS. - self.allow_simple_key = False - - # Scan and add ALIAS. - self.tokens.append(self.scan_anchor(AliasToken)) - - def fetch_anchor(self): - - # ANCHOR could start a simple key. - self.save_possible_simple_key() - - # No simple keys after ANCHOR. - self.allow_simple_key = False - - # Scan and add ANCHOR. - self.tokens.append(self.scan_anchor(AnchorToken)) - - def fetch_tag(self): - - # TAG could start a simple key. - self.save_possible_simple_key() - - # No simple keys after TAG. - self.allow_simple_key = False - - # Scan and add TAG. - self.tokens.append(self.scan_tag()) - - def fetch_literal(self): - self.fetch_block_scalar(style='|') - - def fetch_folded(self): - self.fetch_block_scalar(style='>') - - def fetch_block_scalar(self, style): - - # A simple key may follow a block scalar. - self.allow_simple_key = True - - # Reset possible simple key on the current level. - self.remove_possible_simple_key() - - # Scan and add SCALAR. - self.tokens.append(self.scan_block_scalar(style)) - - def fetch_single(self): - self.fetch_flow_scalar(style='\'') - - def fetch_double(self): - self.fetch_flow_scalar(style='"') - - def fetch_flow_scalar(self, style): - - # A flow scalar could be a simple key. - self.save_possible_simple_key() - - # No simple keys after flow scalars. - self.allow_simple_key = False - - # Scan and add SCALAR. - self.tokens.append(self.scan_flow_scalar(style)) - - def fetch_plain(self): - - # A plain scalar could be a simple key. - self.save_possible_simple_key() - - # No simple keys after plain scalars. But note that `scan_plain` will - # change this flag if the scan is finished at the beginning of the - # line. - self.allow_simple_key = False - - # Scan and add SCALAR. May change `allow_simple_key`. - self.tokens.append(self.scan_plain()) - - # Checkers. - - def check_directive(self): - - # DIRECTIVE: ^ '%' ... - # The '%' indicator is already checked. - if self.column == 0: - return True - - def check_document_start(self): - - # DOCUMENT-START: ^ '---' (' '|'\n') - if self.column == 0: - if self.prefix(3) == '---' \ - and self.peek(3) in '\0 \t\r\n\x85\u2028\u2029': - return True - - def check_document_end(self): - - # DOCUMENT-END: ^ '...' (' '|'\n') - if self.column == 0: - if self.prefix(3) == '...' \ - and self.peek(3) in '\0 \t\r\n\x85\u2028\u2029': - return True - - def check_block_entry(self): - - # BLOCK-ENTRY: '-' (' '|'\n') - return self.peek(1) in '\0 \t\r\n\x85\u2028\u2029' - - def check_key(self): - - # KEY(flow context): '?' - if self.flow_level: - return True - - # KEY(block context): '?' (' '|'\n') - else: - return self.peek(1) in '\0 \t\r\n\x85\u2028\u2029' - - def check_value(self): - - # VALUE(flow context): ':' - if self.flow_level: - return True - - # VALUE(block context): ':' (' '|'\n') - else: - return self.peek(1) in '\0 \t\r\n\x85\u2028\u2029' - - def check_plain(self): - - # A plain scalar may start with any non-space character except: - # '-', '?', ':', ',', '[', ']', '{', '}', - # '#', '&', '*', '!', '|', '>', '\'', '\"', - # '%', '@', '`'. - # - # It may also start with - # '-', '?', ':' - # if it is followed by a non-space character. - # - # Note that we limit the last rule to the block context (except the - # '-' character) because we want the flow context to be space - # independent. - ch = self.peek() - return ch not in '\0 \t\r\n\x85\u2028\u2029-?:,[]{}#&*!|>\'\"%@`' \ - or (self.peek(1) not in '\0 \t\r\n\x85\u2028\u2029' - and (ch == '-' or (not self.flow_level and ch in '?:'))) - - # Scanners. - - def scan_to_next_token(self): - # We ignore spaces, line breaks and comments. - # If we find a line break in the block context, we set the flag - # `allow_simple_key` on. - # The byte order mark is stripped if it's the first character in the - # stream. We do not yet support BOM inside the stream as the - # specification requires. Any such mark will be considered as a part - # of the document. - # - # TODO: We need to make tab handling rules more sane. A good rule is - # Tabs cannot precede tokens - # BLOCK-SEQUENCE-START, BLOCK-MAPPING-START, BLOCK-END, - # KEY(block), VALUE(block), BLOCK-ENTRY - # So the checking code is - # if <TAB>: - # self.allow_simple_keys = False - # We also need to add the check for `allow_simple_keys == True` to - # `unwind_indent` before issuing BLOCK-END. - # Scanners for block, flow, and plain scalars need to be modified. - - if self.index == 0 and self.peek() == '\uFEFF': - self.forward() - found = False - while not found: - while self.peek() == ' ': - self.forward() - if self.peek() == '#': - while self.peek() not in '\0\r\n\x85\u2028\u2029': - self.forward() - if self.scan_line_break(): - if not self.flow_level: - self.allow_simple_key = True - else: - found = True - - def scan_directive(self): - # See the specification for details. - start_mark = self.get_mark() - self.forward() - name = self.scan_directive_name(start_mark) - value = None - if name == 'YAML': - value = self.scan_yaml_directive_value(start_mark) - end_mark = self.get_mark() - elif name == 'TAG': - value = self.scan_tag_directive_value(start_mark) - end_mark = self.get_mark() - else: - end_mark = self.get_mark() - while self.peek() not in '\0\r\n\x85\u2028\u2029': - self.forward() - self.scan_directive_ignored_line(start_mark) - return DirectiveToken(name, value, start_mark, end_mark) - - def scan_directive_name(self, start_mark): - # See the specification for details. - length = 0 - ch = self.peek(length) - while '0' <= ch <= '9' or 'A' <= ch <= 'Z' or 'a' <= ch <= 'z' \ - or ch in '-_': - length += 1 - ch = self.peek(length) - if not length: - raise ScannerError("while scanning a directive", start_mark, - "expected alphabetic or numeric character, but found %r" - % ch, self.get_mark()) - value = self.prefix(length) - self.forward(length) - ch = self.peek() - if ch not in '\0 \r\n\x85\u2028\u2029': - raise ScannerError("while scanning a directive", start_mark, - "expected alphabetic or numeric character, but found %r" - % ch, self.get_mark()) - return value - - def scan_yaml_directive_value(self, start_mark): - # See the specification for details. - while self.peek() == ' ': - self.forward() - major = self.scan_yaml_directive_number(start_mark) - if self.peek() != '.': - raise ScannerError("while scanning a directive", start_mark, - "expected a digit or '.', but found %r" % self.peek(), - self.get_mark()) - self.forward() - minor = self.scan_yaml_directive_number(start_mark) - if self.peek() not in '\0 \r\n\x85\u2028\u2029': - raise ScannerError("while scanning a directive", start_mark, - "expected a digit or ' ', but found %r" % self.peek(), - self.get_mark()) - return (major, minor) - - def scan_yaml_directive_number(self, start_mark): - # See the specification for details. - ch = self.peek() - if not ('0' <= ch <= '9'): - raise ScannerError("while scanning a directive", start_mark, - "expected a digit, but found %r" % ch, self.get_mark()) - length = 0 - while '0' <= self.peek(length) <= '9': - length += 1 - value = int(self.prefix(length)) - self.forward(length) - return value - - def scan_tag_directive_value(self, start_mark): - # See the specification for details. - while self.peek() == ' ': - self.forward() - handle = self.scan_tag_directive_handle(start_mark) - while self.peek() == ' ': - self.forward() - prefix = self.scan_tag_directive_prefix(start_mark) - return (handle, prefix) - - def scan_tag_directive_handle(self, start_mark): - # See the specification for details. - value = self.scan_tag_handle('directive', start_mark) - ch = self.peek() - if ch != ' ': - raise ScannerError("while scanning a directive", start_mark, - "expected ' ', but found %r" % ch, self.get_mark()) - return value - - def scan_tag_directive_prefix(self, start_mark): - # See the specification for details. - value = self.scan_tag_uri('directive', start_mark) - ch = self.peek() - if ch not in '\0 \r\n\x85\u2028\u2029': - raise ScannerError("while scanning a directive", start_mark, - "expected ' ', but found %r" % ch, self.get_mark()) - return value - - def scan_directive_ignored_line(self, start_mark): - # See the specification for details. - while self.peek() == ' ': - self.forward() - if self.peek() == '#': - while self.peek() not in '\0\r\n\x85\u2028\u2029': - self.forward() - ch = self.peek() - if ch not in '\0\r\n\x85\u2028\u2029': - raise ScannerError("while scanning a directive", start_mark, - "expected a comment or a line break, but found %r" - % ch, self.get_mark()) - self.scan_line_break() - - def scan_anchor(self, TokenClass): - # The specification does not restrict characters for anchors and - # aliases. This may lead to problems, for instance, the document: - # [ *alias, value ] - # can be interpteted in two ways, as - # [ "value" ] - # and - # [ *alias , "value" ] - # Therefore we restrict aliases to numbers and ASCII letters. - start_mark = self.get_mark() - indicator = self.peek() - if indicator == '*': - name = 'alias' - else: - name = 'anchor' - self.forward() - length = 0 - ch = self.peek(length) - while '0' <= ch <= '9' or 'A' <= ch <= 'Z' or 'a' <= ch <= 'z' \ - or ch in '-_': - length += 1 - ch = self.peek(length) - if not length: - raise ScannerError("while scanning an %s" % name, start_mark, - "expected alphabetic or numeric character, but found %r" - % ch, self.get_mark()) - value = self.prefix(length) - self.forward(length) - ch = self.peek() - if ch not in '\0 \t\r\n\x85\u2028\u2029?:,]}%@`': - raise ScannerError("while scanning an %s" % name, start_mark, - "expected alphabetic or numeric character, but found %r" - % ch, self.get_mark()) - end_mark = self.get_mark() - return TokenClass(value, start_mark, end_mark) - - def scan_tag(self): - # See the specification for details. - start_mark = self.get_mark() - ch = self.peek(1) - if ch == '<': - handle = None - self.forward(2) - suffix = self.scan_tag_uri('tag', start_mark) - if self.peek() != '>': - raise ScannerError("while parsing a tag", start_mark, - "expected '>', but found %r" % self.peek(), - self.get_mark()) - self.forward() - elif ch in '\0 \t\r\n\x85\u2028\u2029': - handle = None - suffix = '!' - self.forward() - else: - length = 1 - use_handle = False - while ch not in '\0 \r\n\x85\u2028\u2029': - if ch == '!': - use_handle = True - break - length += 1 - ch = self.peek(length) - handle = '!' - if use_handle: - handle = self.scan_tag_handle('tag', start_mark) - else: - handle = '!' - self.forward() - suffix = self.scan_tag_uri('tag', start_mark) - ch = self.peek() - if ch not in '\0 \r\n\x85\u2028\u2029': - raise ScannerError("while scanning a tag", start_mark, - "expected ' ', but found %r" % ch, self.get_mark()) - value = (handle, suffix) - end_mark = self.get_mark() - return TagToken(value, start_mark, end_mark) - - def scan_block_scalar(self, style): - # See the specification for details. - - if style == '>': - folded = True - else: - folded = False - - chunks = [] - start_mark = self.get_mark() - - # Scan the header. - self.forward() - chomping, increment = self.scan_block_scalar_indicators(start_mark) - self.scan_block_scalar_ignored_line(start_mark) - - # Determine the indentation level and go to the first non-empty line. - min_indent = self.indent+1 - if min_indent < 1: - min_indent = 1 - if increment is None: - breaks, max_indent, end_mark = self.scan_block_scalar_indentation() - indent = max(min_indent, max_indent) - else: - indent = min_indent+increment-1 - breaks, end_mark = self.scan_block_scalar_breaks(indent) - line_break = '' - - # Scan the inner part of the block scalar. - while self.column == indent and self.peek() != '\0': - chunks.extend(breaks) - leading_non_space = self.peek() not in ' \t' - length = 0 - while self.peek(length) not in '\0\r\n\x85\u2028\u2029': - length += 1 - chunks.append(self.prefix(length)) - self.forward(length) - line_break = self.scan_line_break() - breaks, end_mark = self.scan_block_scalar_breaks(indent) - if self.column == indent and self.peek() != '\0': - - # Unfortunately, folding rules are ambiguous. - # - # This is the folding according to the specification: - - if folded and line_break == '\n' \ - and leading_non_space and self.peek() not in ' \t': - if not breaks: - chunks.append(' ') - else: - chunks.append(line_break) - - # This is Clark Evans's interpretation (also in the spec - # examples): - # - #if folded and line_break == '\n': - # if not breaks: - # if self.peek() not in ' \t': - # chunks.append(' ') - # else: - # chunks.append(line_break) - #else: - # chunks.append(line_break) - else: - break - - # Chomp the tail. - if chomping is not False: - chunks.append(line_break) - if chomping is True: - chunks.extend(breaks) - - # We are done. - return ScalarToken(''.join(chunks), False, start_mark, end_mark, - style) - - def scan_block_scalar_indicators(self, start_mark): - # See the specification for details. - chomping = None - increment = None - ch = self.peek() - if ch in '+-': - if ch == '+': - chomping = True - else: - chomping = False - self.forward() - ch = self.peek() - if ch in '0123456789': - increment = int(ch) - if increment == 0: - raise ScannerError("while scanning a block scalar", start_mark, - "expected indentation indicator in the range 1-9, but found 0", - self.get_mark()) - self.forward() - elif ch in '0123456789': - increment = int(ch) - if increment == 0: - raise ScannerError("while scanning a block scalar", start_mark, - "expected indentation indicator in the range 1-9, but found 0", - self.get_mark()) - self.forward() - ch = self.peek() - if ch in '+-': - if ch == '+': - chomping = True - else: - chomping = False - self.forward() - ch = self.peek() - if ch not in '\0 \r\n\x85\u2028\u2029': - raise ScannerError("while scanning a block scalar", start_mark, - "expected chomping or indentation indicators, but found %r" - % ch, self.get_mark()) - return chomping, increment - - def scan_block_scalar_ignored_line(self, start_mark): - # See the specification for details. - while self.peek() == ' ': - self.forward() - if self.peek() == '#': - while self.peek() not in '\0\r\n\x85\u2028\u2029': - self.forward() - ch = self.peek() - if ch not in '\0\r\n\x85\u2028\u2029': - raise ScannerError("while scanning a block scalar", start_mark, - "expected a comment or a line break, but found %r" % ch, - self.get_mark()) - self.scan_line_break() - - def scan_block_scalar_indentation(self): - # See the specification for details. - chunks = [] - max_indent = 0 - end_mark = self.get_mark() - while self.peek() in ' \r\n\x85\u2028\u2029': - if self.peek() != ' ': - chunks.append(self.scan_line_break()) - end_mark = self.get_mark() - else: - self.forward() - if self.column > max_indent: - max_indent = self.column - return chunks, max_indent, end_mark - - def scan_block_scalar_breaks(self, indent): - # See the specification for details. - chunks = [] - end_mark = self.get_mark() - while self.column < indent and self.peek() == ' ': - self.forward() - while self.peek() in '\r\n\x85\u2028\u2029': - chunks.append(self.scan_line_break()) - end_mark = self.get_mark() - while self.column < indent and self.peek() == ' ': - self.forward() - return chunks, end_mark - - def scan_flow_scalar(self, style): - # See the specification for details. - # Note that we loose indentation rules for quoted scalars. Quoted - # scalars don't need to adhere indentation because " and ' clearly - # mark the beginning and the end of them. Therefore we are less - # restrictive then the specification requires. We only need to check - # that document separators are not included in scalars. - if style == '"': - double = True - else: - double = False - chunks = [] - start_mark = self.get_mark() - quote = self.peek() - self.forward() - chunks.extend(self.scan_flow_scalar_non_spaces(double, start_mark)) - while self.peek() != quote: - chunks.extend(self.scan_flow_scalar_spaces(double, start_mark)) - chunks.extend(self.scan_flow_scalar_non_spaces(double, start_mark)) - self.forward() - end_mark = self.get_mark() - return ScalarToken(''.join(chunks), False, start_mark, end_mark, - style) - - ESCAPE_REPLACEMENTS = { - '0': '\0', - 'a': '\x07', - 'b': '\x08', - 't': '\x09', - '\t': '\x09', - 'n': '\x0A', - 'v': '\x0B', - 'f': '\x0C', - 'r': '\x0D', - 'e': '\x1B', - ' ': '\x20', - '\"': '\"', - '\\': '\\', - 'N': '\x85', - '_': '\xA0', - 'L': '\u2028', - 'P': '\u2029', - } - - ESCAPE_CODES = { - 'x': 2, - 'u': 4, - 'U': 8, - } - - def scan_flow_scalar_non_spaces(self, double, start_mark): - # See the specification for details. - chunks = [] - while True: - length = 0 - while self.peek(length) not in '\'\"\\\0 \t\r\n\x85\u2028\u2029': - length += 1 - if length: - chunks.append(self.prefix(length)) - self.forward(length) - ch = self.peek() - if not double and ch == '\'' and self.peek(1) == '\'': - chunks.append('\'') - self.forward(2) - elif (double and ch == '\'') or (not double and ch in '\"\\'): - chunks.append(ch) - self.forward() - elif double and ch == '\\': - self.forward() - ch = self.peek() - if ch in self.ESCAPE_REPLACEMENTS: - chunks.append(self.ESCAPE_REPLACEMENTS[ch]) - self.forward() - elif ch in self.ESCAPE_CODES: - length = self.ESCAPE_CODES[ch] - self.forward() - for k in range(length): - if self.peek(k) not in '0123456789ABCDEFabcdef': - raise ScannerError("while scanning a double-quoted scalar", start_mark, - "expected escape sequence of %d hexdecimal numbers, but found %r" % - (length, self.peek(k)), self.get_mark()) - code = int(self.prefix(length), 16) - chunks.append(chr(code)) - self.forward(length) - elif ch in '\r\n\x85\u2028\u2029': - self.scan_line_break() - chunks.extend(self.scan_flow_scalar_breaks(double, start_mark)) - else: - raise ScannerError("while scanning a double-quoted scalar", start_mark, - "found unknown escape character %r" % ch, self.get_mark()) - else: - return chunks - - def scan_flow_scalar_spaces(self, double, start_mark): - # See the specification for details. - chunks = [] - length = 0 - while self.peek(length) in ' \t': - length += 1 - whitespaces = self.prefix(length) - self.forward(length) - ch = self.peek() - if ch == '\0': - raise ScannerError("while scanning a quoted scalar", start_mark, - "found unexpected end of stream", self.get_mark()) - elif ch in '\r\n\x85\u2028\u2029': - line_break = self.scan_line_break() - breaks = self.scan_flow_scalar_breaks(double, start_mark) - if line_break != '\n': - chunks.append(line_break) - elif not breaks: - chunks.append(' ') - chunks.extend(breaks) - else: - chunks.append(whitespaces) - return chunks - - def scan_flow_scalar_breaks(self, double, start_mark): - # See the specification for details. - chunks = [] - while True: - # Instead of checking indentation, we check for document - # separators. - prefix = self.prefix(3) - if (prefix == '---' or prefix == '...') \ - and self.peek(3) in '\0 \t\r\n\x85\u2028\u2029': - raise ScannerError("while scanning a quoted scalar", start_mark, - "found unexpected document separator", self.get_mark()) - while self.peek() in ' \t': - self.forward() - if self.peek() in '\r\n\x85\u2028\u2029': - chunks.append(self.scan_line_break()) - else: - return chunks - - def scan_plain(self): - # See the specification for details. - # We add an additional restriction for the flow context: - # plain scalars in the flow context cannot contain ',', ':' and '?'. - # We also keep track of the `allow_simple_key` flag here. - # Indentation rules are loosed for the flow context. - chunks = [] - start_mark = self.get_mark() - end_mark = start_mark - indent = self.indent+1 - # We allow zero indentation for scalars, but then we need to check for - # document separators at the beginning of the line. - #if indent == 0: - # indent = 1 - spaces = [] - while True: - length = 0 - if self.peek() == '#': - break - while True: - ch = self.peek(length) - if ch in '\0 \t\r\n\x85\u2028\u2029' \ - or (not self.flow_level and ch == ':' and - self.peek(length+1) in '\0 \t\r\n\x85\u2028\u2029') \ - or (self.flow_level and ch in ',:?[]{}'): - break - length += 1 - # It's not clear what we should do with ':' in the flow context. - if (self.flow_level and ch == ':' - and self.peek(length+1) not in '\0 \t\r\n\x85\u2028\u2029,[]{}'): - self.forward(length) - raise ScannerError("while scanning a plain scalar", start_mark, - "found unexpected ':'", self.get_mark(), - "Please check http://pyyaml.org/wiki/YAMLColonInFlowContext for details.") - if length == 0: - break - self.allow_simple_key = False - chunks.extend(spaces) - chunks.append(self.prefix(length)) - self.forward(length) - end_mark = self.get_mark() - spaces = self.scan_plain_spaces(indent, start_mark) - if not spaces or self.peek() == '#' \ - or (not self.flow_level and self.column < indent): - break - return ScalarToken(''.join(chunks), True, start_mark, end_mark) - - def scan_plain_spaces(self, indent, start_mark): - # See the specification for details. - # The specification is really confusing about tabs in plain scalars. - # We just forbid them completely. Do not use tabs in YAML! - chunks = [] - length = 0 - while self.peek(length) in ' ': - length += 1 - whitespaces = self.prefix(length) - self.forward(length) - ch = self.peek() - if ch in '\r\n\x85\u2028\u2029': - line_break = self.scan_line_break() - self.allow_simple_key = True - prefix = self.prefix(3) - if (prefix == '---' or prefix == '...') \ - and self.peek(3) in '\0 \t\r\n\x85\u2028\u2029': - return - breaks = [] - while self.peek() in ' \r\n\x85\u2028\u2029': - if self.peek() == ' ': - self.forward() - else: - breaks.append(self.scan_line_break()) - prefix = self.prefix(3) - if (prefix == '---' or prefix == '...') \ - and self.peek(3) in '\0 \t\r\n\x85\u2028\u2029': - return - if line_break != '\n': - chunks.append(line_break) - elif not breaks: - chunks.append(' ') - chunks.extend(breaks) - elif whitespaces: - chunks.append(whitespaces) - return chunks - - def scan_tag_handle(self, name, start_mark): - # See the specification for details. - # For some strange reasons, the specification does not allow '_' in - # tag handles. I have allowed it anyway. - ch = self.peek() - if ch != '!': - raise ScannerError("while scanning a %s" % name, start_mark, - "expected '!', but found %r" % ch, self.get_mark()) - length = 1 - ch = self.peek(length) - if ch != ' ': - while '0' <= ch <= '9' or 'A' <= ch <= 'Z' or 'a' <= ch <= 'z' \ - or ch in '-_': - length += 1 - ch = self.peek(length) - if ch != '!': - self.forward(length) - raise ScannerError("while scanning a %s" % name, start_mark, - "expected '!', but found %r" % ch, self.get_mark()) - length += 1 - value = self.prefix(length) - self.forward(length) - return value - - def scan_tag_uri(self, name, start_mark): - # See the specification for details. - # Note: we do not check if URI is well-formed. - chunks = [] - length = 0 - ch = self.peek(length) - while '0' <= ch <= '9' or 'A' <= ch <= 'Z' or 'a' <= ch <= 'z' \ - or ch in '-;/?:@&=+$,_.!~*\'()[]%': - if ch == '%': - chunks.append(self.prefix(length)) - self.forward(length) - length = 0 - chunks.append(self.scan_uri_escapes(name, start_mark)) - else: - length += 1 - ch = self.peek(length) - if length: - chunks.append(self.prefix(length)) - self.forward(length) - length = 0 - if not chunks: - raise ScannerError("while parsing a %s" % name, start_mark, - "expected URI, but found %r" % ch, self.get_mark()) - return ''.join(chunks) - - def scan_uri_escapes(self, name, start_mark): - # See the specification for details. - codes = [] - mark = self.get_mark() - while self.peek() == '%': - self.forward() - for k in range(2): - if self.peek(k) not in '0123456789ABCDEFabcdef': - raise ScannerError("while scanning a %s" % name, start_mark, - "expected URI escape sequence of 2 hexdecimal numbers, but found %r" - % self.peek(k), self.get_mark()) - codes.append(int(self.prefix(2), 16)) - self.forward(2) - try: - value = bytes(codes).decode('utf-8') - except UnicodeDecodeError as exc: - raise ScannerError("while scanning a %s" % name, start_mark, str(exc), mark) - return value - - def scan_line_break(self): - # Transforms: - # '\r\n' : '\n' - # '\r' : '\n' - # '\n' : '\n' - # '\x85' : '\n' - # '\u2028' : '\u2028' - # '\u2029 : '\u2029' - # default : '' - ch = self.peek() - if ch in '\r\n\x85': - if self.prefix(2) == '\r\n': - self.forward(2) - else: - self.forward() - return '\n' - elif ch in '\u2028\u2029': - self.forward() - return ch - return '' - -#try: -# import psyco -# psyco.bind(Scanner) -#except ImportError: -# pass - diff --git a/python.d/python_modules/pyyaml3/serializer.py b/python.d/python_modules/pyyaml3/serializer.py deleted file mode 100644 index fe911e67a..000000000 --- a/python.d/python_modules/pyyaml3/serializer.py +++ /dev/null @@ -1,111 +0,0 @@ - -__all__ = ['Serializer', 'SerializerError'] - -from .error import YAMLError -from .events import * -from .nodes import * - -class SerializerError(YAMLError): - pass - -class Serializer: - - ANCHOR_TEMPLATE = 'id%03d' - - def __init__(self, encoding=None, - explicit_start=None, explicit_end=None, version=None, tags=None): - self.use_encoding = encoding - self.use_explicit_start = explicit_start - self.use_explicit_end = explicit_end - self.use_version = version - self.use_tags = tags - self.serialized_nodes = {} - self.anchors = {} - self.last_anchor_id = 0 - self.closed = None - - def open(self): - if self.closed is None: - self.emit(StreamStartEvent(encoding=self.use_encoding)) - self.closed = False - elif self.closed: - raise SerializerError("serializer is closed") - else: - raise SerializerError("serializer is already opened") - - def close(self): - if self.closed is None: - raise SerializerError("serializer is not opened") - elif not self.closed: - self.emit(StreamEndEvent()) - self.closed = True - - #def __del__(self): - # self.close() - - def serialize(self, node): - if self.closed is None: - raise SerializerError("serializer is not opened") - elif self.closed: - raise SerializerError("serializer is closed") - self.emit(DocumentStartEvent(explicit=self.use_explicit_start, - version=self.use_version, tags=self.use_tags)) - self.anchor_node(node) - self.serialize_node(node, None, None) - self.emit(DocumentEndEvent(explicit=self.use_explicit_end)) - self.serialized_nodes = {} - self.anchors = {} - self.last_anchor_id = 0 - - def anchor_node(self, node): - if node in self.anchors: - if self.anchors[node] is None: - self.anchors[node] = self.generate_anchor(node) - else: - self.anchors[node] = None - if isinstance(node, SequenceNode): - for item in node.value: - self.anchor_node(item) - elif isinstance(node, MappingNode): - for key, value in node.value: - self.anchor_node(key) - self.anchor_node(value) - - def generate_anchor(self, node): - self.last_anchor_id += 1 - return self.ANCHOR_TEMPLATE % self.last_anchor_id - - def serialize_node(self, node, parent, index): - alias = self.anchors[node] - if node in self.serialized_nodes: - self.emit(AliasEvent(alias)) - else: - self.serialized_nodes[node] = True - self.descend_resolver(parent, index) - if isinstance(node, ScalarNode): - detected_tag = self.resolve(ScalarNode, node.value, (True, False)) - default_tag = self.resolve(ScalarNode, node.value, (False, True)) - implicit = (node.tag == detected_tag), (node.tag == default_tag) - self.emit(ScalarEvent(alias, node.tag, implicit, node.value, - style=node.style)) - elif isinstance(node, SequenceNode): - implicit = (node.tag - == self.resolve(SequenceNode, node.value, True)) - self.emit(SequenceStartEvent(alias, node.tag, implicit, - flow_style=node.flow_style)) - index = 0 - for item in node.value: - self.serialize_node(item, node, index) - index += 1 - self.emit(SequenceEndEvent()) - elif isinstance(node, MappingNode): - implicit = (node.tag - == self.resolve(MappingNode, node.value, True)) - self.emit(MappingStartEvent(alias, node.tag, implicit, - flow_style=node.flow_style)) - for key, value in node.value: - self.serialize_node(key, node, None) - self.serialize_node(value, node, key) - self.emit(MappingEndEvent()) - self.ascend_resolver() - diff --git a/python.d/python_modules/pyyaml3/tokens.py b/python.d/python_modules/pyyaml3/tokens.py deleted file mode 100644 index 4d0b48a39..000000000 --- a/python.d/python_modules/pyyaml3/tokens.py +++ /dev/null @@ -1,104 +0,0 @@ - -class Token(object): - def __init__(self, start_mark, end_mark): - self.start_mark = start_mark - self.end_mark = end_mark - def __repr__(self): - attributes = [key for key in self.__dict__ - if not key.endswith('_mark')] - attributes.sort() - arguments = ', '.join(['%s=%r' % (key, getattr(self, key)) - for key in attributes]) - return '%s(%s)' % (self.__class__.__name__, arguments) - -#class BOMToken(Token): -# id = '<byte order mark>' - -class DirectiveToken(Token): - id = '<directive>' - def __init__(self, name, value, start_mark, end_mark): - self.name = name - self.value = value - self.start_mark = start_mark - self.end_mark = end_mark - -class DocumentStartToken(Token): - id = '<document start>' - -class DocumentEndToken(Token): - id = '<document end>' - -class StreamStartToken(Token): - id = '<stream start>' - def __init__(self, start_mark=None, end_mark=None, - encoding=None): - self.start_mark = start_mark - self.end_mark = end_mark - self.encoding = encoding - -class StreamEndToken(Token): - id = '<stream end>' - -class BlockSequenceStartToken(Token): - id = '<block sequence start>' - -class BlockMappingStartToken(Token): - id = '<block mapping start>' - -class BlockEndToken(Token): - id = '<block end>' - -class FlowSequenceStartToken(Token): - id = '[' - -class FlowMappingStartToken(Token): - id = '{' - -class FlowSequenceEndToken(Token): - id = ']' - -class FlowMappingEndToken(Token): - id = '}' - -class KeyToken(Token): - id = '?' - -class ValueToken(Token): - id = ':' - -class BlockEntryToken(Token): - id = '-' - -class FlowEntryToken(Token): - id = ',' - -class AliasToken(Token): - id = '<alias>' - def __init__(self, value, start_mark, end_mark): - self.value = value - self.start_mark = start_mark - self.end_mark = end_mark - -class AnchorToken(Token): - id = '<anchor>' - def __init__(self, value, start_mark, end_mark): - self.value = value - self.start_mark = start_mark - self.end_mark = end_mark - -class TagToken(Token): - id = '<tag>' - def __init__(self, value, start_mark, end_mark): - self.value = value - self.start_mark = start_mark - self.end_mark = end_mark - -class ScalarToken(Token): - id = '<scalar>' - def __init__(self, value, plain, start_mark, end_mark, style=None): - self.value = value - self.plain = plain - self.start_mark = start_mark - self.end_mark = end_mark - self.style = style - diff --git a/python.d/redis.chart.py b/python.d/redis.chart.py index 218401e12..61f4f6d61 100644 --- a/python.d/redis.chart.py +++ b/python.d/redis.chart.py @@ -19,40 +19,58 @@ retries = 60 # 'unix_socket': None # }} -ORDER = ['operations', 'hit_rate', 'memory', 'keys', 'clients', 'slaves'] +ORDER = ['operations', 'hit_rate', 'memory', 'keys', 'net', 'connections', 'clients', 'slaves', 'persistence'] CHARTS = { 'operations': { - 'options': [None, 'Operations', 'operations/s', 'Statistics', 'redis.operations', 'line'], + 'options': [None, 'Redis Operations', 'operations/s', 'operations', 'redis.operations', 'line'], 'lines': [ + ['total_commands_processed', 'commands', 'incremental'], ['instantaneous_ops_per_sec', 'operations', 'absolute'] ]}, 'hit_rate': { - 'options': [None, 'Hit rate', 'percent', 'Statistics', 'redis.hit_rate', 'line'], + 'options': [None, 'Redis Hit rate', 'percent', 'hits', 'redis.hit_rate', 'line'], 'lines': [ ['hit_rate', 'rate', 'absolute'] ]}, 'memory': { - 'options': [None, 'Memory utilization', 'kilobytes', 'Memory', 'redis.memory', 'line'], + 'options': [None, 'Redis Memory utilization', 'kilobytes', 'memory', 'redis.memory', 'line'], 'lines': [ ['used_memory', 'total', 'absolute', 1, 1024], ['used_memory_lua', 'lua', 'absolute', 1, 1024] ]}, + 'net': { + 'options': [None, 'Redis Bandwidth', 'kilobits/s', 'network', 'redis.net', 'area'], + 'lines': [ + ['total_net_input_bytes', 'in', 'incremental', 8, 1024], + ['total_net_output_bytes', 'out', 'incremental', -8, 1024] + ]}, 'keys': { - 'options': [None, 'Database keys', 'keys', 'Keys', 'redis.keys', 'line'], + 'options': [None, 'Redis Keys per Database', 'keys', 'keys', 'redis.keys', 'line'], 'lines': [ # lines are created dynamically in `check()` method ]}, + 'connections': { + 'options': [None, 'Redis Connections', 'connections/s', 'connections', 'redis.connections', 'line'], + 'lines': [ + ['total_connections_received', 'received', 'incremental', 1], + ['rejected_connections', 'rejected', 'incremental', -1] + ]}, 'clients': { - 'options': [None, 'Clients', 'clients', 'Clients', 'redis.clients', 'line'], + 'options': [None, 'Redis Clients', 'clients', 'connections', 'redis.clients', 'line'], 'lines': [ - ['connected_clients', 'connected', 'absolute'], - ['blocked_clients', 'blocked', 'absolute'] + ['connected_clients', 'connected', 'absolute', 1], + ['blocked_clients', 'blocked', 'absolute', -1] ]}, 'slaves': { - 'options': [None, 'Slaves', 'slaves', 'Replication', 'redis.slaves', 'line'], + 'options': [None, 'Redis Slaves', 'slaves', 'replication', 'redis.slaves', 'line'], 'lines': [ ['connected_slaves', 'connected', 'absolute'] + ]}, + 'persistence': { + 'options': [None, 'Redis Persistence Changes Since Last Save', 'changes', 'persistence', 'redis.rdb_changes', 'line'], + 'lines': [ + ['rdb_changes_since_last_save', 'changes', 'absolute'] ]} } @@ -61,46 +79,73 @@ class Service(SocketService): def __init__(self, configuration=None, name=None): SocketService.__init__(self, configuration=configuration, name=name) self.request = "INFO\r\n" - self.host = "localhost" - self.port = 6379 - self.unix_socket = None self.order = ORDER self.definitions = CHARTS self._keep_alive = True self.chart_name = "" + self.passwd = None + self.port = 6379 + if 'port' in configuration: + self.port = configuration['port'] + if 'pass' in configuration: + self.passwd = configuration['pass'] + if 'host' in configuration: + self.host = configuration['host'] + if 'socket' in configuration: + self.unix_socket = configuration['socket'] def _get_data(self): """ Get data from socket :return: dict """ + if self.passwd: + info_request = self.request + self.request = "AUTH " + self.passwd + "\r\n" + raw = self._get_raw_data().strip() + if raw != "+OK": + self.error("invalid password") + return None + self.request = info_request + response = self._get_raw_data() + if response is None: + # error has already been logged + return None + try: - raw = self._get_raw_data().split("\n") + parsed = response.split("\n") except AttributeError: - self.error("no data received") + self.error("response is invalid/empty") return None + data = {} - for line in raw: - if line.startswith(('instantaneous', 'keyspace', 'used_memory', 'connected', 'blocked')): - try: - t = line.split(':') - data[t[0]] = int(t[1]) - except (IndexError, ValueError): - pass - elif line.startswith('db'): + for line in parsed: + if len(line) < 5 or line[0] == '$' or line[0] == '#': + continue + + if line.startswith('db'): tmp = line.split(',')[0].replace('keys=', '') record = tmp.split(':') - data[record[0]] = int(record[1]) + data[record[0]] = record[1] + continue + + try: + t = line.split(':') + data[t[0]] = t[1] + except (IndexError, ValueError): + self.debug("invalid line received: " + str(line)) + pass + + if len(data) == 0: + self.error("received data doesn't have any records") + return None + try: - data['hit_rate'] = int((data['keyspace_hits'] / float(data['keyspace_hits'] + data['keyspace_misses'])) * 100) + data['hit_rate'] = (int(data['keyspace_hits']) * 100) / (int(data['keyspace_hits']) + int(data['keyspace_misses'])) except: data['hit_rate'] = 0 - if len(data) == 0: - self.error("received data doesn't have needed records") - return None - else: - return data + return data def _check_raw_data(self, data): """ @@ -112,12 +157,15 @@ class Service(SocketService): length = len(data) supposed = data.split('\n')[0][1:] offset = len(supposed) + 4 # 1 dollar sing, 1 new line character + 1 ending sequence '\r\n' + if not supposed.isdigit(): + return True supposed = int(supposed) + if length - offset >= supposed: + self.debug("received full response from redis") return True - else: - return False + self.debug("waiting more data from redis") return False def check(self): diff --git a/python.d/sensors.chart.py b/python.d/sensors.chart.py index 23bccb97c..e83aacfd8 100644 --- a/python.d/sensors.chart.py +++ b/python.d/sensors.chart.py @@ -77,6 +77,9 @@ class Service(SimpleService): SimpleService.__init__(self, configuration=configuration, name=name) self.order = [] self.definitions = {} + self.celsius = ('Celsius', lambda x: x) + self.fahrenheit = ('Fahrenheit', lambda x: x * 9 / 5 + 32) if self.configuration.get('fahrenheit') else False + self.choice = (choice for choice in [self.fahrenheit, self.celsius] if choice) self.chips = [] def _get_data(self): @@ -94,7 +97,10 @@ class Service(SimpleService): limit = LIMITS[typeName]; if val < limit[0] or val > limit[1]: continue - data[prefix + "_" + str(feature.name.decode())] = int(val * 1000) + if 'temp' in str(feature.name.decode()): + data[prefix + "_" + str(feature.name.decode())] = int(self.calc(val) * 1000) + else: + data[prefix + "_" + str(feature.name.decode())] = int(val * 1000) except Exception as e: self.error(e) return None @@ -121,6 +127,8 @@ class Service(SimpleService): self.order.append(name) chart_def = list(CHARTS[type]['options']) chart_def[1] = chip_name + chart_def[1] + if chart_def[2] == 'Celsius': + chart_def[2] = self.choice[0] self.definitions[name] = {'options': chart_def} self.definitions[name]['lines'] = [] line = list(CHARTS[type]['lines'][0]) @@ -134,10 +142,20 @@ class Service(SimpleService): except Exception as e: self.error(e) return False + + try: + self.choice = next(self.choice) + except StopIteration: + # That can not happen but.. + self.choice = ('Celsius', lambda x: x) + self.calc = self.choice[1] + else: + self.calc = self.choice[1] try: self._create_definitions() except Exception as e: self.error(e) return False + return True diff --git a/python.d/squid.chart.py b/python.d/squid.chart.py index 8300d9bad..e9e8f1d08 100644 --- a/python.d/squid.chart.py +++ b/python.d/squid.chart.py @@ -58,20 +58,25 @@ class Service(SocketService): Get data via http request :return: dict """ + response = self._get_raw_data() + data = {} try: raw = "" - for tmp in self._get_raw_data().split('\r\n'): + for tmp in response.split('\r\n'): if tmp.startswith("sample_time"): raw = tmp break + if raw.startswith('<'): self.error("invalid data received") return None + for row in raw.split('\n'): if row.startswith(("client", "server.all")): tmp = row.split("=") data[tmp[0].replace('.', '_').strip(' ')] = int(tmp[1]) + except (ValueError, AttributeError, TypeError): self.error("invalid data received") return None @@ -83,15 +88,19 @@ class Service(SocketService): return data def _check_raw_data(self, data): - if "Connection: keep-alive" in data[:1024]: + header = data[:1024].lower() + + if "connection: keep-alive" in header: self._keep_alive = True else: self._keep_alive = False - if data[-7:] == "\r\n0\r\n\r\n" and "Transfer-Encoding: chunked" in data[:1024]: # HTTP/1.1 response + if data[-7:] == "\r\n0\r\n\r\n" and "transfer-encoding: chunked" in header: # HTTP/1.1 response + self.debug("received full response from squid") return True - else: - return False + + self.debug("waiting more data from squid") + return False def check(self): """ diff --git a/python.d/varnish.chart.py b/python.d/varnish.chart.py new file mode 100644 index 000000000..2b1512f4e --- /dev/null +++ b/python.d/varnish.chart.py @@ -0,0 +1,239 @@ +# -*- coding: utf-8 -*- +# Description: varnish netdata python.d module +# Author: l2isbad + +from base import SimpleService +from re import compile +from os import access as is_executable, X_OK +from subprocess import Popen, PIPE + + +# default module values (can be overridden per job in `config`) +# update_every = 2 +priority = 60000 +retries = 60 + +ORDER = ['session', 'hit_rate', 'chit_rate', 'expunge', 'threads', 'backend_health', 'memory_usage', 'bad', 'uptime'] + +CHARTS = {'backend_health': + {'lines': [['backend_conn', 'conn', 'incremental', 1, 1], + ['backend_unhealthy', 'unhealthy', 'incremental', 1, 1], + ['backend_busy', 'busy', 'incremental', 1, 1], + ['backend_fail', 'fail', 'incremental', 1, 1], + ['backend_reuse', 'reuse', 'incremental', 1, 1], + ['backend_recycle', 'resycle', 'incremental', 1, 1], + ['backend_toolate', 'toolate', 'incremental', 1, 1], + ['backend_retry', 'retry', 'incremental', 1, 1], + ['backend_req', 'req', 'incremental', 1, 1]], + 'options': [None, 'Backend health', 'connections', 'Backend health', 'varnish.backend_traf', 'line']}, + 'bad': + {'lines': [['sess_drop_b', None, 'incremental', 1, 1], + ['backend_unhealthy_b', None, 'incremental', 1, 1], + ['fetch_failed', None, 'incremental', 1, 1], + ['backend_busy_b', None, 'incremental', 1, 1], + ['threads_failed_b', None, 'incremental', 1, 1], + ['threads_limited_b', None, 'incremental', 1, 1], + ['threads_destroyed_b', None, 'incremental', 1, 1], + ['thread_queue_len_b', 'queue_len', 'absolute', 1, 1], + ['losthdr_b', None, 'incremental', 1, 1], + ['esi_errors_b', None, 'incremental', 1, 1], + ['esi_warnings_b', None, 'incremental', 1, 1], + ['sess_fail_b', None, 'incremental', 1, 1], + ['sc_pipe_overflow_b', None, 'incremental', 1, 1], + ['sess_pipe_overflow_b', None, 'incremental', 1, 1]], + 'options': [None, 'Misbehavior', 'problems', 'Problems summary', 'varnish.bad', 'line']}, + 'expunge': + {'lines': [['n_expired', 'expired', 'incremental', 1, 1], + ['n_lru_nuked', 'lru_nuked', 'incremental', 1, 1]], + 'options': [None, 'Object expunging', 'objects', 'Cache performance', 'varnish.expunge', 'line']}, + 'hit_rate': + {'lines': [['cache_hit_perc', 'hit', 'absolute', 1, 100], + ['cache_miss_perc', 'miss', 'absolute', 1, 100], + ['cache_hitpass_perc', 'hitpass', 'absolute', 1, 100]], + 'options': [None, 'All history hit rate ratio','percent', 'Cache performance', 'varnish.hit_rate', 'stacked']}, + 'chit_rate': + {'lines': [['cache_hit_cperc', 'hit', 'absolute', 1, 100], + ['cache_miss_cperc', 'miss', 'absolute', 1, 100], + ['cache_hitpass_cperc', 'hitpass', 'absolute', 1, 100]], + 'options': [None, 'Current poll hit rate ratio','percent', 'Cache performance', 'varnish.chit_rate', 'stacked']}, + 'memory_usage': + {'lines': [['s0.g_space', 'available', 'absolute', 1, 1048576], + ['s0.g_bytes', 'allocated', 'absolute', -1, 1048576]], + 'options': [None, 'Memory usage', 'megabytes', 'Memory usage', 'varnish.memory_usage', 'stacked']}, + 'session': + {'lines': [['sess_conn', 'sess_conn', 'incremental', 1, 1], + ['client_req', 'client_requests', 'incremental', 1, 1], + ['client_conn', 'client_conn', 'incremental', 1, 1], + ['client_drop', 'client_drop', 'incremental', 1, 1], + ['sess_dropped', 'sess_dropped', 'incremental', 1, 1]], + 'options': [None, 'Sessions', 'units', 'Client metrics', 'varnish.session', 'line']}, + 'threads': + {'lines': [['threads', None, 'absolute', 1, 1], + ['threads_created', 'created', 'incremental', 1, 1], + ['threads_failed', 'failed', 'incremental', 1, 1], + ['threads_limited', 'limited', 'incremental', 1, 1], + ['thread_queue_len', 'queue_len', 'incremental', 1, 1], + ['sess_queued', 'sess_queued', 'incremental', 1, 1]], + 'options': [None, 'Thread status', 'threads', 'Thread-related metrics', 'varnish.threads', 'line']}, + 'uptime': + {'lines': [['uptime', None, 'absolute', 1, 1]], + 'options': [None, 'Varnish uptime', 'seconds', 'Uptime', 'varnish.uptime', 'line']} +} + +DIRECTORIES = ['/bin/', '/usr/bin/', '/sbin/', '/usr/sbin/'] + + +class Service(SimpleService): + def __init__(self, configuration=None, name=None): + SimpleService.__init__(self, configuration=configuration, name=name) + try: + self.varnish = [''.join([directory, 'varnishstat']) for directory in DIRECTORIES + if is_executable(''.join([directory, 'varnishstat']), X_OK)][0] + except IndexError: + self.varnish = False + self.rgx_all = compile(r'([A-Z]+\.)?([\d\w_.]+)\s+(\d+)') + # Could be + # VBE.boot.super_backend.pipe_hdrbyte (new) + # or + # VBE.default2(127.0.0.2,,81).bereq_bodybytes (old) + # Regex result: [('super_backend', 'beresp_hdrbytes', '0'), ('super_backend', 'beresp_bodybytes', '0')] + self.rgx_bck = (compile(r'VBE.([\d\w_.]+)\(.*?\).(beresp[\w_]+)\s+(\d+)'), + compile(r'VBE\.[\d\w-]+\.([\w\d_]+).(beresp[\w_]+)\s+(\d+)')) + self.cache_prev = list() + + def check(self): + # Cant start without 'varnishstat' command + if not self.varnish: + self.error('\'varnishstat\' command was not found in %s or not executable by netdata' % DIRECTORIES) + return False + + # If command is present and we can execute it we need to make sure.. + # 1. STDOUT is not empty + reply = self._get_raw_data() + if not reply: + self.error('No output from \'varnishstat\' (not enough privileges?)') + return False + + # 2. Output is parsable (list is not empty after regex findall) + is_parsable = self.rgx_all.findall(reply) + if not is_parsable: + self.error('Cant parse output...') + return False + + # We need to find the right regex for backend parse + self.backend_list = self.rgx_bck[0].findall(reply)[::2] + if self.backend_list: + self.rgx_bck = self.rgx_bck[0] + else: + self.backend_list = self.rgx_bck[1].findall(reply)[::2] + self.rgx_bck = self.rgx_bck[1] + + # We are about to start! + self.create_charts() + + self.info('Plugin was started successfully') + return True + + def _get_raw_data(self): + try: + reply = Popen([self.varnish, '-1'], stdout=PIPE, stderr=PIPE, shell=False) + except OSError: + return None + + raw_data = reply.communicate()[0] + + if not raw_data: + return None + + return raw_data + + def _get_data(self): + """ + Format data received from shell command + :return: dict + """ + raw_data = self._get_raw_data() + data_all = self.rgx_all.findall(raw_data) + data_backend = self.rgx_bck.findall(raw_data) + + if not data_all: + return None + + # 1. ALL data from 'varnishstat -1'. t - type(MAIN, MEMPOOL etc) + to_netdata = {k: int(v) for t, k, v in data_all} + + # 2. ADD backend statistics + to_netdata.update({'_'.join([n, k]): int(v) for n, k, v in data_backend}) + + # 3. ADD additional keys to dict + # 3.1 Cache hit/miss/hitpass OVERALL in percent + cache_summary = sum([to_netdata.get('cache_hit', 0), to_netdata.get('cache_miss', 0), + to_netdata.get('cache_hitpass', 0)]) + to_netdata['cache_hit_perc'] = find_percent(to_netdata.get('cache_hit', 0), cache_summary, 10000) + to_netdata['cache_miss_perc'] = find_percent(to_netdata.get('cache_miss', 0), cache_summary, 10000) + to_netdata['cache_hitpass_perc'] = find_percent(to_netdata.get('cache_hitpass', 0), cache_summary, 10000) + + # 3.2 Cache hit/miss/hitpass CURRENT in percent + if self.cache_prev: + cache_summary = sum([to_netdata.get('cache_hit', 0), to_netdata.get('cache_miss', 0), + to_netdata.get('cache_hitpass', 0)]) - sum(self.cache_prev) + to_netdata['cache_hit_cperc'] = find_percent(to_netdata.get('cache_hit', 0) - self.cache_prev[0], cache_summary, 10000) + to_netdata['cache_miss_cperc'] = find_percent(to_netdata.get('cache_miss', 0) - self.cache_prev[1], cache_summary, 10000) + to_netdata['cache_hitpass_cperc'] = find_percent(to_netdata.get('cache_hitpass', 0) - self.cache_prev[2], cache_summary, 10000) + else: + to_netdata['cache_hit_cperc'] = 0 + to_netdata['cache_miss_cperc'] = 0 + to_netdata['cache_hitpass_cperc'] = 0 + + self.cache_prev = [to_netdata.get('cache_hit', 0), to_netdata.get('cache_miss', 0), to_netdata.get('cache_hitpass', 0)] + + # 3.3 Problems summary chart + for elem in ['backend_busy', 'backend_unhealthy', 'esi_errors', 'esi_warnings', 'losthdr', 'sess_drop', 'sc_pipe_overflow', + 'sess_fail', 'sess_pipe_overflow', 'threads_destroyed', 'threads_failed', 'threads_limited', 'thread_queue_len']: + if to_netdata.get(elem) is not None: + to_netdata[''.join([elem, '_b'])] = to_netdata.get(elem) + + # Ready steady go! + return to_netdata + + def create_charts(self): + # If 'all_charts' is true...ALL charts are displayed. If no only default + 'extra_charts' + #if self.configuration.get('all_charts'): + # self.order = EXTRA_ORDER + #else: + # try: + # extra_charts = list(filter(lambda chart: chart in EXTRA_ORDER, self.extra_charts.split())) + # except (AttributeError, NameError, ValueError): + # self.error('Extra charts disabled.') + # extra_charts = [] + + self.order = ORDER[:] + #self.order.extend(extra_charts) + + # Create static charts + #self.definitions = {chart: values for chart, values in CHARTS.items() if chart in self.order} + self.definitions = CHARTS + + # Create dynamic backend charts + if self.backend_list: + for backend in self.backend_list: + self.order.insert(0, ''.join([backend[0], '_resp_stats'])) + self.definitions.update({''.join([backend[0], '_resp_stats']): { + 'options': [None, + '%s response statistics' % backend[0].capitalize(), + "kilobit/s", + 'Backend response', + 'varnish.backend', + 'area'], + 'lines': [[''.join([backend[0], '_beresp_hdrbytes']), + 'header', 'incremental', 8, 1000], + [''.join([backend[0], '_beresp_bodybytes']), + 'body', 'incremental', -8, 1000]]}}) + + +def find_percent(value1, value2, multiply): + # If value2 is 0 return 0 + if not value2: + return 0 + else: + return round(float(value1) / float(value2) * multiply) diff --git a/src/Makefile.am b/src/Makefile.am index 86b9a9fe4..d8274d255 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -24,26 +24,75 @@ dist_cache_DATA = .keep dist_varlib_DATA = .keep dist_registry_DATA = .keep dist_log_DATA = .keep +if !MACOS plugins_PROGRAMS = apps.plugin +endif netdata_SOURCES = \ appconfig.c appconfig.h \ + adaptive_resortable_list.c adaptive_resortable_list.h \ avl.c avl.h \ + backends.c backends.h \ + clocks.c clocks.h \ common.c common.h \ daemon.c daemon.h \ dictionary.c dictionary.h \ eval.c eval.h \ global_statistics.c global_statistics.h \ health.c health.h \ + inlined.h \ log.c log.h \ main.c main.h \ plugin_checks.c plugin_checks.h \ plugin_idlejitter.c plugin_idlejitter.h \ plugin_nfacct.c plugin_nfacct.h \ - plugin_proc.c plugin_proc.h \ plugin_tc.c plugin_tc.h \ plugins_d.c plugins_d.h \ popen.c popen.h \ + socket.c socket.h \ + simple_pattern.c simple_pattern.h \ + sys_fs_cgroup.c \ + sys_devices_system_edac_mc.c \ + sys_devices_system_node.c \ + procfile.c procfile.h \ + proc_self_mountinfo.c proc_self_mountinfo.h \ + registry.c registry.h \ + registry_internals.c registry_internals.h \ + registry_url.c registry_url.h \ + registry_person.c registry_person.h \ + registry_machine.c registry_machine.h \ + registry_init.c \ + registry_db.c \ + registry_log.c \ + rrd.c rrd.h \ + rrd2json.c rrd2json.h \ + storage_number.c storage_number.h \ + unit_test.c unit_test.h \ + url.c url.h \ + web_buffer.c web_buffer.h \ + web_buffer_svg.c web_buffer_svg.h \ + web_client.c web_client.h \ + web_server.c web_server.h \ + $(NULL) + +if FREEBSD +netdata_SOURCES += \ + plugin_freebsd.c plugin_freebsd.h \ + freebsd_sysctl.c \ + $(NULL) +else +if MACOS +netdata_SOURCES += \ + plugin_macos.c plugin_macos.h \ + macos_sysctl.c \ + macos_mach_smi.c \ + macos_fw.c \ + $(NULL) +else +netdata_SOURCES += \ + ipc.c ipc.h \ + plugin_proc.c plugin_proc.h \ + plugin_proc_diskspace.c plugin_proc_diskspace.h \ proc_diskstats.c \ proc_interrupts.c \ proc_softirqs.c \ @@ -60,23 +109,14 @@ netdata_SOURCES = \ proc_net_stat_conntrack.c \ proc_net_stat_synproxy.c \ proc_stat.c \ - proc_self_mountinfo.c proc_self_mountinfo.h \ proc_sys_kernel_random_entropy_avail.c \ proc_vmstat.c \ + proc_uptime.c \ sys_kernel_mm_ksm.c \ - sys_fs_cgroup.c \ - procfile.c procfile.h \ - registry.c registry.h \ - rrd.c rrd.h \ - rrd2json.c rrd2json.h \ - storage_number.c storage_number.h \ - unit_test.c unit_test.h \ - url.c url.h \ - web_buffer.c web_buffer.h \ - web_buffer_svg.c web_buffer_svg.h \ - web_client.c web_client.h \ - web_server.c web_server.h \ $(NULL) +endif +endif + netdata_LDADD = \ $(OPTIONAL_MATH_LIBS) \ $(OPTIONAL_NFACCT_LIBS) \ @@ -87,7 +127,9 @@ netdata_LDADD = \ apps_plugin_SOURCES = \ apps_plugin.c \ avl.c avl.h \ + clocks.c clocks.h \ common.c common.h \ + inlined.h \ log.c log.h \ procfile.c procfile.h \ web_buffer.c web_buffer.h \ diff --git a/src/Makefile.in b/src/Makefile.in index 6645f40de..f94646363 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -1,7 +1,7 @@ -# Makefile.in generated by automake 1.14.1 from Makefile.am. +# Makefile.in generated by automake 1.15 from Makefile.am. # @configure_input@ -# Copyright (C) 1994-2013 Free Software Foundation, Inc. +# Copyright (C) 1994-2014 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -16,7 +16,17 @@ VPATH = @srcdir@ -am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ @@ -80,21 +90,60 @@ POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ sbin_PROGRAMS = netdata$(EXEEXT) -plugins_PROGRAMS = apps.plugin$(EXEEXT) +@MACOS_FALSE@plugins_PROGRAMS = apps.plugin$(EXEEXT) +@FREEBSD_TRUE@am__append_1 = \ +@FREEBSD_TRUE@ plugin_freebsd.c plugin_freebsd.h \ +@FREEBSD_TRUE@ freebsd_sysctl.c \ +@FREEBSD_TRUE@ $(NULL) + +@FREEBSD_FALSE@@MACOS_TRUE@am__append_2 = \ +@FREEBSD_FALSE@@MACOS_TRUE@ plugin_macos.c plugin_macos.h \ +@FREEBSD_FALSE@@MACOS_TRUE@ macos_sysctl.c \ +@FREEBSD_FALSE@@MACOS_TRUE@ macos_mach_smi.c \ +@FREEBSD_FALSE@@MACOS_TRUE@ macos_fw.c \ +@FREEBSD_FALSE@@MACOS_TRUE@ $(NULL) + +@FREEBSD_FALSE@@MACOS_FALSE@am__append_3 = \ +@FREEBSD_FALSE@@MACOS_FALSE@ ipc.c ipc.h \ +@FREEBSD_FALSE@@MACOS_FALSE@ plugin_proc.c plugin_proc.h \ +@FREEBSD_FALSE@@MACOS_FALSE@ plugin_proc_diskspace.c plugin_proc_diskspace.h \ +@FREEBSD_FALSE@@MACOS_FALSE@ proc_diskstats.c \ +@FREEBSD_FALSE@@MACOS_FALSE@ proc_interrupts.c \ +@FREEBSD_FALSE@@MACOS_FALSE@ proc_softirqs.c \ +@FREEBSD_FALSE@@MACOS_FALSE@ proc_loadavg.c \ +@FREEBSD_FALSE@@MACOS_FALSE@ proc_meminfo.c \ +@FREEBSD_FALSE@@MACOS_FALSE@ proc_net_dev.c \ +@FREEBSD_FALSE@@MACOS_FALSE@ proc_net_ip_vs_stats.c \ +@FREEBSD_FALSE@@MACOS_FALSE@ proc_net_netstat.c \ +@FREEBSD_FALSE@@MACOS_FALSE@ proc_net_rpc_nfs.c \ +@FREEBSD_FALSE@@MACOS_FALSE@ proc_net_rpc_nfsd.c \ +@FREEBSD_FALSE@@MACOS_FALSE@ proc_net_snmp.c \ +@FREEBSD_FALSE@@MACOS_FALSE@ proc_net_snmp6.c \ +@FREEBSD_FALSE@@MACOS_FALSE@ proc_net_softnet_stat.c \ +@FREEBSD_FALSE@@MACOS_FALSE@ proc_net_stat_conntrack.c \ +@FREEBSD_FALSE@@MACOS_FALSE@ proc_net_stat_synproxy.c \ +@FREEBSD_FALSE@@MACOS_FALSE@ proc_stat.c \ +@FREEBSD_FALSE@@MACOS_FALSE@ proc_sys_kernel_random_entropy_avail.c \ +@FREEBSD_FALSE@@MACOS_FALSE@ proc_vmstat.c \ +@FREEBSD_FALSE@@MACOS_FALSE@ proc_uptime.c \ +@FREEBSD_FALSE@@MACOS_FALSE@ sys_kernel_mm_ksm.c \ +@FREEBSD_FALSE@@MACOS_FALSE@ $(NULL) + subdir = src -DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ - $(top_srcdir)/depcomp $(dist_cache_DATA) $(dist_log_DATA) \ - $(dist_registry_DATA) $(dist_varlib_DATA) ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_c___atomic.m4 \ $(top_srcdir)/m4/ax_c__generic.m4 \ $(top_srcdir)/m4/ax_c_mallinfo.m4 \ $(top_srcdir)/m4/ax_c_mallopt.m4 \ $(top_srcdir)/m4/ax_check_compile_flag.m4 \ + $(top_srcdir)/m4/ax_gcc_func_attribute.m4 \ $(top_srcdir)/m4/ax_pthread.m4 $(top_srcdir)/m4/jemalloc.m4 \ $(top_srcdir)/m4/tcmalloc.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(dist_cache_DATA) \ + $(dist_log_DATA) $(dist_registry_DATA) $(dist_varlib_DATA) \ + $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = @@ -104,33 +153,91 @@ am__installdirs = "$(DESTDIR)$(pluginsdir)" "$(DESTDIR)$(sbindir)" \ "$(DESTDIR)$(registrydir)" "$(DESTDIR)$(varlibdir)" PROGRAMS = $(plugins_PROGRAMS) $(sbin_PROGRAMS) am_apps_plugin_OBJECTS = apps_plugin.$(OBJEXT) avl.$(OBJEXT) \ - common.$(OBJEXT) log.$(OBJEXT) procfile.$(OBJEXT) \ - web_buffer.$(OBJEXT) + clocks.$(OBJEXT) common.$(OBJEXT) log.$(OBJEXT) \ + procfile.$(OBJEXT) web_buffer.$(OBJEXT) apps_plugin_OBJECTS = $(am_apps_plugin_OBJECTS) apps_plugin_LDADD = $(LDADD) -am_netdata_OBJECTS = appconfig.$(OBJEXT) avl.$(OBJEXT) \ - common.$(OBJEXT) daemon.$(OBJEXT) dictionary.$(OBJEXT) \ - eval.$(OBJEXT) global_statistics.$(OBJEXT) health.$(OBJEXT) \ - log.$(OBJEXT) main.$(OBJEXT) plugin_checks.$(OBJEXT) \ +am__netdata_SOURCES_DIST = appconfig.c appconfig.h \ + adaptive_resortable_list.c adaptive_resortable_list.h avl.c \ + avl.h backends.c backends.h clocks.c clocks.h common.c \ + common.h daemon.c daemon.h dictionary.c dictionary.h eval.c \ + eval.h global_statistics.c global_statistics.h health.c \ + health.h inlined.h log.c log.h main.c main.h plugin_checks.c \ + plugin_checks.h plugin_idlejitter.c plugin_idlejitter.h \ + plugin_nfacct.c plugin_nfacct.h plugin_tc.c plugin_tc.h \ + plugins_d.c plugins_d.h popen.c popen.h socket.c socket.h \ + simple_pattern.c simple_pattern.h sys_fs_cgroup.c \ + sys_devices_system_edac_mc.c sys_devices_system_node.c \ + procfile.c procfile.h proc_self_mountinfo.c \ + proc_self_mountinfo.h registry.c registry.h \ + registry_internals.c registry_internals.h registry_url.c \ + registry_url.h registry_person.c registry_person.h \ + registry_machine.c registry_machine.h registry_init.c \ + registry_db.c registry_log.c rrd.c rrd.h rrd2json.c rrd2json.h \ + storage_number.c storage_number.h unit_test.c unit_test.h \ + url.c url.h web_buffer.c web_buffer.h web_buffer_svg.c \ + web_buffer_svg.h web_client.c web_client.h web_server.c \ + web_server.h plugin_freebsd.c plugin_freebsd.h \ + freebsd_sysctl.c plugin_macos.c plugin_macos.h macos_sysctl.c \ + macos_mach_smi.c macos_fw.c ipc.c ipc.h plugin_proc.c \ + plugin_proc.h plugin_proc_diskspace.c plugin_proc_diskspace.h \ + proc_diskstats.c proc_interrupts.c proc_softirqs.c \ + proc_loadavg.c proc_meminfo.c proc_net_dev.c \ + proc_net_ip_vs_stats.c proc_net_netstat.c proc_net_rpc_nfs.c \ + proc_net_rpc_nfsd.c proc_net_snmp.c proc_net_snmp6.c \ + proc_net_softnet_stat.c proc_net_stat_conntrack.c \ + proc_net_stat_synproxy.c proc_stat.c \ + proc_sys_kernel_random_entropy_avail.c proc_vmstat.c \ + proc_uptime.c sys_kernel_mm_ksm.c +@FREEBSD_TRUE@am__objects_1 = plugin_freebsd.$(OBJEXT) \ +@FREEBSD_TRUE@ freebsd_sysctl.$(OBJEXT) +@FREEBSD_FALSE@@MACOS_TRUE@am__objects_2 = plugin_macos.$(OBJEXT) \ +@FREEBSD_FALSE@@MACOS_TRUE@ macos_sysctl.$(OBJEXT) \ +@FREEBSD_FALSE@@MACOS_TRUE@ macos_mach_smi.$(OBJEXT) \ +@FREEBSD_FALSE@@MACOS_TRUE@ macos_fw.$(OBJEXT) +@FREEBSD_FALSE@@MACOS_FALSE@am__objects_3 = ipc.$(OBJEXT) \ +@FREEBSD_FALSE@@MACOS_FALSE@ plugin_proc.$(OBJEXT) \ +@FREEBSD_FALSE@@MACOS_FALSE@ plugin_proc_diskspace.$(OBJEXT) \ +@FREEBSD_FALSE@@MACOS_FALSE@ proc_diskstats.$(OBJEXT) \ +@FREEBSD_FALSE@@MACOS_FALSE@ proc_interrupts.$(OBJEXT) \ +@FREEBSD_FALSE@@MACOS_FALSE@ proc_softirqs.$(OBJEXT) \ +@FREEBSD_FALSE@@MACOS_FALSE@ proc_loadavg.$(OBJEXT) \ +@FREEBSD_FALSE@@MACOS_FALSE@ proc_meminfo.$(OBJEXT) \ +@FREEBSD_FALSE@@MACOS_FALSE@ proc_net_dev.$(OBJEXT) \ +@FREEBSD_FALSE@@MACOS_FALSE@ proc_net_ip_vs_stats.$(OBJEXT) \ +@FREEBSD_FALSE@@MACOS_FALSE@ proc_net_netstat.$(OBJEXT) \ +@FREEBSD_FALSE@@MACOS_FALSE@ proc_net_rpc_nfs.$(OBJEXT) \ +@FREEBSD_FALSE@@MACOS_FALSE@ proc_net_rpc_nfsd.$(OBJEXT) \ +@FREEBSD_FALSE@@MACOS_FALSE@ proc_net_snmp.$(OBJEXT) \ +@FREEBSD_FALSE@@MACOS_FALSE@ proc_net_snmp6.$(OBJEXT) \ +@FREEBSD_FALSE@@MACOS_FALSE@ proc_net_softnet_stat.$(OBJEXT) \ +@FREEBSD_FALSE@@MACOS_FALSE@ proc_net_stat_conntrack.$(OBJEXT) \ +@FREEBSD_FALSE@@MACOS_FALSE@ proc_net_stat_synproxy.$(OBJEXT) \ +@FREEBSD_FALSE@@MACOS_FALSE@ proc_stat.$(OBJEXT) \ +@FREEBSD_FALSE@@MACOS_FALSE@ proc_sys_kernel_random_entropy_avail.$(OBJEXT) \ +@FREEBSD_FALSE@@MACOS_FALSE@ proc_vmstat.$(OBJEXT) \ +@FREEBSD_FALSE@@MACOS_FALSE@ proc_uptime.$(OBJEXT) \ +@FREEBSD_FALSE@@MACOS_FALSE@ sys_kernel_mm_ksm.$(OBJEXT) +am_netdata_OBJECTS = appconfig.$(OBJEXT) \ + adaptive_resortable_list.$(OBJEXT) avl.$(OBJEXT) \ + backends.$(OBJEXT) clocks.$(OBJEXT) common.$(OBJEXT) \ + daemon.$(OBJEXT) dictionary.$(OBJEXT) eval.$(OBJEXT) \ + global_statistics.$(OBJEXT) health.$(OBJEXT) log.$(OBJEXT) \ + main.$(OBJEXT) plugin_checks.$(OBJEXT) \ plugin_idlejitter.$(OBJEXT) plugin_nfacct.$(OBJEXT) \ - plugin_proc.$(OBJEXT) plugin_tc.$(OBJEXT) plugins_d.$(OBJEXT) \ - popen.$(OBJEXT) proc_diskstats.$(OBJEXT) \ - proc_interrupts.$(OBJEXT) proc_softirqs.$(OBJEXT) \ - proc_loadavg.$(OBJEXT) proc_meminfo.$(OBJEXT) \ - proc_net_dev.$(OBJEXT) proc_net_ip_vs_stats.$(OBJEXT) \ - proc_net_netstat.$(OBJEXT) proc_net_rpc_nfs.$(OBJEXT) \ - proc_net_rpc_nfsd.$(OBJEXT) proc_net_snmp.$(OBJEXT) \ - proc_net_snmp6.$(OBJEXT) proc_net_softnet_stat.$(OBJEXT) \ - proc_net_stat_conntrack.$(OBJEXT) \ - proc_net_stat_synproxy.$(OBJEXT) proc_stat.$(OBJEXT) \ - proc_self_mountinfo.$(OBJEXT) \ - proc_sys_kernel_random_entropy_avail.$(OBJEXT) \ - proc_vmstat.$(OBJEXT) sys_kernel_mm_ksm.$(OBJEXT) \ - sys_fs_cgroup.$(OBJEXT) procfile.$(OBJEXT) registry.$(OBJEXT) \ - rrd.$(OBJEXT) rrd2json.$(OBJEXT) storage_number.$(OBJEXT) \ - unit_test.$(OBJEXT) url.$(OBJEXT) web_buffer.$(OBJEXT) \ - web_buffer_svg.$(OBJEXT) web_client.$(OBJEXT) \ - web_server.$(OBJEXT) + plugin_tc.$(OBJEXT) plugins_d.$(OBJEXT) popen.$(OBJEXT) \ + socket.$(OBJEXT) simple_pattern.$(OBJEXT) \ + sys_fs_cgroup.$(OBJEXT) sys_devices_system_edac_mc.$(OBJEXT) \ + sys_devices_system_node.$(OBJEXT) procfile.$(OBJEXT) \ + proc_self_mountinfo.$(OBJEXT) registry.$(OBJEXT) \ + registry_internals.$(OBJEXT) registry_url.$(OBJEXT) \ + registry_person.$(OBJEXT) registry_machine.$(OBJEXT) \ + registry_init.$(OBJEXT) registry_db.$(OBJEXT) \ + registry_log.$(OBJEXT) rrd.$(OBJEXT) rrd2json.$(OBJEXT) \ + storage_number.$(OBJEXT) unit_test.$(OBJEXT) url.$(OBJEXT) \ + web_buffer.$(OBJEXT) web_buffer_svg.$(OBJEXT) \ + web_client.$(OBJEXT) web_server.$(OBJEXT) $(am__objects_1) \ + $(am__objects_2) $(am__objects_3) netdata_OBJECTS = $(am_netdata_OBJECTS) am__DEPENDENCIES_1 = netdata_DEPENDENCIES = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ @@ -164,7 +271,7 @@ am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(apps_plugin_SOURCES) $(netdata_SOURCES) -DIST_SOURCES = $(apps_plugin_SOURCES) $(netdata_SOURCES) +DIST_SOURCES = $(apps_plugin_SOURCES) $(am__netdata_SOURCES_DIST) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ @@ -218,6 +325,7 @@ am__define_uniq_tagged_files = \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags +am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ @@ -381,58 +489,27 @@ dist_cache_DATA = .keep dist_varlib_DATA = .keep dist_registry_DATA = .keep dist_log_DATA = .keep -netdata_SOURCES = \ - appconfig.c appconfig.h \ - avl.c avl.h \ - common.c common.h \ - daemon.c daemon.h \ - dictionary.c dictionary.h \ - eval.c eval.h \ - global_statistics.c global_statistics.h \ - health.c health.h \ - log.c log.h \ - main.c main.h \ - plugin_checks.c plugin_checks.h \ - plugin_idlejitter.c plugin_idlejitter.h \ - plugin_nfacct.c plugin_nfacct.h \ - plugin_proc.c plugin_proc.h \ - plugin_tc.c plugin_tc.h \ - plugins_d.c plugins_d.h \ - popen.c popen.h \ - proc_diskstats.c \ - proc_interrupts.c \ - proc_softirqs.c \ - proc_loadavg.c \ - proc_meminfo.c \ - proc_net_dev.c \ - proc_net_ip_vs_stats.c \ - proc_net_netstat.c \ - proc_net_rpc_nfs.c \ - proc_net_rpc_nfsd.c \ - proc_net_snmp.c \ - proc_net_snmp6.c \ - proc_net_softnet_stat.c \ - proc_net_stat_conntrack.c \ - proc_net_stat_synproxy.c \ - proc_stat.c \ - proc_self_mountinfo.c proc_self_mountinfo.h \ - proc_sys_kernel_random_entropy_avail.c \ - proc_vmstat.c \ - sys_kernel_mm_ksm.c \ - sys_fs_cgroup.c \ - procfile.c procfile.h \ - registry.c registry.h \ - rrd.c rrd.h \ - rrd2json.c rrd2json.h \ - storage_number.c storage_number.h \ - unit_test.c unit_test.h \ - url.c url.h \ - web_buffer.c web_buffer.h \ - web_buffer_svg.c web_buffer_svg.h \ - web_client.c web_client.h \ - web_server.c web_server.h \ - $(NULL) - +netdata_SOURCES = appconfig.c appconfig.h adaptive_resortable_list.c \ + adaptive_resortable_list.h avl.c avl.h backends.c backends.h \ + clocks.c clocks.h common.c common.h daemon.c daemon.h \ + dictionary.c dictionary.h eval.c eval.h global_statistics.c \ + global_statistics.h health.c health.h inlined.h log.c log.h \ + main.c main.h plugin_checks.c plugin_checks.h \ + plugin_idlejitter.c plugin_idlejitter.h plugin_nfacct.c \ + plugin_nfacct.h plugin_tc.c plugin_tc.h plugins_d.c \ + plugins_d.h popen.c popen.h socket.c socket.h simple_pattern.c \ + simple_pattern.h sys_fs_cgroup.c sys_devices_system_edac_mc.c \ + sys_devices_system_node.c procfile.c procfile.h \ + proc_self_mountinfo.c proc_self_mountinfo.h registry.c \ + registry.h registry_internals.c registry_internals.h \ + registry_url.c registry_url.h registry_person.c \ + registry_person.h registry_machine.c registry_machine.h \ + registry_init.c registry_db.c registry_log.c rrd.c rrd.h \ + rrd2json.c rrd2json.h storage_number.c storage_number.h \ + unit_test.c unit_test.h url.c url.h web_buffer.c web_buffer.h \ + web_buffer_svg.c web_buffer_svg.h web_client.c web_client.h \ + web_server.c web_server.h $(NULL) $(am__append_1) \ + $(am__append_2) $(am__append_3) netdata_LDADD = \ $(OPTIONAL_MATH_LIBS) \ $(OPTIONAL_NFACCT_LIBS) \ @@ -443,7 +520,9 @@ netdata_LDADD = \ apps_plugin_SOURCES = \ apps_plugin.c \ avl.c avl.h \ + clocks.c clocks.h \ common.c common.h \ + inlined.h \ log.c log.h \ procfile.c procfile.h \ web_buffer.c web_buffer.h \ @@ -465,7 +544,6 @@ $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__confi echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu src/Makefile -.PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ @@ -582,21 +660,32 @@ mostlyclean-compile: distclean-compile: -rm -f *.tab.c +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/adaptive_resortable_list.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/appconfig.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/apps_plugin.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/avl.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/backends.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/clocks.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/common.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/daemon.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dictionary.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/eval.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/freebsd_sysctl.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/global_statistics.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/health.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ipc.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/log.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/macos_fw.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/macos_mach_smi.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/macos_sysctl.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/main.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/plugin_checks.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/plugin_freebsd.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/plugin_idlejitter.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/plugin_macos.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/plugin_nfacct.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/plugin_proc.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/plugin_proc_diskspace.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/plugin_tc.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/plugins_d.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/popen.Po@am__quote@ @@ -618,12 +707,24 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/proc_softirqs.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/proc_stat.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/proc_sys_kernel_random_entropy_avail.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/proc_uptime.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/proc_vmstat.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/procfile.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/registry.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/registry_db.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/registry_init.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/registry_internals.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/registry_log.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/registry_machine.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/registry_person.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/registry_url.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rrd.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rrd2json.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/simple_pattern.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/socket.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/storage_number.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sys_devices_system_edac_mc.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sys_devices_system_node.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sys_fs_cgroup.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sys_kernel_mm_ksm.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/unit_test.Po@am__quote@ @@ -946,6 +1047,8 @@ uninstall-am: uninstall-dist_cacheDATA uninstall-dist_logDATA \ uninstall-dist_registryDATA uninstall-dist_varlibDATA \ uninstall-pluginsPROGRAMS uninstall-sbinPROGRAMS +.PRECIOUS: Makefile + install-data-hook: if [ `id -u` == 0 ]; then \ diff --git a/src/adaptive_resortable_list.c b/src/adaptive_resortable_list.c new file mode 100644 index 000000000..a37c396fa --- /dev/null +++ b/src/adaptive_resortable_list.c @@ -0,0 +1,222 @@ +#include "adaptive_resortable_list.h" + +// the default processor() of the ARL +// can be overwritten at arl_create() +static inline void arl_callback_str2ull(const char *name, uint32_t hash, const char *value, void *dst) { + (void)name; + (void)hash; + + register unsigned long long *d = dst; + *d = str2ull(value); + // fprintf(stderr, "name '%s' with hash %u and value '%s' is %llu\n", name, hash, value, *d); +} + +// create a new ARL +ARL_BASE *arl_create(const char *name, void (*processor)(const char *, uint32_t, const char *, void *), size_t rechecks) { + ARL_BASE *base = callocz(1, sizeof(ARL_BASE)); + + base->name = strdupz(name); + + if(!processor) + base->processor = arl_callback_str2ull; + else + base->processor = processor; + + base->rechecks = rechecks; + + return base; +} + +void arl_free(ARL_BASE *arl_base) { + if(unlikely(!arl_base)) + return; + + while(arl_base->head) { + ARL_ENTRY *e = arl_base->head; + arl_base->head = e->next; + + freez(e->name); +#ifdef NETDATA_INTERNAL_CHECKS + memset(e, 0, sizeof(ARL_ENTRY)); +#endif + freez(e); + } + + freez(arl_base->name); + +#ifdef NETDATA_INTERNAL_CHECKS + memset(arl_base, 0, sizeof(ARL_BASE)); +#endif + + freez(arl_base); +} + +void arl_begin(ARL_BASE *base) { + ARL_ENTRY *e; + +#ifdef NETDATA_INTERNAL_CHECKS + if(likely(base->iteration > 10)) { + // do these checks after the ARL has been sorted + + if(unlikely(base->relinkings > (base->expected + base->allocated))) + info("ARL '%s' has %zu relinkings with %zu expected and %zu allocated entries. Is the source changing so fast?" + , base->name, base->relinkings, base->expected, base->allocated); + + if(unlikely(base->slow > base->fast)) + info("ARL '%s' has %zu fast searches and %zu slow searches. Is the source really changing so fast?" + , base->name, base->fast, base->slow); + + if(unlikely(base->iteration % 60 == 0)) { + info("ARL '%s' statistics: iteration %zu, expected %zu, wanted %zu, allocated %zu, fred %zu, relinkings %zu, found %zu, added %zu, fast %zu, slow %zu" + , base->name + , base->iteration + , base->expected + , base->wanted + , base->allocated + , base->fred + , base->relinkings + , base->found + , base->added + , base->fast + , base->slow + ); + // for(e = base->head; e; e = e->next) fprintf(stderr, "%s ", e->name); + // fprintf(stderr, "\n"); + } + } +#endif + + if(unlikely(base->added || base->iteration % base->rechecks) == 1) { + base->added = 0; + base->wanted = 0; + for(e = base->head; e ; e = e->next) { + if(e->flags & ARL_ENTRY_FLAG_FOUND) { + + // remove the found flag + e->flags &= ~ARL_ENTRY_FLAG_FOUND; + + // count it in wanted + if(e->flags & ARL_ENTRY_FLAG_EXPECTED) + base->wanted++; + } + else if(e->flags & ARL_ENTRY_FLAG_DYNAMIC) { + // we can remove this entry + // it is not found, and it was created because + // it was found in the source file + if(e->next) e->next->prev = e->prev; + if(e->prev) e->prev->next = e->next; + if(base->head == e) base->head = e->next; + freez(e->name); + freez(e); + + base->fred++; + } + } + } + + base->iteration++; + base->next_keyword = base->head; + base->found = 0; +} + +// register an expected keyword to the ARL +// together with its destination ( i.e. the output of the processor() ) +ARL_ENTRY *arl_expect(ARL_BASE *base, const char *keyword, void *dst) { + ARL_ENTRY *e = callocz(1, sizeof(ARL_ENTRY)); + e->name = strdupz(keyword); + e->hash = simple_hash(e->name); + e->dst = dst; + e->flags = ARL_ENTRY_FLAG_EXPECTED; + e->prev = NULL; + e->next = base->head; + + if(base->head) base->head->prev = e; + else base->next_keyword = e; + + base->head = e; + base->expected++; + base->allocated++; + + base->wanted = base->expected; + + return e; +} + +int arl_find_or_create_and_relink(ARL_BASE *base, const char *s, const char *value) { + ARL_ENTRY *e; + + uint32_t hash = simple_hash(s); + + // find if it already exists in the data + for(e = base->head; e ; e = e->next) + if(e->hash == hash && !strcmp(e->name, s)) + break; + +#ifdef NETDATA_INTERNAL_CHECKS + if(unlikely(e == base->next_keyword)) + fatal("Internal Error: e == base->last"); +#endif + + if(e) { + // found it in the keywords + + base->relinkings++; + + // run the processor for it + if(unlikely(e->dst)) { + base->processor(e->name, hash, value, e->dst); + base->found++; + } + + // unlink it - we will relink it below + if(e->next) e->next->prev = e->prev; + if(e->prev) e->prev->next = e->next; + + // make sure the head is properly linked + if(base->head == e) + base->head = e->next; + } + else { + // not found + + // create it + e = callocz(1, sizeof(ARL_ENTRY)); + e->name = strdupz(s); + e->hash = hash; + e->flags = ARL_ENTRY_FLAG_DYNAMIC; + + base->allocated++; + base->added++; + } + +#ifdef NETDATA_INTERNAL_CHECKS + if(unlikely(base->iteration % 60 == 0 && e->flags & ARL_ENTRY_FLAG_FOUND)) + info("ARL '%s': entry '%s' is already found. Did you forget to call arl_begin()?", base->name, s); +#endif + + e->flags |= ARL_ENTRY_FLAG_FOUND; + + // link it here + e->next = base->next_keyword; + if(base->next_keyword) { + e->prev = base->next_keyword->prev; + base->next_keyword->prev = e; + + if(e->prev) + e->prev->next = e; + + if(base->head == base->next_keyword) + base->head = e; + } + else + e->prev = NULL; + + base->next_keyword = e->next; + if(unlikely(!base->next_keyword)) + base->next_keyword = base->head; + + if(unlikely(base->found == base->wanted)) + return 1; + + return 0; +} diff --git a/src/adaptive_resortable_list.h b/src/adaptive_resortable_list.h new file mode 100644 index 000000000..609cd0c43 --- /dev/null +++ b/src/adaptive_resortable_list.h @@ -0,0 +1,162 @@ +#ifndef NETDATA_ADAPTIVE_RESORTABLE_LIST_H +#define NETDATA_ADAPTIVE_RESORTABLE_LIST_H + +/* + * ADAPTIVE RE-SORTABLE LIST + * This structure allows netdata to read a file of NAME VALUE lines + * in the fastest possible way. + * + * It maintains a linked list of all NAME (keywords), sorted in the + * same order as found in the source data file. + * The linked list is kept sorted at all times - the source file + * may change at any time, the list will adapt. + * + * The caller: + * + * 1. calls arl_create() to create a list + * + * 2. calls arl_expect() to register the expected keyword + * + * Then: + * + * 3. calls arl_begin() to initiate a data collection iteration. + * This is to be called just ONCE every time the source is re-scanned. + * + * 4. calls arl_check() for each line read from the file. + * + * Finally: + * + * 5. calls arl_free() to destroy this and free all memory. + * + * The program will call the processor() function, given to + * arl_create(), for each expected keyword found. + * The default processor() expects dst to be an unsigned long long *. + * + * LIMITATIONS + * DO NOT USE THIS IF THE A NAME/KEYWORD MAY APPEAR MORE THAN + * ONCE IN THE SOURCE DATA SET. + */ + +#include "common.h" + +#define ARL_ENTRY_FLAG_FOUND 0x01 // the entry has been found in the source data +#define ARL_ENTRY_FLAG_EXPECTED 0x02 // the entry is expected by the program +#define ARL_ENTRY_FLAG_DYNAMIC 0x04 // the entry was dynamically allocated, from source data + +typedef struct arl_entry { + char *name; // the keywords + uint32_t hash; // the hash of the keyword + + void *dst; // the dst to pass to the processor + + uint8_t flags; // ARL_ENTRY_FLAG_* + + // double linked list for fast re-linkings + struct arl_entry *prev, *next; +} ARL_ENTRY; + +typedef struct arl_base { + char *name; + + size_t iteration; // incremented on each iteration (arl_begin()) + size_t found; // the number of expected keywords found in this iteration + size_t expected; // the number of expected keywords + size_t wanted; // the number of wanted keywords + // i.e. the number of keywords found and expected + + size_t relinkings; // the number of relinkings we have made so far + + size_t allocated; // the number of keywords allocated + size_t fred; // the number of keywords cleaned up + + size_t rechecks; // the number of iterations between re-checks of the + // wanted number of keywords + // this is only needed in cases where the source + // is having less lines over time. + + size_t added; // it is non-zero if new keywords have been added + // this is only needed to detect new lines have + // been added to the file, over time. + +#ifdef NETDATA_INTERNAL_CHECKS + size_t fast; // the number of times we have taken the fast path + size_t slow; // the number of times we have taken the slow path +#endif + + // the processor to do the job + void (*processor)(const char *name, uint32_t hash, const char *value, void *dst); + + // the linked list of the keywords + ARL_ENTRY *head; + + // since we keep the list of keywords sorted (as found in the source data) + // this is next keyword that we expect to find in the source data. + ARL_ENTRY *next_keyword; +} ARL_BASE; + +// create a new ARL +extern ARL_BASE *arl_create(const char *name, void (*processor)(const char *, uint32_t, const char *, void *), size_t rechecks); + +// free an ARL +extern void arl_free(ARL_BASE *arl_base); + +// register an expected keyword to the ARL +// together with its destination ( i.e. the output of the processor() ) +extern ARL_ENTRY *arl_expect(ARL_BASE *base, const char *keyword, void *dst); + +// an internal call to complete the check() call +extern int arl_find_or_create_and_relink(ARL_BASE *base, const char *s, const char *value); + +// begin an ARL iteration +extern void arl_begin(ARL_BASE *base); + +// check a keyword against the ARL +// this is to be called for each keyword read from source data +// s = the keyword, as collected +// src = the src data to be passed to the processor +// it is defined in the header file in order to be inlined +static inline int arl_check(ARL_BASE *base, const char *keyword, const char *value) { + ARL_ENTRY *e = base->next_keyword; + +#ifdef NETDATA_INTERNAL_CHECKS + if(unlikely((base->fast + base->slow) % (base->expected + base->allocated) == 0 && (base->fast + base->slow) > (base->expected + base->allocated) * base->iteration)) + info("ARL '%s': Did you forget to call arl_begin()?", base->name); +#endif + + // it should be the first entry (pointed by base->next_keyword) + if(likely(!strcmp(keyword, e->name))) { + // it is + +#ifdef NETDATA_INTERNAL_CHECKS + base->fast++; +#endif + + e->flags |= ARL_ENTRY_FLAG_FOUND; + + // execute the processor + if(unlikely(e->dst)) { + base->processor(e->name, e->hash, value, e->dst); + base->found++; + } + + // be prepared for the next iteration + base->next_keyword = e->next; + if(unlikely(!base->next_keyword)) + base->next_keyword = base->head; + + // stop if we collected all the values for this iteration + if(unlikely(base->found == base->wanted)) + return 1; + + return 0; + } + +#ifdef NETDATA_INTERNAL_CHECKS + base->slow++; +#endif + + // we read from source, a not-expected keyword + return arl_find_or_create_and_relink(base, keyword, value); +} + +#endif //NETDATA_ADAPTIVE_RESORTABLE_LIST_H diff --git a/src/appconfig.c b/src/appconfig.c index 947407484..81ab01be2 100644 --- a/src/appconfig.c +++ b/src/appconfig.c @@ -72,8 +72,8 @@ static int config_value_compare(void* a, void* b) { else return strcmp(((struct config_value *)a)->name, ((struct config_value *)b)->name); } -#define config_value_index_add(co, cv) avl_insert_lock(&((co)->values_index), (avl *)(cv)) -#define config_value_index_del(co, cv) avl_remove_lock(&((co)->values_index), (avl *)(cv)) +#define config_value_index_add(co, cv) (struct config_value *)avl_insert_lock(&((co)->values_index), (avl *)(cv)) +#define config_value_index_del(co, cv) (struct config_value *)avl_remove_lock(&((co)->values_index), (avl *)(cv)) static struct config_value *config_value_index_find(struct config *co, const char *name, uint32_t hash) { struct config_value tmp; @@ -98,8 +98,8 @@ avl_tree_lock config_root_index = { AVL_LOCK_INITIALIZER }; -#define config_index_add(cfg) avl_insert_lock(&config_root_index, (avl *)(cfg)) -#define config_index_del(cfg) avl_remove_lock(&config_root_index, (avl *)(cfg)) +#define config_index_add(cfg) (struct config *)avl_insert_lock(&config_root_index, (avl *)(cfg)) +#define config_index_del(cfg) (struct config *)avl_remove_lock(&config_root_index, (avl *)(cfg)) static struct config *config_index_find(const char *name, uint32_t hash) { struct config tmp; @@ -127,7 +127,8 @@ static inline struct config *config_section_create(const char *section) avl_init_lock(&co->values_index, config_value_compare); - config_index_add(co); + if(unlikely(config_index_add(co) != co)) + error("INTERNAL ERROR: indexing of section '%s', already exists.", co->name); config_global_write_lock(); struct config *co2 = config_root; @@ -154,7 +155,8 @@ static inline struct config_value *config_value_create(struct config *co, const cv->hash = simple_hash(cv->name); cv->value = strdupz(value); - config_value_index_add(co, cv); + if(unlikely(config_value_index_add(co, cv) != cv)) + error("INTERNAL ERROR: indexing of config '%s' in section '%s': already exists.", cv->name, co->name); config_section_write_lock(co); struct config_value *cv2 = co->values; @@ -198,14 +200,15 @@ int config_rename(const char *section, const char *old, const char *new) { cv2 = config_value_index_find(co, new, 0); if(cv2) goto cleanup; - config_value_index_del(co, cv); + if(unlikely(config_value_index_del(co, cv) != cv)) + error("INTERNAL ERROR: deletion of config '%s' from section '%s', deleted tge wrong config entry.", cv->name, co->name); freez(cv->name); cv->name = strdupz(new); - cv->hash = simple_hash(cv->name); + if(unlikely(config_value_index_add(co, cv) != cv)) + error("INTERNAL ERROR: indexing of config '%s' in section '%s', already exists.", cv->name, co->name); - config_value_index_add(co, cv); config_section_unlock(co); return 0; @@ -490,9 +493,10 @@ void generate_config(BUFFER *wb, int only_changed) config_global_write_lock(); for(co = config_root; co ; co = co->next) { if(!strcmp(co->name, "global") || - !strcmp(co->name, "plugins") || + !strcmp(co->name, "plugins") || !strcmp(co->name, "registry") || - !strcmp(co->name, "health")) + !strcmp(co->name, "health") || + !strcmp(co->name, "backend")) pri = 0; else if(!strncmp(co->name, "plugin:", 7)) pri = 1; else pri = 2; diff --git a/src/apps_plugin.c b/src/apps_plugin.c index f22a575ba..0a72190aa 100644 --- a/src/apps_plugin.c +++ b/src/apps_plugin.c @@ -13,6 +13,8 @@ // etc. #define RATES_DETAIL 10000ULL +#define MAX_SPARE_FDS 1 + int debug = 0; int update_every = 1; @@ -30,6 +32,8 @@ int show_guest_time_old = 0; int enable_guest_charts = 0; int enable_file_charts = 1; +int enable_users_charts = 1; +int enable_groups_charts = 1; // ---------------------------------------------------------------------------- @@ -84,7 +88,9 @@ struct target { unsigned long long io_storage_bytes_written; // unsigned long long io_cancelled_write_bytes; - int *fds; + int *target_fds; + int target_fds_size; + unsigned long long openfiles; unsigned long long openpipes; unsigned long long opensockets; @@ -119,7 +125,7 @@ long apps_groups_targets = 0; struct target *users_root_target = NULL; struct target *groups_root_target = NULL; -struct target *get_users_target(uid_t uid) +static struct target *get_users_target(uid_t uid) { struct target *w; for(w = users_root_target ; w ; w = w->next) @@ -187,10 +193,11 @@ struct target *get_groups_target(gid_t gid) // find or create a new target // there are targets that are just aggregated to other target (the second argument) -struct target *get_apps_groups_target(const char *id, struct target *target) { - int tdebug = 0, thidden = 0, ends_with = 0; +static struct target *get_apps_groups_target(const char *id, struct target *target, const char *name) { + int tdebug = 0, thidden = target?target->hidden:0, ends_with = 0; const char *nid = id; + // extract the options while(nid[0] == '-' || nid[0] == '+' || nid[0] == '*') { if(nid[0] == '-') thidden = 1; if(nid[0] == '+') tdebug = 1; @@ -199,6 +206,7 @@ struct target *get_apps_groups_target(const char *id, struct target *target) { } uint32_t hash = simple_hash(id); + // find if it already exists struct target *w, *last = apps_groups_root_target; for(w = apps_groups_root_target ; w ; w = w->next) { if(w->idhash == hash && strncmp(nid, w->id, MAX_NAME) == 0) @@ -207,11 +215,37 @@ struct target *get_apps_groups_target(const char *id, struct target *target) { last = w; } + // find an existing target + if(unlikely(!target)) { + while(*name == '-') { + if(*name == '-') thidden = 1; + name++; + } + for(target = apps_groups_root_target ; target ; target = target->next) { + if(!target->target && strcmp(name, target->name) == 0) + break; + } + if(unlikely(debug)) { + if(unlikely(target)) + fprintf(stderr, "apps.plugin: REUSING TARGET NAME '%s' on ID '%s'\n", target->name, target->id); + else + fprintf(stderr, "apps.plugin: NEW TARGET NAME '%s' on ID '%s'\n", name, id); + } + } + + if(target && target->target) + fatal("Internal Error: request to link process '%s' to target '%s' which is linked to target '%s'", id, target->id, target->target->id); + w = callocz(sizeof(struct target), 1); strncpyz(w->id, nid, MAX_NAME); w->idhash = simple_hash(w->id); - strncpyz(w->name, nid, MAX_NAME); + if(unlikely(!target)) + // copy the name + strncpyz(w->name, name, MAX_NAME); + else + // copy the id + strncpyz(w->name, nid, MAX_NAME); strncpyz(w->compare, nid, MAX_COMPARE_NAME); size_t len = strlen(w->compare); @@ -239,7 +273,7 @@ struct target *get_apps_groups_target(const char *id, struct target *target) { fprintf(stderr, "apps.plugin: ADDING TARGET ID '%s', process name '%s' (%s), aggregated on target '%s', options: %s %s\n" , w->id , w->compare, (w->starts_with && w->ends_with)?"substring":((w->starts_with)?"prefix":((w->ends_with)?"suffix":"exact")) - , w->target?w->target->id:w->id + , w->target?w->target->name:w->name , (w->hidden)?"hidden":"-" , (w->debug)?"debug":"-" ); @@ -248,11 +282,11 @@ struct target *get_apps_groups_target(const char *id, struct target *target) { } // read the apps_groups.conf file -int read_apps_groups_conf(const char *name) +static int read_apps_groups_conf(const char *file) { char filename[FILENAME_MAX + 1]; - snprintfz(filename, FILENAME_MAX, "%s/apps_%s.conf", config_dir, name); + snprintfz(filename, FILENAME_MAX, "%s/apps_%s.conf", config_dir, file); if(unlikely(debug)) fprintf(stderr, "apps.plugin: process groups file: '%s'\n", filename); @@ -272,59 +306,45 @@ int read_apps_groups_conf(const char *name) for(line = 0; line < lines ;line++) { unsigned long word, words = procfile_linewords(ff, line); - struct target *w = NULL; + if(!words) continue; + + char *name = procfile_lineword(ff, line, 0); + if(!name || !*name) continue; - char *t = procfile_lineword(ff, line, 0); - if(!t || !*t) continue; + // find a possibly existing target + struct target *w = NULL; + // loop through all words, skipping the first one (the name) for(word = 0; word < words ;word++) { char *s = procfile_lineword(ff, line, word); if(!s || !*s) continue; if(*s == '#') break; - if(t == s) continue; + // is this the first word? skip it + if(s == name) continue; - struct target *n = get_apps_groups_target(s, w); + // add this target + struct target *n = get_apps_groups_target(s, w, name); if(!n) { error("Cannot create target '%s' (line %lu, word %lu)", s, line, word); continue; } - if(!w) w = n; - } - - if(w) { - int tdebug = 0, thidden = 0; - - while(t[0] == '-' || t[0] == '+') { - if(t[0] == '-') thidden = 1; - if(t[0] == '+') tdebug = 1; - t++; - } - - strncpyz(w->name, t, MAX_NAME); - w->hidden = thidden; - w->debug = tdebug; - - if(unlikely(debug)) - fprintf(stderr, "apps.plugin: AGGREGATION TARGET NAME '%s' on ID '%s', process name '%s' (%s), aggregated on target '%s', options: %s %s\n" - , w->name - , w->id - , w->compare, (w->starts_with && w->ends_with)?"substring":((w->starts_with)?"prefix":((w->ends_with)?"suffix":"exact")) - , w->target?w->target->id:w->id - , (w->hidden)?"hidden":"-" - , (w->debug)?"debug":"-" - ); + // just some optimization + // to avoid searching for a target for each process + if(!w) w = n->target?n->target:n; } } procfile_close(ff); - apps_groups_default_target = get_apps_groups_target("p+!o@w#e$i^r&7*5(-i)l-o_", NULL); // match nothing + apps_groups_default_target = get_apps_groups_target("p+!o@w#e$i^r&7*5(-i)l-o_", NULL, "other"); // match nothing if(!apps_groups_default_target) - error("Cannot create default target"); - else - strncpyz(apps_groups_default_target->name, "other", MAX_NAME); + fatal("Cannot create default target"); + + // allow the user to override group 'other' + if(apps_groups_default_target->target) + apps_groups_default_target = apps_groups_default_target->target; return 0; } @@ -467,15 +487,15 @@ struct pid_stat { long all_pids_count = 0; -struct pid_stat *get_pid_entry(pid_t pid) { +static inline struct pid_stat *get_pid_entry(pid_t pid) { if(all_pids[pid]) { all_pids[pid]->new_entry = 0; return all_pids[pid]; } all_pids[pid] = callocz(sizeof(struct pid_stat), 1); - all_pids[pid]->fds = callocz(sizeof(int), 100); - all_pids[pid]->fds_size = 100; + all_pids[pid]->fds = callocz(sizeof(int), MAX_SPARE_FDS); + all_pids[pid]->fds_size = MAX_SPARE_FDS; if(root_of_pids) root_of_pids->prev = all_pids[pid]; all_pids[pid]->next = root_of_pids; @@ -489,7 +509,7 @@ struct pid_stat *get_pid_entry(pid_t pid) { return all_pids[pid]; } -void del_pid_entry(pid_t pid) { +static inline void del_pid_entry(pid_t pid) { if(!all_pids[pid]) { error("attempted to free pid %d that is not allocated.", pid); return; @@ -517,7 +537,7 @@ void del_pid_entry(pid_t pid) { // ---------------------------------------------------------------------------- // update pids from proc -int read_proc_pid_cmdline(struct pid_stat *p) { +static inline int read_proc_pid_cmdline(struct pid_stat *p) { if(unlikely(!p->cmdline_filename)) { char filename[FILENAME_MAX + 1]; @@ -548,7 +568,7 @@ cleanup: return 0; } -int read_proc_pid_ownership(struct pid_stat *p) { +static inline int read_proc_pid_ownership(struct pid_stat *p) { if(unlikely(!p->stat_filename)) { error("pid %d does not have a stat_filename", p->pid); return 0; @@ -569,7 +589,7 @@ int read_proc_pid_ownership(struct pid_stat *p) { return 1; } -int read_proc_pid_stat(struct pid_stat *p) { +static inline int read_proc_pid_stat(struct pid_stat *p) { static procfile *ff = NULL; if(unlikely(!p->stat_filename)) { @@ -590,89 +610,89 @@ int read_proc_pid_stat(struct pid_stat *p) { if(unlikely(!ff)) goto cleanup; p->last_stat_collected_usec = p->stat_collected_usec; - p->stat_collected_usec = time_usec(); + p->stat_collected_usec = now_realtime_usec(); file_counter++; - // p->pid = atol(procfile_lineword(ff, 0, 0+i)); + // p->pid = str2ul(procfile_lineword(ff, 0, 0+i)); strncpyz(p->comm, procfile_lineword(ff, 0, 1), MAX_COMPARE_NAME); // p->state = *(procfile_lineword(ff, 0, 2)); - p->ppid = (int32_t) atol(procfile_lineword(ff, 0, 3)); - // p->pgrp = atol(procfile_lineword(ff, 0, 4)); - // p->session = atol(procfile_lineword(ff, 0, 5)); - // p->tty_nr = atol(procfile_lineword(ff, 0, 6)); - // p->tpgid = atol(procfile_lineword(ff, 0, 7)); - // p->flags = strtoull(procfile_lineword(ff, 0, 8), NULL, 10); + p->ppid = (int32_t)str2ul(procfile_lineword(ff, 0, 3)); + // p->pgrp = str2ul(procfile_lineword(ff, 0, 4)); + // p->session = str2ul(procfile_lineword(ff, 0, 5)); + // p->tty_nr = str2ul(procfile_lineword(ff, 0, 6)); + // p->tpgid = str2ul(procfile_lineword(ff, 0, 7)); + // p->flags = str2ull(procfile_lineword(ff, 0, 8)); unsigned long long last; last = p->minflt_raw; - p->minflt_raw = strtoull(procfile_lineword(ff, 0, 9), NULL, 10); - p->minflt = (p->minflt_raw - last) * (1000000ULL * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec); + p->minflt_raw = str2ull(procfile_lineword(ff, 0, 9)); + p->minflt = (p->minflt_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec); last = p->cminflt_raw; - p->cminflt_raw = strtoull(procfile_lineword(ff, 0, 10), NULL, 10); - p->cminflt = (p->cminflt_raw - last) * (1000000ULL * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec); + p->cminflt_raw = str2ull(procfile_lineword(ff, 0, 10)); + p->cminflt = (p->cminflt_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec); last = p->majflt_raw; - p->majflt_raw = strtoull(procfile_lineword(ff, 0, 11), NULL, 10); - p->majflt = (p->majflt_raw - last) * (1000000ULL * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec); + p->majflt_raw = str2ull(procfile_lineword(ff, 0, 11)); + p->majflt = (p->majflt_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec); last = p->cmajflt_raw; - p->cmajflt_raw = strtoull(procfile_lineword(ff, 0, 12), NULL, 10); - p->cmajflt = (p->cmajflt_raw - last) * (1000000ULL * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec); + p->cmajflt_raw = str2ull(procfile_lineword(ff, 0, 12)); + p->cmajflt = (p->cmajflt_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec); last = p->utime_raw; - p->utime_raw = strtoull(procfile_lineword(ff, 0, 13), NULL, 10); - p->utime = (p->utime_raw - last) * (1000000ULL * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec); + p->utime_raw = str2ull(procfile_lineword(ff, 0, 13)); + p->utime = (p->utime_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec); last = p->stime_raw; - p->stime_raw = strtoull(procfile_lineword(ff, 0, 14), NULL, 10); - p->stime = (p->stime_raw - last) * (1000000ULL * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec); + p->stime_raw = str2ull(procfile_lineword(ff, 0, 14)); + p->stime = (p->stime_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec); last = p->cutime_raw; - p->cutime_raw = strtoull(procfile_lineword(ff, 0, 15), NULL, 10); - p->cutime = (p->cutime_raw - last) * (1000000ULL * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec); + p->cutime_raw = str2ull(procfile_lineword(ff, 0, 15)); + p->cutime = (p->cutime_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec); last = p->cstime_raw; - p->cstime_raw = strtoull(procfile_lineword(ff, 0, 16), NULL, 10); - p->cstime = (p->cstime_raw - last) * (1000000ULL * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec); - - // p->priority = strtoull(procfile_lineword(ff, 0, 17), NULL, 10); - // p->nice = strtoull(procfile_lineword(ff, 0, 18), NULL, 10); - p->num_threads = (int32_t) atol(procfile_lineword(ff, 0, 19)); - // p->itrealvalue = strtoull(procfile_lineword(ff, 0, 20), NULL, 10); - // p->starttime = strtoull(procfile_lineword(ff, 0, 21), NULL, 10); - // p->vsize = strtoull(procfile_lineword(ff, 0, 22), NULL, 10); - // p->rss = strtoull(procfile_lineword(ff, 0, 23), NULL, 10); - // p->rsslim = strtoull(procfile_lineword(ff, 0, 24), NULL, 10); - // p->starcode = strtoull(procfile_lineword(ff, 0, 25), NULL, 10); - // p->endcode = strtoull(procfile_lineword(ff, 0, 26), NULL, 10); - // p->startstack = strtoull(procfile_lineword(ff, 0, 27), NULL, 10); - // p->kstkesp = strtoull(procfile_lineword(ff, 0, 28), NULL, 10); - // p->kstkeip = strtoull(procfile_lineword(ff, 0, 29), NULL, 10); - // p->signal = strtoull(procfile_lineword(ff, 0, 30), NULL, 10); - // p->blocked = strtoull(procfile_lineword(ff, 0, 31), NULL, 10); - // p->sigignore = strtoull(procfile_lineword(ff, 0, 32), NULL, 10); - // p->sigcatch = strtoull(procfile_lineword(ff, 0, 33), NULL, 10); - // p->wchan = strtoull(procfile_lineword(ff, 0, 34), NULL, 10); - // p->nswap = strtoull(procfile_lineword(ff, 0, 35), NULL, 10); - // p->cnswap = strtoull(procfile_lineword(ff, 0, 36), NULL, 10); - // p->exit_signal = atol(procfile_lineword(ff, 0, 37)); - // p->processor = atol(procfile_lineword(ff, 0, 38)); - // p->rt_priority = strtoul(procfile_lineword(ff, 0, 39), NULL, 10); - // p->policy = strtoul(procfile_lineword(ff, 0, 40), NULL, 10); - // p->delayacct_blkio_ticks = strtoull(procfile_lineword(ff, 0, 41), NULL, 10); + p->cstime_raw = str2ull(procfile_lineword(ff, 0, 16)); + p->cstime = (p->cstime_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec); + + // p->priority = str2ull(procfile_lineword(ff, 0, 17)); + // p->nice = str2ull(procfile_lineword(ff, 0, 18)); + p->num_threads = (int32_t)str2ul(procfile_lineword(ff, 0, 19)); + // p->itrealvalue = str2ull(procfile_lineword(ff, 0, 20)); + // p->starttime = str2ull(procfile_lineword(ff, 0, 21)); + // p->vsize = str2ull(procfile_lineword(ff, 0, 22)); + // p->rss = str2ull(procfile_lineword(ff, 0, 23)); + // p->rsslim = str2ull(procfile_lineword(ff, 0, 24)); + // p->starcode = str2ull(procfile_lineword(ff, 0, 25)); + // p->endcode = str2ull(procfile_lineword(ff, 0, 26)); + // p->startstack = str2ull(procfile_lineword(ff, 0, 27)); + // p->kstkesp = str2ull(procfile_lineword(ff, 0, 28)); + // p->kstkeip = str2ull(procfile_lineword(ff, 0, 29)); + // p->signal = str2ull(procfile_lineword(ff, 0, 30)); + // p->blocked = str2ull(procfile_lineword(ff, 0, 31)); + // p->sigignore = str2ull(procfile_lineword(ff, 0, 32)); + // p->sigcatch = str2ull(procfile_lineword(ff, 0, 33)); + // p->wchan = str2ull(procfile_lineword(ff, 0, 34)); + // p->nswap = str2ull(procfile_lineword(ff, 0, 35)); + // p->cnswap = str2ull(procfile_lineword(ff, 0, 36)); + // p->exit_signal = str2ul(procfile_lineword(ff, 0, 37)); + // p->processor = str2ul(procfile_lineword(ff, 0, 38)); + // p->rt_priority = str2ul(procfile_lineword(ff, 0, 39)); + // p->policy = str2ul(procfile_lineword(ff, 0, 40)); + // p->delayacct_blkio_ticks = str2ull(procfile_lineword(ff, 0, 41)); if(enable_guest_charts) { last = p->gtime_raw; - p->gtime_raw = strtoull(procfile_lineword(ff, 0, 42), NULL, 10); - p->gtime = (p->gtime_raw - last) * (1000000ULL * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec); + p->gtime_raw = str2ull(procfile_lineword(ff, 0, 42)); + p->gtime = (p->gtime_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec); last = p->cgtime_raw; - p->cgtime_raw = strtoull(procfile_lineword(ff, 0, 43), NULL, 10); - p->cgtime = (p->cgtime_raw - last) * (1000000ULL * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec); + p->cgtime_raw = str2ull(procfile_lineword(ff, 0, 43)); + p->cgtime = (p->cgtime_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec); if (show_guest_time || p->gtime || p->cgtime) { p->utime -= (p->utime >= p->gtime) ? p->gtime : p->utime; @@ -715,7 +735,7 @@ cleanup: return 0; } -int read_proc_pid_statm(struct pid_stat *p) { +static inline int read_proc_pid_statm(struct pid_stat *p) { static procfile *ff = NULL; if(unlikely(!p->statm_filename)) { @@ -732,13 +752,13 @@ int read_proc_pid_statm(struct pid_stat *p) { file_counter++; - p->statm_size = strtoull(procfile_lineword(ff, 0, 0), NULL, 10); - p->statm_resident = strtoull(procfile_lineword(ff, 0, 1), NULL, 10); - p->statm_share = strtoull(procfile_lineword(ff, 0, 2), NULL, 10); - // p->statm_text = strtoull(procfile_lineword(ff, 0, 3), NULL, 10); - // p->statm_lib = strtoull(procfile_lineword(ff, 0, 4), NULL, 10); - // p->statm_data = strtoull(procfile_lineword(ff, 0, 5), NULL, 10); - // p->statm_dirty = strtoull(procfile_lineword(ff, 0, 6), NULL, 10); + p->statm_size = str2ull(procfile_lineword(ff, 0, 0)); + p->statm_resident = str2ull(procfile_lineword(ff, 0, 1)); + p->statm_share = str2ull(procfile_lineword(ff, 0, 2)); + // p->statm_text = str2ull(procfile_lineword(ff, 0, 3)); + // p->statm_lib = str2ull(procfile_lineword(ff, 0, 4)); + // p->statm_data = str2ull(procfile_lineword(ff, 0, 5)); + // p->statm_dirty = str2ull(procfile_lineword(ff, 0, 6)); return 1; @@ -753,7 +773,7 @@ cleanup: return 0; } -int read_proc_pid_io(struct pid_stat *p) { +static inline int read_proc_pid_io(struct pid_stat *p) { static procfile *ff = NULL; if(unlikely(!p->io_filename)) { @@ -772,37 +792,37 @@ int read_proc_pid_io(struct pid_stat *p) { file_counter++; p->last_io_collected_usec = p->io_collected_usec; - p->io_collected_usec = time_usec(); + p->io_collected_usec = now_realtime_usec(); unsigned long long last; last = p->io_logical_bytes_read_raw; - p->io_logical_bytes_read_raw = strtoull(procfile_lineword(ff, 0, 1), NULL, 10); - p->io_logical_bytes_read = (p->io_logical_bytes_read_raw - last) * (1000000ULL * RATES_DETAIL) / (p->io_collected_usec - p->last_io_collected_usec); + p->io_logical_bytes_read_raw = str2ull(procfile_lineword(ff, 0, 1)); + p->io_logical_bytes_read = (p->io_logical_bytes_read_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (p->io_collected_usec - p->last_io_collected_usec); last = p->io_logical_bytes_written_raw; - p->io_logical_bytes_written_raw = strtoull(procfile_lineword(ff, 1, 1), NULL, 10); - p->io_logical_bytes_written = (p->io_logical_bytes_written_raw - last) * (1000000ULL * RATES_DETAIL) / (p->io_collected_usec - p->last_io_collected_usec); + p->io_logical_bytes_written_raw = str2ull(procfile_lineword(ff, 1, 1)); + p->io_logical_bytes_written = (p->io_logical_bytes_written_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (p->io_collected_usec - p->last_io_collected_usec); // last = p->io_read_calls_raw; - // p->io_read_calls_raw = strtoull(procfile_lineword(ff, 2, 1), NULL, 10); - // p->io_read_calls = (p->io_read_calls_raw - last) * (1000000ULL * RATES_DETAIL) / (p->io_collected_usec - p->last_io_collected_usec); + // p->io_read_calls_raw = str2ull(procfile_lineword(ff, 2, 1)); + // p->io_read_calls = (p->io_read_calls_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (p->io_collected_usec - p->last_io_collected_usec); // last = p->io_write_calls_raw; - // p->io_write_calls_raw = strtoull(procfile_lineword(ff, 3, 1), NULL, 10); - // p->io_write_calls = (p->io_write_calls_raw - last) * (1000000ULL * RATES_DETAIL) / (p->io_collected_usec - p->last_io_collected_usec); + // p->io_write_calls_raw = str2ull(procfile_lineword(ff, 3, 1)); + // p->io_write_calls = (p->io_write_calls_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (p->io_collected_usec - p->last_io_collected_usec); last = p->io_storage_bytes_read_raw; - p->io_storage_bytes_read_raw = strtoull(procfile_lineword(ff, 4, 1), NULL, 10); - p->io_storage_bytes_read = (p->io_storage_bytes_read_raw - last) * (1000000ULL * RATES_DETAIL) / (p->io_collected_usec - p->last_io_collected_usec); + p->io_storage_bytes_read_raw = str2ull(procfile_lineword(ff, 4, 1)); + p->io_storage_bytes_read = (p->io_storage_bytes_read_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (p->io_collected_usec - p->last_io_collected_usec); last = p->io_storage_bytes_written_raw; - p->io_storage_bytes_written_raw = strtoull(procfile_lineword(ff, 5, 1), NULL, 10); - p->io_storage_bytes_written = (p->io_storage_bytes_written_raw - last) * (1000000ULL * RATES_DETAIL) / (p->io_collected_usec - p->last_io_collected_usec); + p->io_storage_bytes_written_raw = str2ull(procfile_lineword(ff, 5, 1)); + p->io_storage_bytes_written = (p->io_storage_bytes_written_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (p->io_collected_usec - p->last_io_collected_usec); // last = p->io_cancelled_write_bytes_raw; - // p->io_cancelled_write_bytes_raw = strtoull(procfile_lineword(ff, 6, 1), NULL, 10); - // p->io_cancelled_write_bytes = (p->io_cancelled_write_bytes_raw - last) * (1000000ULL * RATES_DETAIL) / (p->io_collected_usec - p->last_io_collected_usec); + // p->io_cancelled_write_bytes_raw = str2ull(procfile_lineword(ff, 6, 1)); + // p->io_cancelled_write_bytes = (p->io_cancelled_write_bytes_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (p->io_collected_usec - p->last_io_collected_usec); if(unlikely(global_iterations_counter == 1)) { p->io_logical_bytes_read = 0; @@ -831,10 +851,11 @@ unsigned long long global_utime = 0; unsigned long long global_stime = 0; unsigned long long global_gtime = 0; -int read_proc_stat() { +static inline int read_proc_stat() { static char filename[FILENAME_MAX + 1] = ""; static procfile *ff = NULL; - static unsigned long long utime_raw = 0, stime_raw = 0, gtime_raw = 0, gntime_raw = 0, ntime_raw = 0, collected_usec = 0, last_collected_usec = 0; + static unsigned long long utime_raw = 0, stime_raw = 0, gtime_raw = 0, gntime_raw = 0, ntime_raw = 0; + static usec_t collected_usec = 0, last_collected_usec = 0; if(unlikely(!ff)) { snprintfz(filename, FILENAME_MAX, "%s/proc/stat", global_host_prefix); @@ -846,34 +867,34 @@ int read_proc_stat() { if(unlikely(!ff)) goto cleanup; last_collected_usec = collected_usec; - collected_usec = time_usec(); + collected_usec = now_realtime_usec(); file_counter++; unsigned long long last; last = utime_raw; - utime_raw = strtoull(procfile_lineword(ff, 0, 1), NULL, 10); - global_utime = (utime_raw - last) * (1000000ULL * RATES_DETAIL) / (collected_usec - last_collected_usec); + utime_raw = str2ull(procfile_lineword(ff, 0, 1)); + global_utime = (utime_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (collected_usec - last_collected_usec); // nice time, on user time last = ntime_raw; - ntime_raw = strtoull(procfile_lineword(ff, 0, 2), NULL, 10); - global_utime += (ntime_raw - last) * (1000000ULL * RATES_DETAIL) / (collected_usec - last_collected_usec); + ntime_raw = str2ull(procfile_lineword(ff, 0, 2)); + global_utime += (ntime_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (collected_usec - last_collected_usec); last = stime_raw; - stime_raw = strtoull(procfile_lineword(ff, 0, 3), NULL, 10); - global_stime = (stime_raw - last) * (1000000ULL * RATES_DETAIL) / (collected_usec - last_collected_usec); + stime_raw = str2ull(procfile_lineword(ff, 0, 3)); + global_stime = (stime_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (collected_usec - last_collected_usec); last = gtime_raw; - gtime_raw = strtoull(procfile_lineword(ff, 0, 10), NULL, 10); - global_gtime = (gtime_raw - last) * (1000000ULL * RATES_DETAIL) / (collected_usec - last_collected_usec); + gtime_raw = str2ull(procfile_lineword(ff, 0, 10)); + global_gtime = (gtime_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (collected_usec - last_collected_usec); if(enable_guest_charts) { // guest nice time, on guest time last = gntime_raw; - gntime_raw = strtoull(procfile_lineword(ff, 0, 11), NULL, 10); - global_gtime += (gntime_raw - last) * (1000000ULL * RATES_DETAIL) / (collected_usec - last_collected_usec); + gntime_raw = str2ull(procfile_lineword(ff, 0, 11)); + global_gtime += (gntime_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (collected_usec - last_collected_usec); // remove guest time from user time global_utime -= (global_utime > global_gtime) ? global_gtime : global_utime; @@ -966,7 +987,7 @@ static struct file_descriptor *file_descriptor_find(const char *name, uint32_t h #define FILETYPE_TIMERFD 7 #define FILETYPE_SIGNALFD 8 -void file_descriptor_not_used(int id) +static inline void file_descriptor_not_used(int id) { if(id > 0 && id < all_files_size) { @@ -987,7 +1008,9 @@ void file_descriptor_not_used(int id) if(unlikely(debug)) fprintf(stderr, "apps.plugin: >> slot %d is empty.\n", id); - file_descriptor_remove(&all_files[id]); + if(unlikely(file_descriptor_remove(&all_files[id]) != (void *)&all_files[id])) + error("INTERNAL ERROR: removal of unused fd from index, removed a different fd"); + #ifdef NETDATA_INTERNAL_CHECKS all_files[id].magic = 0x00000000; #endif /* NETDATA_INTERNAL_CHECKS */ @@ -1000,69 +1023,60 @@ void file_descriptor_not_used(int id) else error("Request to decrease counter of fd %d, which is outside the array size (1 to %d)", id, all_files_size); } -int file_descriptor_find_or_add(const char *name) -{ - static int last_pos = 0; - uint32_t hash = simple_hash(name); +static inline void all_files_grow() { + void *old = all_files; + int i; + // there is no empty slot if(unlikely(debug)) - fprintf(stderr, "apps.plugin: adding or finding name '%s' with hash %u\n", name, hash); + fprintf(stderr, "apps.plugin: extending fd array to %d entries\n", all_files_size + FILE_DESCRIPTORS_INCREASE_STEP); - struct file_descriptor *fd = file_descriptor_find(name, hash); - if(fd) { - // found - if(unlikely(debug)) - fprintf(stderr, "apps.plugin: >> found on slot %d\n", fd->pos); - - fd->count++; - return fd->pos; - } - // not found + all_files = reallocz(all_files, (all_files_size + FILE_DESCRIPTORS_INCREASE_STEP) * sizeof(struct file_descriptor)); - // check we have enough memory to add it - if(!all_files || all_files_len == all_files_size) { - void *old = all_files; - int i; + // if the address changed, we have to rebuild the index + // since all pointers are now invalid - // there is no empty slot + if(unlikely(old && old != (void *)all_files)) { if(unlikely(debug)) - fprintf(stderr, "apps.plugin: extending fd array to %d entries\n", all_files_size + FILE_DESCRIPTORS_INCREASE_STEP); + fprintf(stderr, "apps.plugin: >> re-indexing.\n"); - all_files = reallocz(all_files, (all_files_size + FILE_DESCRIPTORS_INCREASE_STEP) * sizeof(struct file_descriptor)); - - // if the address changed, we have to rebuild the index - // since all pointers are now invalid - if(old && old != (void *)all_files) { - if(unlikely(debug)) - fprintf(stderr, "apps.plugin: >> re-indexing.\n"); + all_files_index.root = NULL; + for(i = 0; i < all_files_size; i++) { + if(!all_files[i].count) continue; + if(unlikely(file_descriptor_add(&all_files[i]) != (void *)&all_files[i])) + error("INTERNAL ERROR: duplicate indexing of fd during realloc."); + } - all_files_index.root = NULL; - for(i = 0; i < all_files_size; i++) { - if(!all_files[i].count) continue; - file_descriptor_add(&all_files[i]); - } + if(unlikely(debug)) + fprintf(stderr, "apps.plugin: >> re-indexing done.\n"); + } - if(unlikely(debug)) - fprintf(stderr, "apps.plugin: >> re-indexing done.\n"); - } + // initialize the newly added entries - for(i = all_files_size; i < (all_files_size + FILE_DESCRIPTORS_INCREASE_STEP); i++) { - all_files[i].count = 0; - all_files[i].name = NULL; + for(i = all_files_size; i < (all_files_size + FILE_DESCRIPTORS_INCREASE_STEP); i++) { + all_files[i].count = 0; + all_files[i].name = NULL; #ifdef NETDATA_INTERNAL_CHECKS - all_files[i].magic = 0x00000000; + all_files[i].magic = 0x00000000; #endif /* NETDATA_INTERNAL_CHECKS */ - all_files[i].pos = i; - } - - if(!all_files_size) all_files_len = 1; - all_files_size += FILE_DESCRIPTORS_INCREASE_STEP; + all_files[i].pos = i; } + if(unlikely(!all_files_size)) all_files_len = 1; + all_files_size += FILE_DESCRIPTORS_INCREASE_STEP; +} + +static inline int file_descriptor_set_on_empty_slot(const char *name, uint32_t hash, int type) { + // check we have enough memory to add it + if(!all_files || all_files_len == all_files_size) + all_files_grow(); + if(unlikely(debug)) fprintf(stderr, "apps.plugin: >> searching for empty slot.\n"); // search for an empty slot + + static int last_pos = 0; int i, c; for(i = 0, c = last_pos ; i < all_files_size ; i++, c++) { if(c >= all_files_size) c = 0; @@ -1080,23 +1094,58 @@ int file_descriptor_find_or_add(const char *name) if(unlikely(debug)) fprintf(stderr, "apps.plugin: >> %s fd position %d for %s (last name: %s)\n", all_files[c].name?"re-using":"using", c, name, all_files[c].name); - if(all_files[c].name) freez((void *)all_files[c].name); + freez((void *)all_files[c].name); all_files[c].name = NULL; last_pos = c; break; } } + + all_files_len++; + if(i == all_files_size) { fatal("We should find an empty slot, but there isn't any"); exit(1); } + // else we have an empty slot in 'c' if(unlikely(debug)) fprintf(stderr, "apps.plugin: >> updating slot %d.\n", c); - all_files_len++; + all_files[c].name = strdupz(name); + all_files[c].hash = hash; + all_files[c].type = type; + all_files[c].pos = c; + all_files[c].count = 1; +#ifdef NETDATA_INTERNAL_CHECKS + all_files[c].magic = 0x0BADCAFE; +#endif /* NETDATA_INTERNAL_CHECKS */ + if(unlikely(file_descriptor_add(&all_files[c]) != (void *)&all_files[c])) + error("INTERNAL ERROR: duplicate indexing of fd."); - // else we have an empty slot in 'c' + if(unlikely(debug)) + fprintf(stderr, "apps.plugin: using fd position %d (name: %s)\n", c, all_files[c].name); + + return c; +} + +static inline int file_descriptor_find_or_add(const char *name) +{ + uint32_t hash = simple_hash(name); + + if(unlikely(debug)) + fprintf(stderr, "apps.plugin: adding or finding name '%s' with hash %u\n", name, hash); + + struct file_descriptor *fd = file_descriptor_find(name, hash); + if(fd) { + // found + if(unlikely(debug)) + fprintf(stderr, "apps.plugin: >> found on slot %d\n", fd->pos); + + fd->count++; + return fd->pos; + } + // not found int type; if(name[0] == '/') type = FILETYPE_FILE; @@ -1120,23 +1169,10 @@ int file_descriptor_find_or_add(const char *name) type = FILETYPE_OTHER; } - all_files[c].name = strdupz(name); - all_files[c].hash = hash; - all_files[c].type = type; - all_files[c].pos = c; - all_files[c].count = 1; -#ifdef NETDATA_INTERNAL_CHECKS - all_files[c].magic = 0x0BADCAFE; -#endif /* NETDATA_INTERNAL_CHECKS */ - file_descriptor_add(&all_files[c]); - - if(unlikely(debug)) - fprintf(stderr, "apps.plugin: using fd position %d (name: %s)\n", c, all_files[c].name); - - return c; + return file_descriptor_set_on_empty_slot(name, hash, type); } -int read_pid_file_descriptors(struct pid_stat *p) { +static inline int read_pid_file_descriptors(struct pid_stat *p) { char dirname[FILENAME_MAX+1]; snprintfz(dirname, FILENAME_MAX, "%s/proc/%d/fd", global_host_prefix, p->pid); @@ -1156,22 +1192,18 @@ int read_pid_file_descriptors(struct pid_stat *p) { continue; // check if the fds array is small - int fdid = atoi(de->d_name); + int fdid = (int)str2l(de->d_name); if(fdid < 0) continue; if(fdid >= p->fds_size) { // it is small, extend it if(unlikely(debug)) - fprintf(stderr, "apps.plugin: extending fd memory slots for %s from %d to %d\n", p->comm, p->fds_size, fdid + 100); + fprintf(stderr, "apps.plugin: extending fd memory slots for %s from %d to %d\n", p->comm, p->fds_size, fdid + MAX_SPARE_FDS); - p->fds = reallocz(p->fds, (fdid + 100) * sizeof(int)); - if(!p->fds) { - fatal("Cannot re-allocate fds for %s", p->comm); - break; - } + p->fds = reallocz(p->fds, (fdid + MAX_SPARE_FDS) * sizeof(int)); // and initialize it - for(c = p->fds_size ; c < (fdid + 100) ; c++) p->fds[c] = 0; - p->fds_size = fdid + 100; + for(c = p->fds_size ; c < (fdid + MAX_SPARE_FDS) ; c++) p->fds[c] = 0; + p->fds_size = fdid + MAX_SPARE_FDS; } if(p->fds[fdid] == 0) { @@ -1214,7 +1246,7 @@ int read_pid_file_descriptors(struct pid_stat *p) { // ---------------------------------------------------------------------------- -int print_process_and_parents(struct pid_stat *p, unsigned long long time) { +static inline int print_process_and_parents(struct pid_stat *p, unsigned long long time) { char *prefix = "\\_ "; int indent = 0; @@ -1253,13 +1285,13 @@ int print_process_and_parents(struct pid_stat *p, unsigned long long time) { return indent + 1; } -void print_process_tree(struct pid_stat *p, char *msg) { +static inline void print_process_tree(struct pid_stat *p, char *msg) { log_date(stderr); fprintf(stderr, "%s: process %s (%d, %s) with parents:\n", msg, p->comm, p->pid, p->updated?"running":"exited"); print_process_and_parents(p, p->stat_collected_usec); } -void find_lost_child_debug(struct pid_stat *pe, unsigned long long lost, int type) { +static inline void find_lost_child_debug(struct pid_stat *pe, unsigned long long lost, int type) { int found = 0; struct pid_stat *p = NULL; @@ -1329,7 +1361,7 @@ void find_lost_child_debug(struct pid_stat *pe, unsigned long long lost, int typ } } -unsigned long long remove_exited_child_from_parent(unsigned long long *field, unsigned long long *pfield) { +static inline unsigned long long remove_exited_child_from_parent(unsigned long long *field, unsigned long long *pfield) { unsigned long long absorbed = 0; if(*field > *pfield) { @@ -1346,7 +1378,7 @@ unsigned long long remove_exited_child_from_parent(unsigned long long *field, un return absorbed; } -void process_exited_processes() { +static inline void process_exited_processes() { struct pid_stat *p; for(p = root_of_pids; p ; p = p->next) { @@ -1439,11 +1471,11 @@ void process_exited_processes() { ); } - p->utime_raw = utime * (p->stat_collected_usec - p->last_stat_collected_usec) / (1000000ULL * RATES_DETAIL); - p->stime_raw = stime * (p->stat_collected_usec - p->last_stat_collected_usec) / (1000000ULL * RATES_DETAIL); - p->gtime_raw = gtime * (p->stat_collected_usec - p->last_stat_collected_usec) / (1000000ULL * RATES_DETAIL); - p->minflt_raw = minflt * (p->stat_collected_usec - p->last_stat_collected_usec) / (1000000ULL * RATES_DETAIL); - p->majflt_raw = majflt * (p->stat_collected_usec - p->last_stat_collected_usec) / (1000000ULL * RATES_DETAIL); + p->utime_raw = utime * (p->stat_collected_usec - p->last_stat_collected_usec) / (USEC_PER_SEC * RATES_DETAIL); + p->stime_raw = stime * (p->stat_collected_usec - p->last_stat_collected_usec) / (USEC_PER_SEC * RATES_DETAIL); + p->gtime_raw = gtime * (p->stat_collected_usec - p->last_stat_collected_usec) / (USEC_PER_SEC * RATES_DETAIL); + p->minflt_raw = minflt * (p->stat_collected_usec - p->last_stat_collected_usec) / (USEC_PER_SEC * RATES_DETAIL); + p->majflt_raw = majflt * (p->stat_collected_usec - p->last_stat_collected_usec) / (USEC_PER_SEC * RATES_DETAIL); p->cutime_raw = p->cstime_raw = p->cgtime_raw = p->cminflt_raw = p->cmajflt_raw = 0; if(unlikely(debug)) @@ -1459,7 +1491,7 @@ void process_exited_processes() { } } -void link_all_processes_to_their_parents(void) { +static inline void link_all_processes_to_their_parents(void) { struct pid_stat *p, *pp; // link all children to their parents @@ -1562,15 +1594,15 @@ static inline int managed_log(struct pid_stat *p, uint32_t log, int status) { return status; } -void collect_data_for_pid(pid_t pid) { +static inline int collect_data_for_pid(pid_t pid) { if(unlikely(pid <= 0 || pid > pid_max)) { error("Invalid pid %d read (expected 1 to %d). Ignoring process.", pid, pid_max); - return; + return 0; } struct pid_stat *p = get_pid_entry(pid); - if(unlikely(!p || p->read)) return; - p->read = 1; + if(unlikely(!p || p->read)) return 0; + p->read = 1; // fprintf(stderr, "Reading process %d (%s), sortlist %d\n", p->pid, p->comm, p->sortlist); @@ -1579,7 +1611,7 @@ void collect_data_for_pid(pid_t pid) { if(unlikely(!managed_log(p, PID_LOG_STAT, read_proc_pid_stat(p)))) // there is no reason to proceed if we cannot get its status - return; + return 0; read_proc_pid_ownership(p); @@ -1599,7 +1631,7 @@ void collect_data_for_pid(pid_t pid) { if(unlikely(!managed_log(p, PID_LOG_STATM, read_proc_pid_statm(p)))) // there is no reason to proceed if we cannot get its memory status - return; + return 0; // -------------------------------------------------------------------- // link it @@ -1658,9 +1690,11 @@ void collect_data_for_pid(pid_t pid) { p->updated = 1; p->keep = 0; p->keeploops = 0; + + return 1; } -int collect_data_for_all_processes_from_proc(void) { +static int collect_data_for_all_processes_from_proc(void) { struct pid_stat *p = NULL; if(all_pids_count) { @@ -1688,7 +1722,7 @@ int collect_data_for_all_processes_from_proc(void) { } if(include_exited_childs) { - qsort((void *)all_pids_sortlist, all_pids_count, sizeof(pid_t), compar_pid); + qsort((void *)all_pids_sortlist, (size_t)all_pids_count, sizeof(pid_t), compar_pid); for(slc = 0; slc < all_pids_count; slc++) collect_data_for_pid(all_pids_sortlist[slc]); } @@ -1714,6 +1748,9 @@ int collect_data_for_all_processes_from_proc(void) { } closedir(dir); + if(!all_pids_count) + return 0; + // normally this is done // however we may have processes exited while we collected values // so let's find the exited ones @@ -1743,7 +1780,7 @@ int collect_data_for_all_processes_from_proc(void) { // 9. find the unique file count for each target // check: update_apps_groups_statistics() -void cleanup_exited_pids(void) { +static void cleanup_exited_pids(void) { int c; struct pid_stat *p = NULL; @@ -1771,7 +1808,7 @@ void cleanup_exited_pids(void) { } } -void apply_apps_groups_targets_inheritance(void) { +static void apply_apps_groups_targets_inheritance(void) { struct pid_stat *p = NULL; // children that do not have a target @@ -1881,16 +1918,13 @@ void apply_apps_groups_targets_inheritance(void) { fprintf(stderr, "apps.plugin: apply_apps_groups_targets_inheritance() made %d loops on the process tree\n", loops); } -long zero_all_targets(struct target *root) { +static long zero_all_targets(struct target *root) { struct target *w; long count = 0; for (w = root; w ; w = w->next) { count++; - if(w->fds) freez(w->fds); - w->fds = NULL; - w->minflt = 0; w->majflt = 0; w->utime = 0; @@ -1920,132 +1954,168 @@ long zero_all_targets(struct target *root) { w->io_storage_bytes_read = 0; w->io_storage_bytes_written = 0; // w->io_cancelled_write_bytes = 0; + + // zero file counters + if(w->target_fds) { + memset(w->target_fds, 0, sizeof(int) * w->target_fds_size); + w->openfiles = 0; + w->openpipes = 0; + w->opensockets = 0; + w->openinotifies = 0; + w->openeventfds = 0; + w->opentimerfds = 0; + w->opensignalfds = 0; + w->openeventpolls = 0; + w->openother = 0; + } } return count; } -void aggregate_pid_on_target(struct target *w, struct pid_stat *p, struct target *o) { - (void)o; +static inline void reallocate_target_fds(struct target *w) { + if(unlikely(!w)) + return; - if(unlikely(!w->fds)) - w->fds = callocz(sizeof(int), (size_t) all_files_size); - - if(likely(p->updated)) { - w->cutime += p->cutime; - w->cstime += p->cstime; - w->cgtime += p->cgtime; - w->cminflt += p->cminflt; - w->cmajflt += p->cmajflt; - - w->utime += p->utime; - w->stime += p->stime; - w->gtime += p->gtime; - w->minflt += p->minflt; - w->majflt += p->majflt; - - // w->rss += p->rss; - - w->statm_size += p->statm_size; - w->statm_resident += p->statm_resident; - w->statm_share += p->statm_share; - // w->statm_text += p->statm_text; - // w->statm_lib += p->statm_lib; - // w->statm_data += p->statm_data; - // w->statm_dirty += p->statm_dirty; - - w->io_logical_bytes_read += p->io_logical_bytes_read; - w->io_logical_bytes_written += p->io_logical_bytes_written; - // w->io_read_calls += p->io_read_calls; - // w->io_write_calls += p->io_write_calls; - w->io_storage_bytes_read += p->io_storage_bytes_read; - w->io_storage_bytes_written += p->io_storage_bytes_written; - // w->io_cancelled_write_bytes += p->io_cancelled_write_bytes; - - w->processes++; - w->num_threads += p->num_threads; - - if(likely(w->fds)) { - int c; - for(c = 0; c < p->fds_size ;c++) { - if(p->fds[c] == 0) continue; - - if(likely(p->fds[c] < all_files_size)) { - if(w->fds) w->fds[p->fds[c]]++; - } - else - error("Invalid fd number %d", p->fds[c]); - } - } + if(unlikely(!w->target_fds || w->target_fds_size < all_files_size)) { + w->target_fds = reallocz(w->target_fds, sizeof(int) * all_files_size); + memset(&w->target_fds[w->target_fds_size], 0, sizeof(int) * (all_files_size - w->target_fds_size)); + w->target_fds_size = all_files_size; + } +} + +static inline void aggregate_fd_on_target(int fd, struct target *w) { + if(unlikely(!w)) + return; - if(unlikely(debug || w->debug)) - fprintf(stderr, "apps.plugin: \taggregating '%s' pid %d on target '%s' utime=%llu, stime=%llu, gtime=%llu, cutime=%llu, cstime=%llu, cgtime=%llu, minflt=%llu, majflt=%llu, cminflt=%llu, cmajflt=%llu\n", p->comm, p->pid, w->name, p->utime, p->stime, p->gtime, p->cutime, p->cstime, p->cgtime, p->minflt, p->majflt, p->cminflt, p->cmajflt); + if(unlikely(w->target_fds[fd])) { + // it is already aggregated + // just increase its usage counter + w->target_fds[fd]++; + return; + } + + // increase its usage counter + // so that we will not add it again + w->target_fds[fd]++; + + switch(all_files[fd].type) { + case FILETYPE_FILE: + w->openfiles++; + break; + + case FILETYPE_PIPE: + w->openpipes++; + break; + + case FILETYPE_SOCKET: + w->opensockets++; + break; + + case FILETYPE_INOTIFY: + w->openinotifies++; + break; + + case FILETYPE_EVENTFD: + w->openeventfds++; + break; + + case FILETYPE_TIMERFD: + w->opentimerfds++; + break; + + case FILETYPE_SIGNALFD: + w->opensignalfds++; + break; + + case FILETYPE_EVENTPOLL: + w->openeventpolls++; + break; + + default: + w->openother++; + break; } } -void count_targets_fds(struct target *root) { - int c; - struct target *w; +static inline void aggregate_pid_fds_on_targets(struct pid_stat *p) { - for (w = root; w ; w = w->next) { - if(!w->fds) continue; - - w->openfiles = 0; - w->openpipes = 0; - w->opensockets = 0; - w->openinotifies = 0; - w->openeventfds = 0; - w->opentimerfds = 0; - w->opensignalfds = 0; - w->openeventpolls = 0; - w->openother = 0; - - for(c = 1; c < all_files_size ;c++) { - if(w->fds[c] > 0) - switch(all_files[c].type) { - case FILETYPE_FILE: - w->openfiles++; - break; - - case FILETYPE_PIPE: - w->openpipes++; - break; - - case FILETYPE_SOCKET: - w->opensockets++; - break; - - case FILETYPE_INOTIFY: - w->openinotifies++; - break; - - case FILETYPE_EVENTFD: - w->openeventfds++; - break; - - case FILETYPE_TIMERFD: - w->opentimerfds++; - break; - - case FILETYPE_SIGNALFD: - w->opensignalfds++; - break; - - case FILETYPE_EVENTPOLL: - w->openeventpolls++; - break; - - default: - w->openother++; - } - } + if(unlikely(!p->updated)) { + // the process is not running + return; + } + + struct target *w = p->target, *u = p->user_target, *g = p->group_target; + + reallocate_target_fds(w); + reallocate_target_fds(u); + reallocate_target_fds(g); + + int c, size = p->fds_size, *fds = p->fds; + for(c = 0; c < size ;c++) { + int fd = fds[c]; + + if(likely(fd <= 0 || fd >= all_files_size)) + continue; + + aggregate_fd_on_target(fd, w); + aggregate_fd_on_target(fd, u); + aggregate_fd_on_target(fd, g); + } +} + +static inline void aggregate_pid_on_target(struct target *w, struct pid_stat *p, struct target *o) { + (void)o; - freez(w->fds); - w->fds = NULL; + if(unlikely(!p->updated)) { + // the process is not running + return; + } + + if(unlikely(!w)) { + error("pid %d %s was left without a target!", p->pid, p->comm); + return; } + + w->cutime += p->cutime; + w->cstime += p->cstime; + w->cgtime += p->cgtime; + w->cminflt += p->cminflt; + w->cmajflt += p->cmajflt; + + w->utime += p->utime; + w->stime += p->stime; + w->gtime += p->gtime; + w->minflt += p->minflt; + w->majflt += p->majflt; + + // w->rss += p->rss; + + w->statm_size += p->statm_size; + w->statm_resident += p->statm_resident; + w->statm_share += p->statm_share; + // w->statm_text += p->statm_text; + // w->statm_lib += p->statm_lib; + // w->statm_data += p->statm_data; + // w->statm_dirty += p->statm_dirty; + + w->io_logical_bytes_read += p->io_logical_bytes_read; + w->io_logical_bytes_written += p->io_logical_bytes_written; + // w->io_read_calls += p->io_read_calls; + // w->io_write_calls += p->io_write_calls; + w->io_storage_bytes_read += p->io_storage_bytes_read; + w->io_storage_bytes_written += p->io_storage_bytes_written; + // w->io_cancelled_write_bytes += p->io_cancelled_write_bytes; + + w->processes++; + w->num_threads += p->num_threads; + + if(unlikely(debug || w->debug)) + fprintf(stderr, "apps.plugin: \taggregating '%s' pid %d on target '%s' utime=%llu, stime=%llu, gtime=%llu, cutime=%llu, cstime=%llu, cgtime=%llu, minflt=%llu, majflt=%llu, cminflt=%llu, cmajflt=%llu\n", p->comm, p->pid, w->name, p->utime, p->stime, p->gtime, p->cutime, p->cstime, p->cgtime, p->minflt, p->majflt, p->cminflt, p->cmajflt); } -void calculate_netdata_statistics(void) { +static void calculate_netdata_statistics(void) { + apply_apps_groups_targets_inheritance(); zero_all_targets(users_root_target); @@ -2056,19 +2126,18 @@ void calculate_netdata_statistics(void) { struct pid_stat *p = NULL; struct target *w = NULL, *o = NULL; - // concentrate everything on the apps_groups_targets + // concentrate everything on the targets for(p = root_of_pids; p ; p = p->next) { // -------------------------------------------------------------------- - // apps_groups targets - if(likely(p->target)) - aggregate_pid_on_target(p->target, p, NULL); - else - error("pid %d %s was left without a target!", p->pid, p->comm); + // apps_groups target + + aggregate_pid_on_target(p->target, p, NULL); // -------------------------------------------------------------------- - // user targets + // user target + o = p->user_target; if(likely(p->user_target && p->user_target->uid == p->uid)) w = p->user_target; @@ -2079,14 +2148,12 @@ void calculate_netdata_statistics(void) { w = p->user_target = get_users_target(p->uid); } - if(likely(w)) - aggregate_pid_on_target(w, p, o); - else - error("pid %d %s was left without a user target!", p->pid, p->comm); + aggregate_pid_on_target(w, p, o); // -------------------------------------------------------------------- - // group targets + // user group target + o = p->group_target; if(likely(p->group_target && p->group_target->gid == p->gid)) w = p->group_target; @@ -2097,16 +2164,15 @@ void calculate_netdata_statistics(void) { w = p->group_target = get_groups_target(p->gid); } - if(likely(w)) - aggregate_pid_on_target(w, p, o); - else - error("pid %d %s was left without a group target!", p->pid, p->comm); + aggregate_pid_on_target(w, p, o); - } - count_targets_fds(apps_groups_root_target); - count_targets_fds(users_root_target); - count_targets_fds(groups_root_target); + // -------------------------------------------------------------------- + // aggregate all file descriptors + + if(enable_file_charts) + aggregate_pid_fds_on_targets(p); + } cleanup_exited_pids(); } @@ -2114,72 +2180,58 @@ void calculate_netdata_statistics(void) { // ---------------------------------------------------------------------------- // update chart dimensions -BUFFER *output = NULL; int print_calculated_number(char *str, calculated_number value) { (void)str; (void)value; return 0; } static inline void send_BEGIN(const char *type, const char *id, unsigned long long usec) { - // fprintf(stdout, "BEGIN %s.%s %llu\n", type, id, usec); - buffer_strcat(output, "BEGIN "); - buffer_strcat(output, type); - buffer_strcat(output, "."); - buffer_strcat(output, id); - buffer_strcat(output, " "); - buffer_print_llu(output, usec); - buffer_strcat(output, "\n"); + fprintf(stdout, "BEGIN %s.%s %llu\n", type, id, usec); } static inline void send_SET(const char *name, unsigned long long value) { - // fprintf(stdout, "SET %s = %llu\n", name, value); - buffer_strcat(output, "SET "); - buffer_strcat(output, name); - buffer_strcat(output, " = "); - buffer_print_llu(output, value); - buffer_strcat(output, "\n"); + fprintf(stdout, "SET %s = %llu\n", name, value); } static inline void send_END(void) { - // fprintf(stdout, "END\n"); - buffer_strcat(output, "END\n"); + fprintf(stdout, "END\n"); } double utime_fix_ratio = 1.0, stime_fix_ratio = 1.0, gtime_fix_ratio = 1.0, cutime_fix_ratio = 1.0, cstime_fix_ratio = 1.0, cgtime_fix_ratio = 1.0; double minflt_fix_ratio = 1.0, majflt_fix_ratio = 1.0, cminflt_fix_ratio = 1.0, cmajflt_fix_ratio = 1.0; -unsigned long long send_resource_usage_to_netdata() { +static usec_t send_resource_usage_to_netdata() { static struct timeval last = { 0, 0 }; static struct rusage me_last; struct timeval now; struct rusage me; - unsigned long long usec; - unsigned long long cpuuser; - unsigned long long cpusyst; + usec_t usec; + usec_t cpuuser; + usec_t cpusyst; if(!last.tv_sec) { - gettimeofday(&last, NULL); + now_realtime_timeval(&last); getrusage(RUSAGE_SELF, &me_last); // the first time, give a zero to allow // netdata calibrate to the current time - // usec = update_every * 1000000ULL; + // usec = update_every * USEC_PER_SEC; usec = 0ULL; cpuuser = 0; cpusyst = 0; } else { - gettimeofday(&now, NULL); + now_realtime_timeval(&now); getrusage(RUSAGE_SELF, &me); - usec = usec_dt(&now, &last); - cpuuser = me.ru_utime.tv_sec * 1000000ULL + me.ru_utime.tv_usec; - cpusyst = me.ru_stime.tv_sec * 1000000ULL + me.ru_stime.tv_usec; + usec = dt_usec(&now, &last); + cpuuser = me.ru_utime.tv_sec * USEC_PER_SEC + me.ru_utime.tv_usec; + cpusyst = me.ru_stime.tv_sec * USEC_PER_SEC + me.ru_stime.tv_usec; memmove(&last, &now, sizeof(struct timeval)); memmove(&me_last, &me, sizeof(struct rusage)); } - buffer_sprintf(output, + fprintf(stdout, "BEGIN netdata.apps_cpu %llu\n" "SET user = %llu\n" "SET system = %llu\n" @@ -2214,7 +2266,7 @@ unsigned long long send_resource_usage_to_netdata() { ); if(include_exited_childs) - buffer_sprintf(output, + fprintf(stdout, "BEGIN netdata.apps_children_fix %llu\n" "SET cutime = %llu\n" "SET cstime = %llu\n" @@ -2233,7 +2285,7 @@ unsigned long long send_resource_usage_to_netdata() { return usec; } -void normalize_data(struct target *root) { +static void normalize_data(struct target *root) { struct target *w; // childs processing introduces spikes @@ -2379,7 +2431,7 @@ void normalize_data(struct target *root) { } } -void send_collected_data_to_netdata(struct target *root, const char *type, unsigned long long usec) { +static void send_collected_data_to_netdata(struct target *root, const char *type, usec_t usec) { struct target *w; send_BEGIN(type, "cpu", usec); @@ -2510,7 +2562,7 @@ void send_collected_data_to_netdata(struct target *root, const char *type, unsig // ---------------------------------------------------------------------------- // generate the charts -void send_charts_updates_to_netdata(struct target *root, const char *type, const char *title) +static void send_charts_updates_to_netdata(struct target *root, const char *type, const char *title) { struct target *w; int newly_added = 0; @@ -2530,112 +2582,112 @@ void send_charts_updates_to_netdata(struct target *root, const char *type, const // we have something new to show // update the charts - buffer_sprintf(output, "CHART %s.cpu '' '%s CPU Time (%d%% = %d core%s)' 'cpu time %%' cpu %s.cpu stacked 20001 %d\n", type, title, (processors * 100), processors, (processors>1)?"s":"", type, update_every); + fprintf(stdout, "CHART %s.cpu '' '%s CPU Time (%d%% = %d core%s)' 'cpu time %%' cpu %s.cpu stacked 20001 %d\n", type, title, (processors * 100), processors, (processors>1)?"s":"", type, update_every); for (w = root; w ; w = w->next) { if(unlikely(w->exposed)) - buffer_sprintf(output, "DIMENSION %s '' absolute 1 %llu %s\n", w->name, hz * RATES_DETAIL / 100, w->hidden ? "hidden" : ""); + fprintf(stdout, "DIMENSION %s '' absolute 1 %llu %s\n", w->name, hz * RATES_DETAIL / 100, w->hidden ? "hidden" : ""); } - buffer_sprintf(output, "CHART %s.mem '' '%s Real Memory (w/o shared)' 'MB' mem %s.mem stacked 20003 %d\n", type, title, type, update_every); + fprintf(stdout, "CHART %s.mem '' '%s Real Memory (w/o shared)' 'MB' mem %s.mem stacked 20003 %d\n", type, title, type, update_every); for (w = root; w ; w = w->next) { if(unlikely(w->exposed)) - buffer_sprintf(output, "DIMENSION %s '' absolute %ld %ld\n", w->name, sysconf(_SC_PAGESIZE), 1024L*1024L); + fprintf(stdout, "DIMENSION %s '' absolute %ld %ld\n", w->name, sysconf(_SC_PAGESIZE), 1024L*1024L); } - buffer_sprintf(output, "CHART %s.vmem '' '%s Virtual Memory Size' 'MB' mem %s.vmem stacked 20004 %d\n", type, title, type, update_every); + fprintf(stdout, "CHART %s.vmem '' '%s Virtual Memory Size' 'MB' mem %s.vmem stacked 20004 %d\n", type, title, type, update_every); for (w = root; w ; w = w->next) { if(unlikely(w->exposed)) - buffer_sprintf(output, "DIMENSION %s '' absolute %ld %ld\n", w->name, sysconf(_SC_PAGESIZE), 1024L*1024L); + fprintf(stdout, "DIMENSION %s '' absolute %ld %ld\n", w->name, sysconf(_SC_PAGESIZE), 1024L*1024L); } - buffer_sprintf(output, "CHART %s.threads '' '%s Threads' 'threads' processes %s.threads stacked 20005 %d\n", type, title, type, update_every); + fprintf(stdout, "CHART %s.threads '' '%s Threads' 'threads' processes %s.threads stacked 20005 %d\n", type, title, type, update_every); for (w = root; w ; w = w->next) { if(unlikely(w->exposed)) - buffer_sprintf(output, "DIMENSION %s '' absolute 1 1\n", w->name); + fprintf(stdout, "DIMENSION %s '' absolute 1 1\n", w->name); } - buffer_sprintf(output, "CHART %s.processes '' '%s Processes' 'processes' processes %s.processes stacked 20004 %d\n", type, title, type, update_every); + fprintf(stdout, "CHART %s.processes '' '%s Processes' 'processes' processes %s.processes stacked 20004 %d\n", type, title, type, update_every); for (w = root; w ; w = w->next) { if(unlikely(w->exposed)) - buffer_sprintf(output, "DIMENSION %s '' absolute 1 1\n", w->name); + fprintf(stdout, "DIMENSION %s '' absolute 1 1\n", w->name); } - buffer_sprintf(output, "CHART %s.cpu_user '' '%s CPU User Time (%d%% = %d core%s)' 'cpu time %%' cpu %s.cpu_user stacked 20020 %d\n", type, title, (processors * 100), processors, (processors>1)?"s":"", type, update_every); + fprintf(stdout, "CHART %s.cpu_user '' '%s CPU User Time (%d%% = %d core%s)' 'cpu time %%' cpu %s.cpu_user stacked 20020 %d\n", type, title, (processors * 100), processors, (processors>1)?"s":"", type, update_every); for (w = root; w ; w = w->next) { if(unlikely(w->exposed)) - buffer_sprintf(output, "DIMENSION %s '' absolute 1 %llu\n", w->name, hz * RATES_DETAIL / 100LLU); + fprintf(stdout, "DIMENSION %s '' absolute 1 %llu\n", w->name, hz * RATES_DETAIL / 100LLU); } - buffer_sprintf(output, "CHART %s.cpu_system '' '%s CPU System Time (%d%% = %d core%s)' 'cpu time %%' cpu %s.cpu_system stacked 20021 %d\n", type, title, (processors * 100), processors, (processors>1)?"s":"", type, update_every); + fprintf(stdout, "CHART %s.cpu_system '' '%s CPU System Time (%d%% = %d core%s)' 'cpu time %%' cpu %s.cpu_system stacked 20021 %d\n", type, title, (processors * 100), processors, (processors>1)?"s":"", type, update_every); for (w = root; w ; w = w->next) { if(unlikely(w->exposed)) - buffer_sprintf(output, "DIMENSION %s '' absolute 1 %llu\n", w->name, hz * RATES_DETAIL / 100LLU); + fprintf(stdout, "DIMENSION %s '' absolute 1 %llu\n", w->name, hz * RATES_DETAIL / 100LLU); } if(show_guest_time) { - buffer_sprintf(output, "CHART %s.cpu_guest '' '%s CPU Guest Time (%d%% = %d core%s)' 'cpu time %%' cpu %s.cpu_system stacked 20022 %d\n", type, title, (processors * 100), processors, (processors > 1) ? "s" : "", type, update_every); + fprintf(stdout, "CHART %s.cpu_guest '' '%s CPU Guest Time (%d%% = %d core%s)' 'cpu time %%' cpu %s.cpu_system stacked 20022 %d\n", type, title, (processors * 100), processors, (processors > 1) ? "s" : "", type, update_every); for (w = root; w; w = w->next) { if(unlikely(w->exposed)) - buffer_sprintf(output, "DIMENSION %s '' absolute 1 %llu\n", w->name, hz * RATES_DETAIL / 100LLU); + fprintf(stdout, "DIMENSION %s '' absolute 1 %llu\n", w->name, hz * RATES_DETAIL / 100LLU); } } - buffer_sprintf(output, "CHART %s.major_faults '' '%s Major Page Faults (swap read)' 'page faults/s' swap %s.major_faults stacked 20010 %d\n", type, title, type, update_every); + fprintf(stdout, "CHART %s.major_faults '' '%s Major Page Faults (swap read)' 'page faults/s' swap %s.major_faults stacked 20010 %d\n", type, title, type, update_every); for (w = root; w ; w = w->next) { if(unlikely(w->exposed)) - buffer_sprintf(output, "DIMENSION %s '' absolute 1 %llu\n", w->name, RATES_DETAIL); + fprintf(stdout, "DIMENSION %s '' absolute 1 %llu\n", w->name, RATES_DETAIL); } - buffer_sprintf(output, "CHART %s.minor_faults '' '%s Minor Page Faults' 'page faults/s' mem %s.minor_faults stacked 20011 %d\n", type, title, type, update_every); + fprintf(stdout, "CHART %s.minor_faults '' '%s Minor Page Faults' 'page faults/s' mem %s.minor_faults stacked 20011 %d\n", type, title, type, update_every); for (w = root; w ; w = w->next) { if(unlikely(w->exposed)) - buffer_sprintf(output, "DIMENSION %s '' absolute 1 %llu\n", w->name, RATES_DETAIL); + fprintf(stdout, "DIMENSION %s '' absolute 1 %llu\n", w->name, RATES_DETAIL); } - buffer_sprintf(output, "CHART %s.lreads '' '%s Disk Logical Reads' 'kilobytes/s' disk %s.lreads stacked 20042 %d\n", type, title, type, update_every); + fprintf(stdout, "CHART %s.lreads '' '%s Disk Logical Reads' 'kilobytes/s' disk %s.lreads stacked 20042 %d\n", type, title, type, update_every); for (w = root; w ; w = w->next) { if(unlikely(w->exposed)) - buffer_sprintf(output, "DIMENSION %s '' absolute 1 %llu\n", w->name, 1024LLU * RATES_DETAIL); + fprintf(stdout, "DIMENSION %s '' absolute 1 %llu\n", w->name, 1024LLU * RATES_DETAIL); } - buffer_sprintf(output, "CHART %s.lwrites '' '%s I/O Logical Writes' 'kilobytes/s' disk %s.lwrites stacked 20042 %d\n", type, title, type, update_every); + fprintf(stdout, "CHART %s.lwrites '' '%s I/O Logical Writes' 'kilobytes/s' disk %s.lwrites stacked 20042 %d\n", type, title, type, update_every); for (w = root; w ; w = w->next) { if(unlikely(w->exposed)) - buffer_sprintf(output, "DIMENSION %s '' absolute 1 %llu\n", w->name, 1024LLU * RATES_DETAIL); + fprintf(stdout, "DIMENSION %s '' absolute 1 %llu\n", w->name, 1024LLU * RATES_DETAIL); } - buffer_sprintf(output, "CHART %s.preads '' '%s Disk Reads' 'kilobytes/s' disk %s.preads stacked 20002 %d\n", type, title, type, update_every); + fprintf(stdout, "CHART %s.preads '' '%s Disk Reads' 'kilobytes/s' disk %s.preads stacked 20002 %d\n", type, title, type, update_every); for (w = root; w ; w = w->next) { if(unlikely(w->exposed)) - buffer_sprintf(output, "DIMENSION %s '' absolute 1 %llu\n", w->name, 1024LLU * RATES_DETAIL); + fprintf(stdout, "DIMENSION %s '' absolute 1 %llu\n", w->name, 1024LLU * RATES_DETAIL); } - buffer_sprintf(output, "CHART %s.pwrites '' '%s Disk Writes' 'kilobytes/s' disk %s.pwrites stacked 20002 %d\n", type, title, type, update_every); + fprintf(stdout, "CHART %s.pwrites '' '%s Disk Writes' 'kilobytes/s' disk %s.pwrites stacked 20002 %d\n", type, title, type, update_every); for (w = root; w ; w = w->next) { if(unlikely(w->exposed)) - buffer_sprintf(output, "DIMENSION %s '' absolute 1 %llu\n", w->name, 1024LLU * RATES_DETAIL); + fprintf(stdout, "DIMENSION %s '' absolute 1 %llu\n", w->name, 1024LLU * RATES_DETAIL); } if(enable_file_charts) { - buffer_sprintf(output, "CHART %s.files '' '%s Open Files' 'open files' disk %s.files stacked 20050 %d\n", type, + fprintf(stdout, "CHART %s.files '' '%s Open Files' 'open files' disk %s.files stacked 20050 %d\n", type, title, type, update_every); for (w = root; w; w = w->next) { if (unlikely(w->exposed)) - buffer_sprintf(output, "DIMENSION %s '' absolute 1 1\n", w->name); + fprintf(stdout, "DIMENSION %s '' absolute 1 1\n", w->name); } - buffer_sprintf(output, "CHART %s.sockets '' '%s Open Sockets' 'open sockets' net %s.sockets stacked 20051 %d\n", + fprintf(stdout, "CHART %s.sockets '' '%s Open Sockets' 'open sockets' net %s.sockets stacked 20051 %d\n", type, title, type, update_every); for (w = root; w; w = w->next) { if (unlikely(w->exposed)) - buffer_sprintf(output, "DIMENSION %s '' absolute 1 1\n", w->name); + fprintf(stdout, "DIMENSION %s '' absolute 1 1\n", w->name); } - buffer_sprintf(output, "CHART %s.pipes '' '%s Pipes' 'open pipes' processes %s.pipes stacked 20053 %d\n", type, + fprintf(stdout, "CHART %s.pipes '' '%s Pipes' 'open pipes' processes %s.pipes stacked 20053 %d\n", type, title, type, update_every); for (w = root; w; w = w->next) { if (unlikely(w->exposed)) - buffer_sprintf(output, "DIMENSION %s '' absolute 1 1\n", w->name); + fprintf(stdout, "DIMENSION %s '' absolute 1 1\n", w->name); } } } @@ -2644,20 +2696,25 @@ void send_charts_updates_to_netdata(struct target *root, const char *type, const // ---------------------------------------------------------------------------- // parse command line arguments -void parse_args(int argc, char **argv) +static void parse_args(int argc, char **argv) { int i, freq = 0; char *name = NULL; for(i = 1; i < argc; i++) { if(!freq) { - int n = atoi(argv[i]); + int n = (int)str2l(argv[i]); if(n > 0) { freq = n; continue; } } + if(strcmp("version", argv[i]) == 0 || strcmp("-v", argv[i]) == 0) { + printf("apps.plugin %s\n", VERSION); + exit(0); + } + if(strcmp("debug", argv[i]) == 0) { debug = 1; // debug_flags = 0xffffffff; @@ -2694,35 +2751,52 @@ void parse_args(int argc, char **argv) continue; } + if(strcmp("no-users", argv[i]) == 0 || strcmp("without-users", argv[i]) == 0) { + enable_users_charts = 0; + continue; + } + + if(strcmp("no-groups", argv[i]) == 0 || strcmp("without-groups", argv[i]) == 0) { + enable_groups_charts = 0; + continue; + } + if(strcmp("-h", argv[i]) == 0 || strcmp("--help", argv[i]) == 0) { fprintf(stderr, - "apps.plugin\n" - "(C) 2016 Costa Tsaousis" - "GPL v3+\n" - "This program is a data collector plugin for netdata.\n" "\n" - "Valid command line options:\n" + " netdata apps.plugin %s\n" + " Copyright (C) 2016-2017 Costa Tsaousis <costa@tsaousis.gr>\n" + " Released under GNU Public License v3 or later.\n" + " All rights reserved.\n" + "\n" + " This program is a data collector plugin for netdata.\n" + "\n" + " Valid command line options:\n" "\n" - "SECONDS set the data collection frequency\n" + " SECONDS set the data collection frequency\n" "\n" - "debug enable debugging (lot of output)\n" + " debug enable debugging (lot of output)\n" "\n" - "with-childs\n" - "without-childs enable / disable aggregating exited\n" - " children resources into parents\n" - " (default is enabled)\n" + " with-childs\n" + " without-childs enable / disable aggregating exited\n" + " children resources into parents\n" + " (default is enabled)\n" "\n" - "with-guest\n" - "without-guest enable / disable reporting guest charts\n" - " (default is disabled)\n" + " with-guest\n" + " without-guest enable / disable reporting guest charts\n" + " (default is disabled)\n" "\n" - "with-files\n" - "without-files enable / disable reporting files, sockets, pipes\n" - " (default is enabled)\n" + " with-files\n" + " without-files enable / disable reporting files, sockets, pipes\n" + " (default is enabled)\n" "\n" - "NAME read apps_NAME.conf instead of\n" - " apps_groups.conf\n" - " (default NAME=groups)\n" + " NAME read apps_NAME.conf instead of\n" + " apps_groups.conf\n" + " (default NAME=groups)\n" + "\n" + " version print program version and exit\n" + "\n" + , VERSION ); exit(1); } @@ -2740,7 +2814,7 @@ void parse_args(int argc, char **argv) if(!name) name = "groups"; if(read_apps_groups_conf(name)) { - error("Cannot read process groups %s", name); + error("Cannot read process groups '%s/apps_%s.conf'. There are no internal defaults. Failing.", config_dir, name); exit(1); } } @@ -2786,8 +2860,7 @@ int main(int argc, char **argv) procfile_adaptive_initial_allocation = 1; - time_t started_t = time(NULL); - time_t current_t; + time_t started_t = now_realtime_sec(); get_system_HZ(); get_system_pid_max(); get_system_cpus(); @@ -2797,8 +2870,7 @@ int main(int argc, char **argv) all_pids_sortlist = callocz(sizeof(pid_t), (size_t)pid_max); all_pids = callocz(sizeof(struct pid_stat *), (size_t) pid_max); - output = buffer_create(1024); - buffer_sprintf(output, + fprintf(stdout, "CHART netdata.apps_cpu '' 'Apps Plugin CPU' 'milliseconds/s' apps.plugin netdata.apps_cpu stacked 140000 %1$d\n" "DIMENSION user '' incremental 1 1000\n" "DIMENSION system '' incremental 1 1000\n" @@ -2818,7 +2890,7 @@ int main(int argc, char **argv) ); if(include_exited_childs) - buffer_sprintf(output, + fprintf(stdout, "CHART netdata.apps_children_fix '' 'Apps Plugin Exited Children Normalization Ratios' 'percentage' apps.plugin netdata.apps_children_fix line 140003 %1$d\n" "DIMENSION cutime '' absolute 1 %2$llu\n" "DIMENSION cstime '' absolute 1 %2$llu\n" @@ -2829,22 +2901,23 @@ int main(int argc, char **argv) , RATES_DETAIL ); -#ifndef PROFILING_MODE - unsigned long long sunext = (time(NULL) - (time(NULL) % update_every) + update_every) * 1000000ULL; - unsigned long long sunow; -#endif /* PROFILING_MODE */ - + usec_t step = update_every * USEC_PER_SEC; global_iterations_counter = 1; for(;1; global_iterations_counter++) { -#ifndef PROFILING_MODE - // delay until it is our time to run - while((sunow = time_usec()) < sunext) - sleep_usec(sunext - sunow); - - // find the next time we need to run - while(time_usec() > sunext) - sunext += update_every * 1000000ULL; -#endif /* PROFILING_MODE */ + usec_t now = now_realtime_usec(); + usec_t next = now - (now % step) + step; + +#ifdef NETDATA_PROFILING +#warning "compiling for profiling" + static int profiling_count=0; + profiling_count++; + if(unlikely(profiling_count > 1000)) exit(0); +#else + while(now < next) { + sleep_usec(next - now); + now = now_realtime_usec(); + } +#endif if(!collect_data_for_all_processes_from_proc()) { error("Cannot collect /proc data for running processes. Disabling apps.plugin..."); @@ -2855,36 +2928,35 @@ int main(int argc, char **argv) calculate_netdata_statistics(); normalize_data(apps_groups_root_target); - unsigned long long dt = send_resource_usage_to_netdata(); + usec_t dt = send_resource_usage_to_netdata(); // this is smart enough to show only newly added apps, when needed send_charts_updates_to_netdata(apps_groups_root_target, "apps", "Apps"); - send_charts_updates_to_netdata(users_root_target, "users", "Users"); - send_charts_updates_to_netdata(groups_root_target, "groups", "User Groups"); + + if(likely(enable_users_charts)) + send_charts_updates_to_netdata(users_root_target, "users", "Users"); + + if(likely(enable_groups_charts)) + send_charts_updates_to_netdata(groups_root_target, "groups", "User Groups"); send_collected_data_to_netdata(apps_groups_root_target, "apps", dt); - send_collected_data_to_netdata(users_root_target, "users", dt); - send_collected_data_to_netdata(groups_root_target, "groups", dt); - show_guest_time_old = show_guest_time; + if(likely(enable_users_charts)) + send_collected_data_to_netdata(users_root_target, "users", dt); - //if(puts(buffer_tostring(output)) == EOF) - if(write(STDOUT_FILENO, buffer_tostring(output), buffer_strlen(output)) == -1) - fatal("Cannot send chart values to netdata."); + if(likely(enable_groups_charts)) + send_collected_data_to_netdata(groups_root_target, "groups", dt); - // fflush(stdout); - buffer_flush(output); + fflush(stdout); + + show_guest_time_old = show_guest_time; if(unlikely(debug)) fprintf(stderr, "apps.plugin: done Loop No %llu\n", global_iterations_counter); - current_t = time(NULL); + time_t current_t = now_realtime_sec(); -#ifndef PROFILING_MODE // restart check (14400 seconds) if(current_t - started_t > 14400) exit(0); -#else - if(current_t - started_t > 10) exit(0); -#endif /* PROFILING_MODE */ } } @@ -283,18 +283,30 @@ avl *avl_remove(avl_tree *tree, avl *item) { // --------------------------- // traversing -void avl_walker(avl *node, void (*callback)(void *)) { - if(node->avl_link[0]) - avl_walker(node->avl_link[0], callback); +int avl_walker(avl *node, int (*callback)(void *entry, void *data), void *data) { + int total = 0, ret = 0; - callback(node); + if(node->avl_link[0]) { + ret = avl_walker(node->avl_link[0], callback, data); + if(ret < 0) return ret; + total += ret; + } + + ret = callback(node, data); + if(ret < 0) return ret; + total += ret; - if(node->avl_link[1]) - avl_walker(node->avl_link[1], callback); + if(node->avl_link[1]) { + ret = avl_walker(node->avl_link[1], callback, data); + if (ret < 0) return ret; + total += ret; + } + + return total; } -void avl_traverse(avl_tree *t, void (*callback)(void *)) { - avl_walker(t->root, callback); +int avl_traverse(avl_tree *t, int (*callback)(void *entry, void *data), void *data) { + return avl_walker(t->root, callback, data); } // --------------------------- @@ -372,10 +384,12 @@ avl *avl_insert_lock(avl_tree_lock *t, avl *a) { return ret; } -void avl_traverse_lock(avl_tree_lock *t, void (*callback)(void *)) { +int avl_traverse_lock(avl_tree_lock *t, int (*callback)(void *entry, void *data), void *data) { + int ret; avl_read_lock(t); - avl_traverse(&t->avl_tree, callback); + ret = avl_traverse(&t->avl_tree, callback, data); avl_unlock(t); + return ret; } void avl_init(avl_tree *t, int (*compar)(void *a, void *b)) { @@ -56,16 +56,16 @@ typedef struct avl_tree_lock { * a is linked directly to the tree, so it has to * be properly allocated by the caller. */ -avl *avl_insert_lock(avl_tree_lock *t, avl *a); -avl *avl_insert(avl_tree *t, avl *a); +avl *avl_insert_lock(avl_tree_lock *t, avl *a) NEVERNULL WARNUNUSED; +avl *avl_insert(avl_tree *t, avl *a) NEVERNULL WARNUNUSED; /* Remove an element a from the AVL tree t * returns a pointer to the removed element * or NULL if an element equal to a is not found * (equal as returned by t->compar()) */ -avl *avl_remove_lock(avl_tree_lock *t, avl *a); -avl *avl_remove(avl_tree *t, avl *a); +avl *avl_remove_lock(avl_tree_lock *t, avl *a) WARNUNUSED; +avl *avl_remove(avl_tree *t, avl *a) WARNUNUSED; /* Find the element into the tree that equal to a * (equal as returned by t->compar()) @@ -80,7 +80,7 @@ void avl_init_lock(avl_tree_lock *t, int (*compar)(void *a, void *b)); void avl_init(avl_tree *t, int (*compar)(void *a, void *b)); -void avl_traverse_lock(avl_tree_lock *t, void (*callback)(void *)); -void avl_traverse(avl_tree *t, void (*callback)(void *)); +int avl_traverse_lock(avl_tree_lock *t, int (*callback)(void *entry, void *data), void *data); +int avl_traverse(avl_tree *t, int (*callback)(void *entry, void *data), void *data); #endif /* avl.h */ diff --git a/src/backends.c b/src/backends.c new file mode 100644 index 000000000..1272d0473 --- /dev/null +++ b/src/backends.c @@ -0,0 +1,549 @@ +#include "common.h" + +#define BACKEND_SOURCE_DATA_AS_COLLECTED 0x00000001 +#define BACKEND_SOURCE_DATA_AVERAGE 0x00000002 +#define BACKEND_SOURCE_DATA_SUM 0x00000004 + +static inline calculated_number backend_calculate_value_from_stored_data(RRDSET *st, RRDDIM *rd, time_t after, time_t before, uint32_t options) { + time_t first_t = rrdset_first_entry_t(st); + time_t last_t = rrdset_last_entry_t(st); + + if(unlikely(before - after < st->update_every && after != after - after % st->update_every)) + // when st->update_every is bigger than the frequency we send data to backend + // skip the iterations that are not aligned to the database + return NAN; + + // align the time-frame + // for 'after' also skip the first value by adding st->update_every + after = after - after % st->update_every + st->update_every; + before = before - before % st->update_every; + + if(unlikely(after < first_t)) + after = first_t; + + if(unlikely(after > before)) + // this can happen when the st->update_every > before - after + before = after; + + if(unlikely(before > last_t)) + before = last_t; + + size_t counter = 0; + calculated_number sum = 0; + + long start_at_slot = rrdset_time2slot(st, before), + stop_at_slot = rrdset_time2slot(st, after), + slot, stop_now = 0; + + for(slot = start_at_slot; !stop_now ; slot--) { + if(unlikely(slot < 0)) slot = st->entries - 1; + if(unlikely(slot == stop_at_slot)) stop_now = 1; + + storage_number n = rd->values[slot]; + if(unlikely(!does_storage_number_exist(n))) continue; + + calculated_number value = unpack_storage_number(n); + sum += value; + counter++; + } + + if(unlikely(!counter)) + return NAN; + + if(unlikely(options & BACKEND_SOURCE_DATA_SUM)) + return sum; + + return sum / (calculated_number)counter; +} + +static inline int format_dimension_collected_graphite_plaintext(BUFFER *b, const char *prefix, RRDHOST *host, const char *hostname, RRDSET *st, RRDDIM *rd, time_t after, time_t before, uint32_t options) { + (void)host; + (void)after; + (void)before; + (void)options; + buffer_sprintf(b, "%s.%s.%s.%s " COLLECTED_NUMBER_FORMAT " %u\n", prefix, hostname, st->id, rd->id, rd->last_collected_value, (uint32_t)rd->last_collected_time.tv_sec); + return 1; +} + +static inline int format_dimension_stored_graphite_plaintext(BUFFER *b, const char *prefix, RRDHOST *host, const char *hostname, RRDSET *st, RRDDIM *rd, time_t after, time_t before, uint32_t options) { + (void)host; + calculated_number value = backend_calculate_value_from_stored_data(st, rd, after, before, options); + if(!isnan(value)) { + buffer_sprintf(b, "%s.%s.%s.%s " CALCULATED_NUMBER_FORMAT " %u\n", prefix, hostname, st->id, rd->id, value, (uint32_t) before); + return 1; + } + return 0; +} + +static inline int format_dimension_collected_opentsdb_telnet(BUFFER *b, const char *prefix, RRDHOST *host, const char *hostname, RRDSET *st, RRDDIM *rd, time_t after, time_t before, uint32_t options) { + (void)host; + (void)after; + (void)before; + (void)options; + buffer_sprintf(b, "put %s.%s.%s %u " COLLECTED_NUMBER_FORMAT " host=%s\n", prefix, st->id, rd->id, (uint32_t)rd->last_collected_time.tv_sec, rd->last_collected_value, hostname); + return 1; +} + +static inline int format_dimension_stored_opentsdb_telnet(BUFFER *b, const char *prefix, RRDHOST *host, const char *hostname, RRDSET *st, RRDDIM *rd, time_t after, time_t before, uint32_t options) { + (void)host; + calculated_number value = backend_calculate_value_from_stored_data(st, rd, after, before, options); + if(!isnan(value)) { + buffer_sprintf(b, "put %s.%s.%s %u " CALCULATED_NUMBER_FORMAT " host=%s\n", prefix, st->id, rd->id, (uint32_t) before, value, hostname); + return 1; + } + return 0; +} + +static inline int process_graphite_response(BUFFER *b) { + char sample[1024]; + const char *s = buffer_tostring(b); + char *d = sample, *e = &sample[sizeof(sample) - 1]; + + for(; *s && d < e ;s++) { + char c = *s; + if(unlikely(!isprint(c))) c = ' '; + *d++ = c; + } + *d = '\0'; + + info("Received %zu bytes from graphite backend. Ignoring them. Sample: '%s'", buffer_strlen(b), sample); + buffer_flush(b); + return 0; +} + +static inline int process_opentsdb_response(BUFFER *b) { + char sample[1024]; + const char *s = buffer_tostring(b); + char *d = sample, *e = &sample[sizeof(sample) - 1]; + + for(; *s && d < e ;s++) { + char c = *s; + if(unlikely(!isprint(c))) c = ' '; + *d++ = c; + } + *d = '\0'; + + info("Received %zu bytes from opentsdb backend. Ignoring them. Sample: '%s'", buffer_strlen(b), sample); + buffer_flush(b); + return 0; +} + +void *backends_main(void *ptr) { + struct netdata_static_thread *static_thread = (struct netdata_static_thread *)ptr; + + BUFFER *b = buffer_create(1), *response = buffer_create(1); + int (*backend_request_formatter)(BUFFER *b, const char *prefix, RRDHOST *host, const char *hostname, RRDSET *st, RRDDIM *rd, time_t after, time_t before, uint32_t options) = NULL; + int (*backend_response_checker)(BUFFER *b) = NULL; + + info("BACKEND thread created with task id %d", gettid()); + + if(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL) != 0) + error("Cannot set pthread cancel type to DEFERRED."); + + if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0) + error("Cannot set pthread cancel state to ENABLE."); + + // ------------------------------------------------------------------------ + // collect configuration options + + struct timeval timeout = { + .tv_sec = 0, + .tv_usec = 0 + }; + int default_port = 0; + int sock = -1; + uint32_t options; + int enabled = config_get_boolean("backend", "enabled", 0); + const char *source = config_get("backend", "data source", "average"); + const char *type = config_get("backend", "type", "graphite"); + const char *destination = config_get("backend", "destination", "localhost"); + const char *prefix = config_get("backend", "prefix", "netdata"); + const char *hostname = config_get("backend", "hostname", localhost.hostname); + int frequency = (int)config_get_number("backend", "update every", 10); + int buffer_on_failures = (int)config_get_number("backend", "buffer on failures", 10); + long timeoutms = config_get_number("backend", "timeout ms", frequency * 2 * 1000); + + // ------------------------------------------------------------------------ + // validate configuration options + // and prepare for sending data to our backend + if(!enabled || frequency < 1) + goto cleanup; + + if(!strcmp(source, "as collected")) { + options = BACKEND_SOURCE_DATA_AS_COLLECTED; + } + else if(!strcmp(source, "average")) { + options = BACKEND_SOURCE_DATA_AVERAGE; + } + else if(!strcmp(source, "sum") || !strcmp(source, "volume")) { + options = BACKEND_SOURCE_DATA_SUM; + } + else { + error("Invalid data source method '%s' for backend given. Disabling backed.", source); + goto cleanup; + } + + if(!strcmp(type, "graphite") || !strcmp(type, "graphite:plaintext")) { + default_port = 2003; + if(options == BACKEND_SOURCE_DATA_AS_COLLECTED) + backend_request_formatter = format_dimension_collected_graphite_plaintext; + else + backend_request_formatter = format_dimension_stored_graphite_plaintext; + + backend_response_checker = process_graphite_response; + } + else if(!strcmp(type, "opentsdb") || !strcmp(type, "opentsdb:telnet")) { + default_port = 4242; + if(options == BACKEND_SOURCE_DATA_AS_COLLECTED) + backend_request_formatter = format_dimension_collected_opentsdb_telnet; + else + backend_request_formatter = format_dimension_stored_opentsdb_telnet; + + backend_response_checker = process_opentsdb_response; + } + else { + error("Unknown backend type '%s'", type); + goto cleanup; + } + + if(backend_request_formatter == NULL || backend_response_checker == NULL) { + error("backend is misconfigured - disabling it."); + goto cleanup; + } + + if(timeoutms < 1) { + error("BACKED invalid timeout %ld ms given. Assuming %d ms.", timeoutms, frequency * 2 * 1000); + timeoutms = frequency * 2 * 1000; + } + timeout.tv_sec = (timeoutms * 1000) / 1000000; + timeout.tv_usec = (timeoutms * 1000) % 1000000; + + // ------------------------------------------------------------------------ + // prepare the charts for monitoring the backend + + struct rusage thread; + + collected_number + chart_buffered_metrics = 0, + chart_lost_metrics = 0, + chart_sent_metrics = 0, + chart_buffered_bytes = 0, + chart_received_bytes = 0, + chart_sent_bytes = 0, + chart_receptions = 0, + chart_transmission_successes = 0, + chart_transmission_failures = 0, + chart_data_lost_events = 0, + chart_lost_bytes = 0, + chart_backend_reconnects = 0, + chart_backend_latency = 0; + + RRDSET *chart_metrics = rrdset_find("netdata.backend_metrics"); + if(!chart_metrics) { + chart_metrics = rrdset_create("netdata", "backend_metrics", NULL, "backend", NULL, "Netdata Buffered Metrics", "metrics", 130600, frequency, RRDSET_TYPE_LINE); + rrddim_add(chart_metrics, "buffered", NULL, 1, 1, RRDDIM_ABSOLUTE); + rrddim_add(chart_metrics, "lost", NULL, 1, 1, RRDDIM_ABSOLUTE); + rrddim_add(chart_metrics, "sent", NULL, 1, 1, RRDDIM_ABSOLUTE); + } + + RRDSET *chart_bytes = rrdset_find("netdata.backend_bytes"); + if(!chart_bytes) { + chart_bytes = rrdset_create("netdata", "backend_bytes", NULL, "backend", NULL, "Netdata Backend Data Size", "KB", 130610, frequency, RRDSET_TYPE_AREA); + rrddim_add(chart_bytes, "buffered", NULL, 1, 1024, RRDDIM_ABSOLUTE); + rrddim_add(chart_bytes, "lost", NULL, 1, 1024, RRDDIM_ABSOLUTE); + rrddim_add(chart_bytes, "sent", NULL, 1, 1024, RRDDIM_ABSOLUTE); + rrddim_add(chart_bytes, "received", NULL, 1, 1024, RRDDIM_ABSOLUTE); + } + + RRDSET *chart_ops = rrdset_find("netdata.backend_ops"); + if(!chart_ops) { + chart_ops = rrdset_create("netdata", "backend_ops", NULL, "backend", NULL, "Netdata Backend Operations", "operations", 130630, frequency, RRDSET_TYPE_LINE); + rrddim_add(chart_ops, "write", NULL, 1, 1, RRDDIM_ABSOLUTE); + rrddim_add(chart_ops, "discard", NULL, 1, 1, RRDDIM_ABSOLUTE); + rrddim_add(chart_ops, "reconnect", NULL, 1, 1, RRDDIM_ABSOLUTE); + rrddim_add(chart_ops, "failure", NULL, 1, 1, RRDDIM_ABSOLUTE); + rrddim_add(chart_ops, "read", NULL, 1, 1, RRDDIM_ABSOLUTE); + } + + /* + * this is misleading - we can only measure the time we need to send data + * this time is not related to the time required for the data to travel to + * the backend database and the time that server needed to process them + * + * issue #1432 and https://www.softlab.ntua.gr/facilities/documentation/unix/unix-socket-faq/unix-socket-faq-2.html + * + RRDSET *chart_latency = rrdset_find("netdata.backend_latency"); + if(!chart_latency) { + chart_latency = rrdset_create("netdata", "backend_latency", NULL, "backend", NULL, "Netdata Backend Latency", "ms", 130620, frequency, RRDSET_TYPE_AREA); + rrddim_add(chart_latency, "latency", NULL, 1, 1000, RRDDIM_ABSOLUTE); + } + */ + + RRDSET *chart_rusage = rrdset_find("netdata.backend_thread_cpu"); + if(!chart_rusage) { + chart_rusage = rrdset_create("netdata", "backend_thread_cpu", NULL, "backend", NULL, "NetData Backend Thread CPU usage", "milliseconds/s", 130630, frequency, RRDSET_TYPE_STACKED); + rrddim_add(chart_rusage, "user", NULL, 1, 1000, RRDDIM_INCREMENTAL); + rrddim_add(chart_rusage, "system", NULL, 1, 1000, RRDDIM_INCREMENTAL); + } + + // ------------------------------------------------------------------------ + // prepare the backend main loop + + info("BACKEND configured ('%s' on '%s' sending '%s' data, every %d seconds, as host '%s', with prefix '%s')", type, destination, source, frequency, hostname, prefix); + + usec_t step_ut = frequency * USEC_PER_SEC; + usec_t random_ut = now_realtime_usec() % (step_ut / 2); + time_t before = (time_t)((now_realtime_usec() - step_ut) / USEC_PER_SEC); + time_t after = before; + int failures = 0; + + for(;;) { + // ------------------------------------------------------------------------ + // wait for the next iteration point + + usec_t now_ut = now_realtime_usec(); + usec_t next_ut = now_ut - (now_ut % step_ut) + step_ut; + before = (time_t)(next_ut / USEC_PER_SEC); + + // add a little delay (1/4 of the step) plus some randomness + next_ut += (step_ut / 4) + random_ut; + + while(now_ut < next_ut) { + sleep_usec(next_ut - now_ut); + now_ut = now_realtime_usec(); + } + + // ------------------------------------------------------------------------ + // add to the buffer the data we need to send to the backend + + RRDSET *st; + int pthreadoldcancelstate; + + if(unlikely(pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &pthreadoldcancelstate) != 0)) + error("Cannot set pthread cancel state to DISABLE."); + + rrdhost_rdlock(&localhost); + for(st = localhost.rrdset_root; st ;st = st->next) { + pthread_rwlock_rdlock(&st->rwlock); + + RRDDIM *rd; + for(rd = st->dimensions; rd ;rd = rd->next) { + if(rd->last_collected_time.tv_sec >= after) + chart_buffered_metrics += backend_request_formatter(b, prefix, &localhost, hostname, st, rd, after, before, options); + } + + pthread_rwlock_unlock(&st->rwlock); + } + rrdhost_unlock(&localhost); + + if(unlikely(pthread_setcancelstate(pthreadoldcancelstate, NULL) != 0)) + error("Cannot set pthread cancel state to RESTORE (%d).", pthreadoldcancelstate); + + // ------------------------------------------------------------------------ + + chart_buffered_bytes = (collected_number)buffer_strlen(b); + + // reset the monitoring chart counters + chart_received_bytes = + chart_sent_bytes = + chart_sent_metrics = + chart_lost_metrics = + chart_transmission_successes = + chart_transmission_failures = + chart_data_lost_events = + chart_lost_bytes = + chart_backend_reconnects = + chart_backend_latency = 0; + + if(unlikely(netdata_exit)) break; + + //fprintf(stderr, "\nBACKEND BEGIN:\n%s\nBACKEND END\n", buffer_tostring(b)); // FIXME + //fprintf(stderr, "after = %lu, before = %lu\n", after, before); + + // ------------------------------------------------------------------------ + // if we are connected, receive a response, without blocking + + if(likely(sock != -1)) { + errno = 0; + + // loop through to collect all data + while(sock != -1 && errno != EWOULDBLOCK) { + buffer_need_bytes(response, 4096); + + ssize_t r = recv(sock, &response->buffer[response->len], response->size - response->len, MSG_DONTWAIT); + if(likely(r > 0)) { + // we received some data + response->len += r; + chart_received_bytes += r; + chart_receptions++; + } + else if(r == 0) { + error("Backend '%s' closed the socket", destination); + close(sock); + sock = -1; + } + else { + // failed to receive data + if(errno != EAGAIN && errno != EWOULDBLOCK) { + error("Cannot receive data from backend '%s'.", destination); + } + } + } + + // if we received data, process them + if(buffer_strlen(response)) + backend_response_checker(response); + } + + // ------------------------------------------------------------------------ + // if we are not connected, connect to a backend server + + if(unlikely(sock == -1)) { + usec_t start_ut = now_realtime_usec(); + const char *s = destination; + while(*s) { + const char *e = s; + + // skip separators, moving both s(tart) and e(nd) + while(isspace(*e) || *e == ',') s = ++e; + + // move e(nd) to the first separator + while(*e && !isspace(*e) && *e != ',') e++; + + // is there anything? + if(!*s || s == e) break; + + char buf[e - s + 1]; + strncpyz(buf, s, e - s); + chart_backend_reconnects++; + sock = connect_to(buf, default_port, &timeout); + if(sock != -1) break; + s = e; + } + chart_backend_latency += now_realtime_usec() - start_ut; + } + + if(unlikely(netdata_exit)) break; + + // ------------------------------------------------------------------------ + // if we are connected, send our buffer to the backend server + + if(likely(sock != -1)) { + size_t len = buffer_strlen(b); + usec_t start_ut = now_realtime_usec(); + int flags = 0; +#ifdef MSG_NOSIGNAL + flags += MSG_NOSIGNAL; +#endif + + ssize_t written = send(sock, buffer_tostring(b), len, flags); + chart_backend_latency += now_realtime_usec() - start_ut; + if(written != -1 && (size_t)written == len) { + // we sent the data successfully + chart_transmission_successes++; + chart_sent_bytes += written; + chart_sent_metrics = chart_buffered_metrics; + + // reset the failures count + failures = 0; + + // empty the buffer + buffer_flush(b); + } + else { + // oops! we couldn't send (all or some of the) data + error("Failed to write data to database backend '%s'. Willing to write %zu bytes, wrote %zd bytes. Will re-connect.", destination, len, written); + chart_transmission_failures++; + + if(written != -1) + chart_sent_bytes += written; + + // increment the counter we check for data loss + failures++; + + // close the socket - we will re-open it next time + close(sock); + sock = -1; + } + + // either the buffer is empty + // or is holding the data we couldn't send + // so, make sure the next iteration will continue + // from where we are now + after = before; + } + else { + error("Failed to update database backend '%s'", destination); + chart_transmission_failures++; + + // increment the counter we check for data loss + failures++; + } + + if(failures > buffer_on_failures) { + // too bad! we are going to lose data + chart_lost_bytes += buffer_strlen(b); + error("Reached %d backend failures. Flushing buffers to protect this host - this results in data loss on back-end server '%s'", failures, destination); + buffer_flush(b); + failures = 0; + chart_data_lost_events++; + chart_lost_metrics = chart_buffered_metrics; + } + + if(unlikely(netdata_exit)) break; + + // ------------------------------------------------------------------------ + // update the monitoring charts + + if(chart_ops->counter_done) rrdset_next(chart_ops); + rrddim_set(chart_ops, "read", chart_receptions); + rrddim_set(chart_ops, "write", chart_transmission_successes); + rrddim_set(chart_ops, "discard", chart_data_lost_events); + rrddim_set(chart_ops, "failure", chart_transmission_failures); + rrddim_set(chart_ops, "reconnect", chart_backend_reconnects); + rrdset_done(chart_ops); + + if(chart_metrics->counter_done) rrdset_next(chart_metrics); + rrddim_set(chart_metrics, "buffered", chart_buffered_metrics); + rrddim_set(chart_metrics, "lost", chart_lost_metrics); + rrddim_set(chart_metrics, "sent", chart_sent_metrics); + rrdset_done(chart_metrics); + + if(chart_bytes->counter_done) rrdset_next(chart_bytes); + rrddim_set(chart_bytes, "buffered", chart_buffered_bytes); + rrddim_set(chart_bytes, "lost", chart_lost_bytes); + rrddim_set(chart_bytes, "sent", chart_sent_bytes); + rrddim_set(chart_bytes, "received", chart_received_bytes); + rrdset_done(chart_bytes); + + /* + if(chart_latency->counter_done) rrdset_next(chart_latency); + rrddim_set(chart_latency, "latency", chart_backend_latency); + rrdset_done(chart_latency); + */ + + getrusage(RUSAGE_THREAD, &thread); + if(chart_rusage->counter_done) rrdset_next(chart_rusage); + rrddim_set(chart_rusage, "user", thread.ru_utime.tv_sec * 1000000ULL + thread.ru_utime.tv_usec); + rrddim_set(chart_rusage, "system", thread.ru_stime.tv_sec * 1000000ULL + thread.ru_stime.tv_usec); + rrdset_done(chart_rusage); + + if(likely(buffer_strlen(b) == 0)) + chart_buffered_metrics = 0; + + if(unlikely(netdata_exit)) break; + } + +cleanup: + if(sock != -1) + close(sock); + + buffer_free(b); + buffer_free(response); + + info("BACKEND thread exiting"); + + static_thread->enabled = 0; + pthread_exit(NULL); + return NULL; +} diff --git a/src/backends.h b/src/backends.h new file mode 100644 index 000000000..61122a1d0 --- /dev/null +++ b/src/backends.h @@ -0,0 +1,6 @@ +#ifndef NETDATA_BACKENDS_H +#define NETDATA_BACKENDS_H 1 + +void *backends_main(void *ptr); + +#endif /* NETDATA_BACKENDS_H */ diff --git a/src/clocks.c b/src/clocks.c new file mode 100644 index 000000000..c90a07c2f --- /dev/null +++ b/src/clocks.c @@ -0,0 +1,73 @@ +#include "common.h" + +#ifndef HAVE_CLOCK_GETTIME +inline int clock_gettime(clockid_t clk_id, struct timespec *ts) { + struct timeval tv; + if(unlikely(gettimeofday(&tv, NULL) == -1)) + return -1; + ts->tv_sec = tv.tv_sec; + ts->tv_nsec = tv.tv_usec * NSEC_PER_USEC; + return 0; +} +#endif + +inline time_t now_realtime_sec(void) { + struct timespec ts; + if(unlikely(clock_gettime(CLOCK_REALTIME, &ts) == -1)) + return 0; + return ts.tv_sec; +} + +inline int now_realtime_timeval(struct timeval *tv) { + struct timespec ts; + if(unlikely(clock_gettime(CLOCK_REALTIME, &ts) == -1)) + return -1; + tv->tv_sec = ts.tv_sec; + tv->tv_usec = ts.tv_nsec / NSEC_PER_USEC; + return 0; +} + +inline usec_t now_realtime_usec(void) { + struct timespec ts; + if(unlikely(clock_gettime(CLOCK_REALTIME, &ts) == -1)) + return 0; + return (usec_t)ts.tv_sec * USEC_PER_SEC + ts.tv_nsec / NSEC_PER_USEC; +} + +inline time_t now_monotonic_sec(void) { + struct timespec ts; + if(unlikely(clock_gettime(CLOCK_MONOTONIC, &ts) == -1)) + return 0; + return ts.tv_sec; +} + +inline usec_t now_monotonic_usec(void) { + struct timespec ts; + if(unlikely(clock_gettime(CLOCK_MONOTONIC, &ts) == -1)) + return 0; + return (usec_t)ts.tv_sec * USEC_PER_SEC + ts.tv_nsec / NSEC_PER_USEC; +} + +inline time_t now_boottime_sec(void) { + struct timespec ts; + if(unlikely(clock_gettime(CLOCK_BOOTTIME, &ts) == -1)) + return 0; + return ts.tv_sec; +} + +inline usec_t now_boottime_usec(void) { + struct timespec ts; + if(unlikely(clock_gettime(CLOCK_BOOTTIME, &ts) == -1)) + return 0; + return (usec_t)ts.tv_sec * USEC_PER_SEC + ts.tv_nsec / NSEC_PER_USEC; +} + +inline usec_t timeval_usec(struct timeval *tv) { + return (usec_t)tv->tv_sec * USEC_PER_SEC + tv->tv_usec; +} + +inline usec_t dt_usec(struct timeval *now, struct timeval *old) { + usec_t ts1 = timeval_usec(now); + usec_t ts2 = timeval_usec(old); + return (ts1 > ts2) ? (ts1 - ts2) : (ts2 - ts1); +} diff --git a/src/clocks.h b/src/clocks.h new file mode 100644 index 000000000..c1b8e7017 --- /dev/null +++ b/src/clocks.h @@ -0,0 +1,92 @@ +#ifndef NETDATA_CLOCKS_H +#define NETDATA_CLOCKS_H 1 + +#ifndef HAVE_STRUCT_TIMESPEC +struct timespec { + time_t tv_sec; /* seconds */ + long tv_nsec; /* nanoseconds */ +}; +#endif + +#ifndef HAVE_CLOCKID_T +typedef int clockid_t; +#endif + +#ifndef HAVE_CLOCK_GETTIME +int clock_gettime(clockid_t clk_id, struct timespec *ts); +#endif + +/* Linux value is as good as any other */ +#ifndef CLOCK_REALTIME +#define CLOCK_REALTIME 0 +#endif + +#ifndef CLOCK_MONOTONIC +/* fallback to CLOCK_REALTIME if not available */ +#define CLOCK_MONOTONIC CLOCK_REALTIME +#endif + +#ifndef CLOCK_BOOTTIME +/* fallback to CLOCK_MONOTONIC if not available */ +#define CLOCK_BOOTTIME CLOCK_MONOTONIC +#else +#ifdef HAVE_CLOCK_GETTIME +#define CLOCK_BOOTTIME_IS_AVAILABLE 1 // required for /proc/uptime +#endif +#endif + +typedef unsigned long long usec_t; + +#define NSEC_PER_SEC 1000000000ULL +#define NSEC_PER_MSEC 1000000ULL +#define NSEC_PER_USEC 1000ULL +#define USEC_PER_SEC 1000000ULL + +#ifndef HAVE_CLOCK_GETTIME +/* Fallback function for POSIX.1-2001 clock_gettime() function. + * + * We use a realtime clock from gettimeofday(), this will + * make systems without clock_gettime() support sensitive + * to time jumps or hibernation/suspend side effects. + */ +extern int clock_gettime(clockid_t clk_id, struct timespec *ts); +#endif + +/* Fills struct timeval with time since EPOCH from real-time clock (i.e. wall-clock). + * - Hibernation/suspend time is included + * - adjtime()/NTP adjustments affect this clock + * Return 0 on succes, -1 else with errno set appropriately. + */ +extern int now_realtime_timeval(struct timeval *tv); + +/* Returns time since EPOCH from real-time clock (i.e. wall-clock). + * - Hibernation/suspend time is included + * - adjtime()/NTP adjustments affect this clock + */ +extern time_t now_realtime_sec(void); +extern usec_t now_realtime_usec(void); + +/* Returns time from monotonic clock if available, real-time clock else. + * If monotonic clock is available: + * - hibernation/suspend time is not included + * - adjtime()/NTP adjusments affect this clock + * If monotonic clock is not available, this fallbacks to now_realtime(). + */ +extern time_t now_monotonic_sec(void); +extern usec_t now_monotonic_usec(void); + +/* Returns time from boottime clock if available, + * monotonic clock else if available, real-time clock else. + * If boottime clock is available: + * - hibernation/suspend time is included + * - adjtime()/NTP adjusments affect this clock + * If boottime clock is not available, this fallbacks to now_monotonic(). + * If monotonic clock is not available, this fallbacks to now_realtime(). + */ +extern time_t now_boottime_sec(void); +extern usec_t now_boottime_usec(void); + +extern usec_t timeval_usec(struct timeval *ts); +extern usec_t dt_usec(struct timeval *now, struct timeval *old); + +#endif /* NETDATA_CLOCKS_H */ diff --git a/src/common.c b/src/common.c index e1925ff5e..42f3d8d15 100644 --- a/src/common.c +++ b/src/common.c @@ -1,5 +1,13 @@ #include "common.h" +#ifdef __APPLE__ +#define INHERIT_NONE 0 +#endif /* __APPLE__ */ +#if defined(__FreeBSD__) || defined(__APPLE__) +# define O_NOATIME 0 +# define MADV_DONTFORK INHERIT_NONE +#endif /* __FreeBSD__ || __APPLE__*/ + char *global_host_prefix = ""; int enable_ksm = 1; @@ -192,27 +200,22 @@ void freez(void *ptr) { free(ptr); } -// ---------------------------------------------------------------------------- -// time functions - -inline unsigned long long timeval_usec(struct timeval *tv) { - return tv->tv_sec * 1000000ULL + tv->tv_usec; -} +void json_escape_string(char *dst, const char *src, size_t size) { + const char *t; + char *d = dst, *e = &dst[size - 1]; -// time(NULL) in nanoseconds -inline unsigned long long time_usec(void) { - struct timeval now; - gettimeofday(&now, NULL); - return timeval_usec(&now); -} + for(t = src; *t && d < e ;t++) { + if(unlikely(*t == '\\' || *t == '"')) { + if(unlikely(d + 1 >= e)) break; + *d++ = '\\'; + } + *d++ = *t; + } -inline unsigned long long usec_dt(struct timeval *now, struct timeval *old) { - unsigned long long tv1 = timeval_usec(now); - unsigned long long tv2 = timeval_usec(old); - return (tv1 > tv2) ? (tv1 - tv2) : (tv2 - tv1); + *d = '\0'; } -int sleep_usec(unsigned long long usec) { +int sleep_usec(usec_t usec) { #ifndef NETDATA_WITH_USLEEP // we expect microseconds (1.000.000 per second) @@ -224,7 +227,7 @@ int sleep_usec(unsigned long long usec) { while (nanosleep(&req, &rem) == -1) { if (likely(errno == EINTR)) { - info("nanosleep() interrupted (while sleeping for %llu microseconds).", usec); + debug(D_SYSTEM, "nanosleep() interrupted (while sleeping for %llu microseconds).", usec); req.tv_sec = rem.tv_sec; req.tv_nsec = rem.tv_nsec; } else { @@ -804,7 +807,7 @@ uint32_t simple_hash(const char *name) } */ - +/* // http://isthe.com/chongo/tech/comp/fnv/#FNV-1a uint32_t simple_hash(const char *name) { unsigned char *s = (unsigned char *) name; @@ -839,6 +842,7 @@ uint32_t simple_uhash(const char *name) { } return hval; } +*/ /* // http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx @@ -864,11 +868,9 @@ uint32_t simple_hash(const char *name) { */ void strreverse(char *begin, char *end) { - char aux; - while (end > begin) { // clearer code. - aux = *end; + char aux = *end; *end-- = *begin; *begin++ = aux; } @@ -905,11 +907,10 @@ void *mymmap(const char *filename, size_t size, int flags, int ksm) { #ifdef MADV_MERGEABLE static int log_madvise_2 = 1, log_madvise_3 = 1; #endif - int fd; void *mem = NULL; errno = 0; - fd = open(filename, O_RDWR | O_CREAT | O_NOATIME, 0664); + int fd = open(filename, O_RDWR | O_CREAT | O_NOATIME, 0664); if (fd != -1) { if (lseek(fd, size, SEEK_SET) == (off_t) size) { if (write(fd, "", 1) == 1) { @@ -1028,7 +1029,15 @@ int fd_is_valid(int fd) { } pid_t gettid(void) { +#ifdef __FreeBSD__ + return (pid_t)pthread_getthreadid_np(); +#elif defined(__APPLE__) + uint64_t curthreadid; + pthread_threadid_np(NULL, &curthreadid); + return (pid_t)curthreadid; +#else return (pid_t)syscall(SYS_gettid); +#endif /* __FreeBSD__, __APPLE__*/ } char *fgets_trim_len(char *buf, size_t buf_size, FILE *fp, size_t *len) { @@ -1090,14 +1099,24 @@ int snprintfz(char *dst, size_t n, const char *fmt, ...) { int processors = 1; long get_system_cpus(void) { - procfile *ff = NULL; - processors = 1; + #ifdef __APPLE__ + int32_t tmp_processors; + + if (unlikely(GETSYSCTL("hw.logicalcpu", tmp_processors))) { + error("Assuming system has %d processors.", processors); + } else { + processors = tmp_processors; + } + + return processors; + #else + char filename[FILENAME_MAX + 1]; snprintfz(filename, FILENAME_MAX, "%s/proc/stat", global_host_prefix); - ff = procfile_open(filename, NULL, PROCFILE_FLAG_DEFAULT); + procfile *ff = procfile_open(filename, NULL, PROCFILE_FLAG_DEFAULT); if(!ff) { error("Cannot open file '%s'. Assuming system has %d processors.", filename, processors); return processors; @@ -1121,17 +1140,24 @@ long get_system_cpus(void) { procfile_close(ff); - info("System has %d processors.", processors); + debug(D_SYSTEM, "System has %d processors.", processors); return processors; + + #endif /* __APPLE__ */ } pid_t pid_max = 32768; pid_t get_system_pid_max(void) { - procfile *ff = NULL; + #ifdef __APPLE__ + // As we currently do not know a solution to query pid_max from the os + // we use the number defined in bsd/sys/proc_internal.h in XNU sources + pid_max = 99999; + return pid_max; + #else char filename[FILENAME_MAX + 1]; snprintfz(filename, FILENAME_MAX, "%s/proc/sys/kernel/pid_max", global_host_prefix); - ff = procfile_open(filename, NULL, PROCFILE_FLAG_DEFAULT); + procfile *ff = procfile_open(filename, NULL, PROCFILE_FLAG_DEFAULT); if(!ff) { error("Cannot open file '%s'. Assuming system supports %d pids.", filename, pid_max); return pid_max; @@ -1143,7 +1169,7 @@ pid_t get_system_pid_max(void) { return pid_max; } - pid_max = (pid_t)atoi(procfile_lineword(ff, 0, 0)); + pid_max = (pid_t)str2i(procfile_lineword(ff, 0, 0)); if(!pid_max) { procfile_close(ff); pid_max = 32768; @@ -1152,8 +1178,10 @@ pid_t get_system_pid_max(void) { } procfile_close(ff); - info("System supports %d pids.", pid_max); + debug(D_SYSTEM, "System supports %d pids.", pid_max); return pid_max; + + #endif /* __APPLE__ */ } unsigned int hz; @@ -1161,25 +1189,23 @@ void get_system_HZ(void) { long ticks; if ((ticks = sysconf(_SC_CLK_TCK)) == -1) { - perror("sysconf"); + error("Cannot get system clock ticks"); } hz = (unsigned int) ticks; } -int read_single_number_file(const char *filename, unsigned long long *result) { - char buffer[1024 + 1]; - - int fd = open(filename, O_RDONLY, 0666); - if(unlikely(fd == -1)) return 1; - - ssize_t r = read(fd, buffer, 1024); - if(unlikely(r == -1)) { - close(fd); - return 2; - } - - close(fd); - *result = strtoull(buffer, NULL, 0); - return 0; +/* +// poor man cycle counting +static unsigned long tsc; +void begin_tsc(void) { + unsigned long a, d; + asm volatile ("cpuid\nrdtsc" : "=a" (a), "=d" (d) : "0" (0) : "ebx", "ecx"); + tsc = ((unsigned long)d << 32) | (unsigned long)a; } +unsigned long end_tsc(void) { + unsigned long a, d; + asm volatile ("rdtscp" : "=a" (a), "=d" (d) : : "ecx"); + return (((unsigned long)d << 32) | (unsigned long)a) - tsc; +} +*/ diff --git a/src/common.h b/src/common.h index 9ffa8c8bc..e38e95b48 100644 --- a/src/common.h +++ b/src/common.h @@ -5,6 +5,9 @@ #include <config.h> #endif +// ---------------------------------------------------------------------------- +// system include files for all netdata C programs + /* select the memory allocator, based on autoconf findings */ #if defined(ENABLE_JEMALLOC) @@ -20,26 +23,32 @@ #else /* !defined(ENABLE_JEMALLOC) && !defined(ENABLE_TCMALLOC) */ +#if !(defined(__FreeBSD__) || defined(__APPLE__)) #include <malloc.h> +#endif /* __FreeBSD__ || __APPLE__ */ #endif #include <pthread.h> #include <errno.h> - #include <stdio.h> #include <stdlib.h> #include <stdarg.h> #include <stddef.h> - #include <ctype.h> #include <string.h> #include <strings.h> - #include <arpa/inet.h> -#include <netinet/in.h> #include <netinet/tcp.h> +#ifdef HAVE_NETINET_IN_H +#include <netinet/in.h> +#endif + +#ifdef HAVE_RESOLV_H +#include <resolv.h> +#endif + #include <dirent.h> #include <fcntl.h> #include <getopt.h> @@ -47,12 +56,21 @@ #include <pwd.h> #include <locale.h> +#ifdef HAVE_NETDB_H #include <netdb.h> +#endif + +#include <net/if.h> + #include <poll.h> #include <signal.h> #include <syslog.h> #include <sys/mman.h> + +#if !(defined(__FreeBSD__) || defined(__APPLE__)) #include <sys/prctl.h> +#endif /* __FreeBSD__ || __APPLE__*/ + #include <sys/resource.h> #include <sys/socket.h> #include <sys/stat.h> @@ -65,6 +83,18 @@ #include <unistd.h> #include <uuid/uuid.h> +// #1408 +#ifdef MAJOR_IN_MKDEV +#include <sys/mkdev.h> +#endif +#ifdef MAJOR_IN_SYSMACROS +#include <sys/sysmacros.h> +#endif + +/* +#include <mntent.h> +*/ + #ifdef STORAGE_WITH_MATH #include <math.h> #endif @@ -79,6 +109,9 @@ #include <zlib.h> #endif +// ---------------------------------------------------------------------------- +// netdata common definitions + #if (SIZEOF_VOID_P == 8) #define ENVIRONMENT64 #elif (SIZEOF_VOID_P == 4) @@ -91,7 +124,49 @@ #define GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) #endif // __GNUC__ +#ifdef HAVE_FUNC_ATTRIBUTE_RETURNS_NONNULL +#define NEVERNULL __attribute__((returns_nonnull)) +#else +#define NEVERNULL +#endif + +#ifdef HAVE_FUNC_ATTRIBUTE_MALLOC +#define MALLOCLIKE __attribute__((malloc)) +#else +#define MALLOCLIKE +#endif + +#ifdef HAVE_FUNC_ATTRIBUTE_FORMAT +#define PRINTFLIKE(f, a) __attribute__ ((format(__printf__, f, a))) +#else +#define PRINTFLIKE(f, a) +#endif + +#ifdef HAVE_FUNC_ATTRIBUTE_NORETURN +#define NORETURN __attribute__ ((noreturn)) +#else +#define NORETURN +#endif + +#ifdef HAVE_FUNC_ATTRIBUTE_WARN_UNUSED_RESULT +#define WARNUNUSED __attribute__ ((warn_unused_result)) +#else +#define WARNUNUSED +#endif + +#ifdef abs +#undef abs +#endif +#define abs(x) ((x < 0)? -x : x) + +#define GUID_LEN 36 + +// ---------------------------------------------------------------------------- +// netdata include files + +#include "simple_pattern.h" #include "avl.h" +#include "clocks.h" #include "log.h" #include "global_statistics.h" #include "storage_number.h" @@ -107,47 +182,44 @@ #include "plugin_checks.h" #include "plugin_idlejitter.h" #include "plugin_nfacct.h" + +#if defined(__FreeBSD__) +#include "plugin_freebsd.h" +#elif defined(__APPLE__) +#include "plugin_macos.h" +#else #include "plugin_proc.h" +#include "plugin_proc_diskspace.h" +#endif /* __FreeBSD__, __APPLE__*/ + #include "plugin_tc.h" #include "plugins_d.h" - +#include "socket.h" #include "eval.h" #include "health.h" - #include "rrd.h" #include "rrd2json.h" - #include "web_client.h" #include "web_server.h" - #include "registry.h" #include "daemon.h" #include "main.h" #include "unit_test.h" - -#ifdef abs -#undef abs -#endif -#define abs(x) ((x < 0)? -x : x) - -extern unsigned long long usec_dt(struct timeval *now, struct timeval *old); -extern unsigned long long timeval_usec(struct timeval *tv); - -// #define usec_dt(now, last) (((((now)->tv_sec * 1000000ULL) + (now)->tv_usec) - (((last)->tv_sec * 1000000ULL) + (last)->tv_usec))) +#include "ipc.h" +#include "backends.h" +#include "inlined.h" +#include "adaptive_resortable_list.h" extern void netdata_fix_chart_id(char *s); extern void netdata_fix_chart_name(char *s); -extern uint32_t simple_hash(const char *name); -extern uint32_t simple_uhash(const char *name); - extern void strreverse(char* begin, char* end); extern char *mystrsep(char **ptr, char *s); extern char *trim(char *s); extern char *strncpyz(char *dst, const char *src, size_t n); extern int vsnprintfz(char *dst, size_t n, const char *fmt, va_list args); -extern int snprintfz(char *dst, size_t n, const char *fmt, ...) __attribute__ (( format (printf, 3, 4))); +extern int snprintfz(char *dst, size_t n, const char *fmt, ...) PRINTFLIKE(3, 4); // memory allocation functions that handle failures #ifdef NETDATA_LOG_ALLOCATIONS @@ -163,13 +235,15 @@ extern void *mallocz_int(const char *file, const char *function, const unsigned extern void *reallocz_int(const char *file, const char *function, const unsigned long line, void *ptr, size_t size); extern void freez_int(const char *file, const char *function, const unsigned long line, void *ptr); #else -extern char *strdupz(const char *s); -extern void *callocz(size_t nmemb, size_t size); -extern void *mallocz(size_t size); -extern void *reallocz(void *ptr, size_t size); +extern char *strdupz(const char *s) MALLOCLIKE NEVERNULL; +extern void *callocz(size_t nmemb, size_t size) MALLOCLIKE NEVERNULL; +extern void *mallocz(size_t size) MALLOCLIKE NEVERNULL; +extern void *reallocz(void *ptr, size_t size) MALLOCLIKE NEVERNULL; extern void freez(void *ptr); #endif +extern void json_escape_string(char *dst, const char *src, size_t size); + extern void *mymmap(const char *filename, size_t size, int flags, int ksm); extern int savememory(const char *filename, void *mem, size_t size); @@ -180,8 +254,7 @@ extern int enable_ksm; extern pid_t gettid(void); -extern unsigned long long time_usec(void); -extern int sleep_usec(unsigned long long usec); +extern int sleep_usec(usec_t usec); extern char *fgets_trim_len(char *buf, size_t buf_size, FILE *fp, size_t *len); @@ -203,6 +276,4 @@ extern void get_system_HZ(void); #endif #endif -extern int read_single_number_file(const char *filename, unsigned long long *result); - #endif /* NETDATA_COMMON_H */ diff --git a/src/daemon.c b/src/daemon.c index 1c34405d8..4fd8ca5e5 100644 --- a/src/daemon.c +++ b/src/daemon.c @@ -58,6 +58,21 @@ static void chown_open_file(int fd, uid_t uid, gid_t gid) { } } +void create_needed_dir(const char *dir, uid_t uid, gid_t gid) +{ + // attempt to create the directory + if(mkdir(dir, 0755) == 0) { + // we created it + + // chown it to match the required user + if(chown(dir, uid, gid) == -1) + error("Cannot chown directory '%s' to %u:%u", dir, (unsigned int)uid, (unsigned int)gid); + } + else if(errno != EEXIST) + // log an error only if the directory does not exist + error("Cannot create directory '%s'", dir); +} + int become_user(const char *username, int pid_fd) { struct passwd *pw = getpwnam(username); @@ -69,6 +84,14 @@ int become_user(const char *username, int pid_fd) uid_t uid = pw->pw_uid; gid_t gid = pw->pw_gid; + create_needed_dir(CACHE_DIR, uid, gid); + create_needed_dir(VARLIB_DIR, uid, gid); + + if(pidfile[0]) { + if(chown(pidfile, uid, gid) == -1) + error("Cannot chown '%s' to %u:%u", pidfile, (unsigned int)uid, (unsigned int)gid); + } + int ngroups = (int)sysconf(_SC_NGROUPS_MAX); gid_t *supplementary_groups = NULL; if(ngroups) { @@ -91,16 +114,23 @@ int become_user(const char *username, int pid_fd) error("Cannot set supplementary groups for user '%s'", username); freez(supplementary_groups); - supplementary_groups = NULL; ngroups = 0; } +#ifdef __APPLE__ + if(setregid(gid, gid) != 0) { +#else if(setresgid(gid, gid, gid) != 0) { +#endif /* __APPLE__ */ error("Cannot switch to user's %s group (gid: %u).", username, gid); return -1; } +#ifdef __APPLE__ + if(setreuid(uid, uid) != 0) { +#else if(setresuid(uid, uid, uid) != 0) { +#endif /* __APPLE__ */ error("Cannot switch to user %s (uid: %u).", username, uid); return -1; } @@ -138,7 +168,7 @@ void oom_score_adj(int score) { if(!done) error("Cannot adjust my Out-Of-Memory score to %d.", score); else - info("Adjusted my Out-Of-Memory score to %d.", score); + debug(D_SYSTEM, "Adjusted my Out-Of-Memory score to %d.", score); } int sched_setscheduler_idle(void) { @@ -151,7 +181,7 @@ int sched_setscheduler_idle(void) { if(i != 0) error("Cannot adjust my scheduling priority to IDLE."); else - info("Adjusted my scheduling priority to IDLE."); + debug(D_SYSTEM, "Adjusted my scheduling priority to IDLE."); return i; #else @@ -206,7 +236,7 @@ int become_daemon(int dont_fork, const char *user) } // Set new file permissions - umask(0002); + umask(0007); // adjust my Out-Of-Memory score oom_score_adj(1000); @@ -214,20 +244,22 @@ int become_daemon(int dont_fork, const char *user) // never become a problem if(sched_setscheduler_idle() != 0) { if(nice(19) == -1) error("Cannot lower my CPU priority."); - else info("Set my nice value to 19."); + else debug(D_SYSTEM, "Set my nice value to 19."); } if(user && *user) { if(become_user(user, pidfd) != 0) { error("Cannot become user '%s'. Continuing as we are.", user); } - else info("Successfully became user '%s'.", user); + else debug(D_SYSTEM, "Successfully became user '%s'.", user); + } + else { + create_needed_dir(CACHE_DIR, getuid(), getgid()); + create_needed_dir(VARLIB_DIR, getuid(), getgid()); } - if(pidfd != -1) { + if(pidfd != -1) close(pidfd); - pidfd = -1; - } return(0); } diff --git a/src/dictionary.c b/src/dictionary.c index 91d3b45f1..fb9efeedb 100644 --- a/src/dictionary.c +++ b/src/dictionary.c @@ -59,9 +59,6 @@ static int name_value_compare(void* a, void* b) { else return strcmp(((NAME_VALUE *)a)->name, ((NAME_VALUE *)b)->name); } -#define dictionary_name_value_index_add_nolock(dict, nv) do { NETDATA_DICTIONARY_STATS_INSERTS_PLUS1(dict); avl_insert(&((dict)->values_index), (avl *)(nv)); } while(0) -#define dictionary_name_value_index_del_nolock(dict, nv) do { NETDATA_DICTIONARY_STATS_DELETES_PLUS1(dict); avl_remove(&(dict->values_index), (avl *)(nv)); } while(0) - static inline NAME_VALUE *dictionary_name_value_index_find_nolock(DICTIONARY *dict, const char *name, uint32_t hash) { NAME_VALUE tmp; tmp.hash = (hash)?hash:simple_hash(name); @@ -95,7 +92,10 @@ static NAME_VALUE *dictionary_name_value_create_nolock(DICTIONARY *dict, const c } // index it - dictionary_name_value_index_add_nolock(dict, nv); + NETDATA_DICTIONARY_STATS_INSERTS_PLUS1(dict); + if(unlikely(avl_insert(&((dict)->values_index), (avl *)(nv)) != (avl *)nv)) + error("dictionary: INTERNAL ERROR: duplicate insertion to dictionary."); + NETDATA_DICTIONARY_STATS_ENTRIES_PLUS1(dict); return nv; @@ -104,7 +104,9 @@ static NAME_VALUE *dictionary_name_value_create_nolock(DICTIONARY *dict, const c static void dictionary_name_value_destroy_nolock(DICTIONARY *dict, NAME_VALUE *nv) { debug(D_DICTIONARY, "Destroying name value entry for name '%s'.", nv->name); - dictionary_name_value_index_del_nolock(dict, nv); + NETDATA_DICTIONARY_STATS_DELETES_PLUS1(dict); + if(unlikely(avl_remove(&(dict->values_index), (avl *)(nv)) != (avl *)nv)) + error("dictionary: INTERNAL ERROR: dictionary invalid removal of node."); NETDATA_DICTIONARY_STATS_ENTRIES_MINUS1(dict); diff --git a/src/eval.c b/src/eval.c index 397b43bd7..122959ce4 100644 --- a/src/eval.c +++ b/src/eval.c @@ -59,20 +59,6 @@ static inline void print_parsed_as_constant(BUFFER *out, calculated_number n); // ---------------------------------------------------------------------------- // evaluation of expressions -static inline calculated_number eval_check_number(calculated_number n, int *error) { - if(unlikely(isnan(n))) { - *error = EVAL_ERROR_VALUE_IS_NAN; - return 0; - } - - if(unlikely(isinf(n))) { - *error = EVAL_ERROR_VALUE_IS_INFINITE; - return 0; - } - - return n; -} - static inline calculated_number eval_variable(EVAL_EXPRESSION *exp, EVAL_VARIABLE *v, int *error) { static uint32_t this_hash = 0, now_hash = 0, after_hash = 0, before_hash = 0, status_hash = 0, removed_hash = 0, uninitialized_hash = 0, undefined_hash = 0, clear_hash = 0, warning_hash = 0, critical_hash = 0; calculated_number n; @@ -116,7 +102,7 @@ static inline calculated_number eval_variable(EVAL_EXPRESSION *exp, EVAL_VARIABL } if(unlikely(v->hash == now_hash && !strcmp(v->name, "now"))) { - n = time(NULL); + n = now_realtime_sec(); buffer_strcat(exp->error_msg, "[ $now = "); print_parsed_as_constant(exp->error_msg, n); buffer_strcat(exp->error_msg, " ] "); @@ -187,7 +173,7 @@ static inline calculated_number eval_variable(EVAL_EXPRESSION *exp, EVAL_VARIABL } *error = EVAL_ERROR_UNKNOWN_VARIABLE; - buffer_sprintf(exp->error_msg, "unknown variable '%s'", v->name); + buffer_sprintf(exp->error_msg, "[ undefined variable '%s' ] ", v->name); return 0; } @@ -213,7 +199,6 @@ static inline calculated_number eval_value(EVAL_EXPRESSION *exp, EVAL_VALUE *v, break; } - // return eval_check_number(n, error); return n; } @@ -362,7 +347,6 @@ static inline calculated_number eval_node(EVAL_EXPRESSION *exp, EVAL_NODE *op, i calculated_number n = operators[op->operator].eval(exp, op, error); - // return eval_check_number(n, error); return n; } @@ -1047,9 +1031,7 @@ static inline EVAL_NODE *parse_rest_of_expression(const char **string, int *erro // high level function to parse an expression or a sub-expression static inline EVAL_NODE *parse_full_expression(const char **string, int *error) { - EVAL_NODE *op1 = NULL; - - op1 = parse_one_full_operand(string, error); + EVAL_NODE *op1 = parse_one_full_operand(string, error); if(!op1) { *error = EVAL_ERROR_MISSING_OPERAND; return NULL; @@ -1067,8 +1049,19 @@ int expression_evaluate(EVAL_EXPRESSION *exp) { buffer_reset(exp->error_msg); exp->result = eval_node(exp, (EVAL_NODE *)exp->nodes, &exp->error); - if(exp->error == EVAL_ERROR_OK) - exp->result = eval_check_number(exp->result, &exp->error); + if(unlikely(isnan(exp->result))) { + if(exp->error == EVAL_ERROR_OK) + exp->error = EVAL_ERROR_VALUE_IS_NAN; + } + else if(unlikely(isinf(exp->result))) { + if(exp->error == EVAL_ERROR_OK) + exp->error = EVAL_ERROR_VALUE_IS_INFINITE; + } + else if(unlikely(exp->error == EVAL_ERROR_UNKNOWN_VARIABLE)) { + // although there is an unknown variable + // the expression was evaluated successfully + exp->error = EVAL_ERROR_OK; + } if(exp->error != EVAL_ERROR_OK) { exp->result = NAN; @@ -1086,7 +1079,6 @@ int expression_evaluate(EVAL_EXPRESSION *exp) { EVAL_EXPRESSION *expression_parse(const char *string, const char **failed_at, int *error) { const char *s = string; int err = EVAL_ERROR_OK; - unsigned long pos = 0; EVAL_NODE *op = parse_full_expression(&s, &err); @@ -1102,7 +1094,7 @@ EVAL_EXPRESSION *expression_parse(const char *string, const char **failed_at, in if (error) *error = err; if(!op) { - pos = s - string + 1; + unsigned long pos = s - string + 1; error("failed to parse expression '%s': %s at character %lu (i.e.: '%s').", string, expression_strerror(err), pos, s); return NULL; } diff --git a/src/freebsd_sysctl.c b/src/freebsd_sysctl.c new file mode 100644 index 000000000..9400089db --- /dev/null +++ b/src/freebsd_sysctl.c @@ -0,0 +1,2207 @@ +#include "common.h" + +// NEEDED BY: struct vmtotal, struct vmmeter +#include <sys/vmmeter.h> +// NEEDED BY: struct devstat +#include <sys/devicestat.h> +// NEEDED BY: struct xswdev +#include <vm/vm_param.h> +// NEEDED BY: struct semid_kernel, struct shmid_kernel, struct msqid_kernel +#define _KERNEL +#include <sys/sem.h> +#include <sys/shm.h> +#include <sys/msg.h> +#undef _KERNEL +// NEEDED BY: struct sysctl_netisr_workstream, struct sysctl_netisr_work +#include <net/netisr.h> +// NEEDED BY: struct ifaddrs, getifaddrs() +#include <net/if.h> +#include <ifaddrs.h> +// NEEDED BY do_tcp... +#include <netinet/tcp_var.h> +#include <netinet/tcp_fsm.h> +// NEEDED BY do_udp..., do_ip... +#include <netinet/ip_var.h> +// NEEDED BY do_udp... +#include <netinet/udp.h> +#include <netinet/udp_var.h> +// NEEDED BY do_icmp... +#include <netinet/ip.h> +#include <netinet/ip_icmp.h> +#include <netinet/icmp_var.h> +// NEEDED BY do_ip6... +#include <netinet6/ip6_var.h> +// NEEDED BY do_icmp6... +#include <netinet/icmp6.h> +// NEEDED BY do_space, do_inodes +#include <sys/mount.h> +// NEEDED BY do_uptime +#include <time.h> + +#define KILO_FACTOR 1024 +#define MEGA_FACTOR 1048576 // 1024 * 1024 +#define GIGA_FACTOR 1073741824 // 1024 * 1024 * 1024 + +// NEEDED BY: do_disk_io +#define RRD_TYPE_DISK "disk" + +// FreeBSD calculates load averages once every 5 seconds +#define MIN_LOADAVG_UPDATE_EVERY 5 + +// NEEDED BY: do_bandwidth +#define IFA_DATA(s) (((struct if_data *)ifa->ifa_data)->ifi_ ## s) + +int do_freebsd_sysctl(int update_every, usec_t dt) { + (void)dt; + + static int do_cpu = -1, do_cpu_cores = -1, do_interrupts = -1, do_context = -1, do_forks = -1, do_processes = -1, + do_loadavg = -1, do_all_processes = -1, do_disk_io = -1, do_swap = -1, do_ram = -1, do_swapio = -1, + do_pgfaults = -1, do_committed = -1, do_ipc_semaphores = -1, do_ipc_shared_mem = -1, do_ipc_msg_queues = -1, + do_dev_intr = -1, do_soft_intr = -1, do_netisr = -1, do_netisr_per_core = -1, do_bandwidth = -1, + do_tcp_sockets = -1, do_tcp_packets = -1, do_tcp_errors = -1, do_tcp_handshake = -1, + do_ecn = -1, do_tcpext_syscookies = -1, do_tcpext_ofo = -1, do_tcpext_connaborts = -1, + do_udp_packets = -1, do_udp_errors = -1, do_icmp_packets = -1, do_icmpmsg = -1, + do_ip_packets = -1, do_ip_fragsout = -1, do_ip_fragsin = -1, do_ip_errors = -1, + do_ip6_packets = -1, do_ip6_fragsout = -1, do_ip6_fragsin = -1, do_ip6_errors = -1, + do_icmp6 = -1, do_icmp6_redir = -1, do_icmp6_errors = -1, do_icmp6_echos = -1, do_icmp6_router = -1, + do_icmp6_neighbor = -1, do_icmp6_types = -1, do_space = -1, do_inodes = -1, do_uptime = -1; + + if (unlikely(do_cpu == -1)) { + do_cpu = config_get_boolean("plugin:freebsd:sysctl", "cpu utilization", 1); + do_cpu_cores = config_get_boolean("plugin:freebsd:sysctl", "per cpu core utilization", 1); + do_interrupts = config_get_boolean("plugin:freebsd:sysctl", "cpu interrupts", 1); + do_dev_intr = config_get_boolean("plugin:freebsd:sysctl", "device interrupts", 1); + do_soft_intr = config_get_boolean("plugin:freebsd:sysctl", "software interrupts", 1); + do_context = config_get_boolean("plugin:freebsd:sysctl", "context switches", 1); + do_forks = config_get_boolean("plugin:freebsd:sysctl", "processes started", 1); + do_processes = config_get_boolean("plugin:freebsd:sysctl", "processes running", 1); + do_loadavg = config_get_boolean("plugin:freebsd:sysctl", "enable load average", 1); + do_all_processes = config_get_boolean("plugin:freebsd:sysctl", "enable total processes", 1); + do_disk_io = config_get_boolean("plugin:freebsd:sysctl", "stats for all disks", 1); + do_swap = config_get_boolean("plugin:freebsd:sysctl", "system swap", 1); + do_ram = config_get_boolean("plugin:freebsd:sysctl", "system ram", 1); + do_swapio = config_get_boolean("plugin:freebsd:sysctl", "swap i/o", 1); + do_pgfaults = config_get_boolean("plugin:freebsd:sysctl", "memory page faults", 1); + do_committed = config_get_boolean("plugin:freebsd:sysctl", "committed memory", 1); + do_ipc_semaphores = config_get_boolean("plugin:freebsd:sysctl", "ipc semaphores", 1); + do_ipc_shared_mem = config_get_boolean("plugin:freebsd:sysctl", "ipc shared memory", 1); + do_ipc_msg_queues = config_get_boolean("plugin:freebsd:sysctl", "ipc message queues", 1); + do_netisr = config_get_boolean("plugin:freebsd:sysctl", "netisr", 1); + do_netisr_per_core = config_get_boolean("plugin:freebsd:sysctl", "netisr per core", 1); + do_bandwidth = config_get_boolean("plugin:freebsd:sysctl", "bandwidth", 1); + do_tcp_sockets = config_get_boolean("plugin:freebsd:sysctl", "ipv4 TCP connections", 1); + do_tcp_packets = config_get_boolean("plugin:freebsd:sysctl", "ipv4 TCP packets", 1); + do_tcp_errors = config_get_boolean("plugin:freebsd:sysctl", "ipv4 TCP errors", 1); + do_tcp_handshake = config_get_boolean("plugin:freebsd:sysctl", "ipv4 TCP handshake issues", 1); + do_ecn = config_get_boolean_ondemand("plugin:freebsd:sysctl", "ECN packets", CONFIG_ONDEMAND_ONDEMAND); + do_tcpext_syscookies = config_get_boolean_ondemand("plugin:freebsd:sysctl", "TCP SYN cookies", CONFIG_ONDEMAND_ONDEMAND); + do_tcpext_ofo = config_get_boolean_ondemand("plugin:freebsd:sysctl", "TCP out-of-order queue", CONFIG_ONDEMAND_ONDEMAND); + do_tcpext_connaborts = config_get_boolean_ondemand("plugin:freebsd:sysctl", "TCP connection aborts", CONFIG_ONDEMAND_ONDEMAND); + do_udp_packets = config_get_boolean("plugin:freebsd:sysctl", "ipv4 UDP packets", 1); + do_udp_errors = config_get_boolean("plugin:freebsd:sysctl", "ipv4 UDP errors", 1); + do_icmp_packets = config_get_boolean("plugin:freebsd:sysctl", "ipv4 ICMP packets", 1); + do_icmpmsg = config_get_boolean("plugin:freebsd:sysctl", "ipv4 ICMP messages", 1); + do_ip_packets = config_get_boolean("plugin:freebsd:sysctl", "ipv4 packets", 1); + do_ip_fragsout = config_get_boolean("plugin:freebsd:sysctl", "ipv4 fragments sent", 1); + do_ip_fragsin = config_get_boolean("plugin:freebsd:sysctl", "ipv4 fragments assembly", 1); + do_ip_errors = config_get_boolean("plugin:freebsd:sysctl", "ipv4 errors", 1); + do_ip6_packets = config_get_boolean_ondemand("plugin:freebsd:sysctl", "ipv6 packets", CONFIG_ONDEMAND_ONDEMAND); + do_ip6_fragsout = config_get_boolean_ondemand("plugin:freebsd:sysctl", "ipv6 fragments sent", CONFIG_ONDEMAND_ONDEMAND); + do_ip6_fragsin = config_get_boolean_ondemand("plugin:freebsd:sysctl", "ipv6 fragments assembly", CONFIG_ONDEMAND_ONDEMAND); + do_ip6_errors = config_get_boolean_ondemand("plugin:freebsd:sysctl", "ipv6 errors", CONFIG_ONDEMAND_ONDEMAND); + do_icmp6 = config_get_boolean_ondemand("plugin:freebsd:sysctl", "icmp", CONFIG_ONDEMAND_ONDEMAND); + do_icmp6_redir = config_get_boolean_ondemand("plugin:freebsd:sysctl", "icmp redirects", CONFIG_ONDEMAND_ONDEMAND); + do_icmp6_errors = config_get_boolean_ondemand("plugin:freebsd:sysctl", "icmp errors", CONFIG_ONDEMAND_ONDEMAND); + do_icmp6_echos = config_get_boolean_ondemand("plugin:freebsd:sysctl", "icmp echos", CONFIG_ONDEMAND_ONDEMAND); + do_icmp6_router = config_get_boolean_ondemand("plugin:freebsd:sysctl", "icmp router", CONFIG_ONDEMAND_ONDEMAND); + do_icmp6_neighbor = config_get_boolean_ondemand("plugin:freebsd:sysctl", "icmp neighbor", CONFIG_ONDEMAND_ONDEMAND); + do_icmp6_types = config_get_boolean_ondemand("plugin:freebsd:sysctl", "icmp types", CONFIG_ONDEMAND_ONDEMAND); + do_space = config_get_boolean("plugin:freebsd:sysctl", "space usage for all disks", 1); + do_inodes = config_get_boolean("plugin:freebsd:sysctl", "inodes usage for all disks", 1); + do_uptime = config_get_boolean("plugin:macos:sysctl", "system uptime", 1); + } + + RRDSET *st; + RRDDIM *rd; + + int system_pagesize = getpagesize(); // wouldn't it be better to get value directly from hw.pagesize? + int i, n; + void *p; + int common_error = 0; + size_t size; + char title[4096 + 1]; + + // NEEDED BY: do_loadavg + static usec_t last_loadavg_usec = 0; + struct loadavg sysload; + + // NEEDED BY: do_cpu, do_cpu_cores + long cp_time[CPUSTATES]; + + // NEEDED BY: du_cpu_cores, do_netisr, do_netisr_per_core + int ncpus; + + // NEEDED BY: do_cpu_cores + static long *pcpu_cp_time = NULL; + char cpuid[8]; // no more than 4 digits expected + + // NEEDED BY: do_all_processes, do_processes + struct vmtotal vmtotal_data; + + // NEEDED BY: do_context, do_forks + u_int u_int_data; + + // NEEDED BY: do_interrupts + size_t intrcnt_size; + unsigned long nintr = 0; + static unsigned long *intrcnt = NULL; + static char *intrnames = NULL; + unsigned long long totalintr = 0; + + // NEEDED BY: do_disk_io + #define BINTIME_SCALE 5.42101086242752217003726400434970855712890625e-17 // this is 1000/2^64 + int numdevs; + static void *devstat_data = NULL; + struct devstat *dstat; + char disk[DEVSTAT_NAME_LEN + 10 + 1]; // 10 - maximum number of digits for int + struct cur_dstat { + collected_number duration_read_ms; + collected_number duration_write_ms; + collected_number busy_time_ms; + } cur_dstat; + struct prev_dstat { + collected_number bytes_read; + collected_number bytes_write; + collected_number operations_read; + collected_number operations_write; + collected_number duration_read_ms; + collected_number duration_write_ms; + collected_number busy_time_ms; + } prev_dstat; + + // NEEDED BY: do_swap + size_t mibsize; + int mib[3]; // CTL_MAXNAME = 24 maximum mib components (sysctl.h) + struct xswdev xsw; + struct total_xsw { + collected_number bytes_used; + collected_number bytes_total; + } total_xsw = {0, 0}; + + // NEEDED BY: do_swapio, do_ram + struct vmmeter vmmeter_data; + + // NEEDED BY: do_ram + int vfs_bufspace_count; + + // NEEDED BY: do_ipc_semaphores + struct ipc_sem { + int semmni; + collected_number sets; + collected_number semaphores; + } ipc_sem = {0, 0, 0}; + static struct semid_kernel *ipc_sem_data = NULL; + + // NEEDED BY: do_ipc_shared_mem + struct ipc_shm { + u_long shmmni; + collected_number segs; + collected_number segsize; + } ipc_shm = {0, 0, 0}; + static struct shmid_kernel *ipc_shm_data = NULL; + + // NEEDED BY: do_ipc_msg_queues + struct ipc_msq { + int msgmni; + collected_number queues; + collected_number messages; + collected_number usedsize; + collected_number allocsize; + } ipc_msq = {0, 0, 0, 0, 0}; + static struct msqid_kernel *ipc_msq_data = NULL; + + // NEEDED BY: do_netisr, do_netisr_per_core + size_t netisr_workstream_size; + size_t netisr_work_size; + unsigned long num_netisr_workstreams = 0, num_netisr_works = 0; + static struct sysctl_netisr_workstream *netisr_workstream = NULL; + static struct sysctl_netisr_work *netisr_work = NULL; + static struct netisr_stats { + collected_number dispatched; + collected_number hybrid_dispatched; + collected_number qdrops; + collected_number queued; + } *netisr_stats = NULL; + char netstat_cpuid[21]; // no more than 4 digits expected + + // NEEDED BY: do_bandwidth + struct ifaddrs *ifa, *ifap; + struct iftot { + u_long ift_ibytes; + u_long ift_obytes; + } iftot = {0, 0}; + + // NEEDED BY: do_tcp... + struct tcpstat tcpstat; + uint64_t tcps_states[TCP_NSTATES]; + + // NEEDED BY: do_udp... + struct udpstat udpstat; + + // NEEDED BY: do_icmp... + struct icmpstat icmpstat; + struct icmp_total { + u_long msgs_in; + u_long msgs_out; + } icmp_total = {0, 0}; + + // NEEDED BY: do_ip... + struct ipstat ipstat; + + // NEEDED BY: do_ip6... + struct ip6stat ip6stat; + + // NEEDED BY: do_icmp6... + struct icmp6stat icmp6stat; + struct icmp6_total { + u_long msgs_in; + u_long msgs_out; + } icmp6_total = {0, 0}; + + // NEEDED BY: do_space, do_inodes + struct statfs *mntbuf; + int mntsize; + char mntonname[MNAMELEN + 1]; + + // NEEDED BY: do_uptime + struct timespec boot_time, cur_time; + + // -------------------------------------------------------------------- + + if (last_loadavg_usec <= dt) { + if (likely(do_loadavg)) { + if (unlikely(GETSYSCTL("vm.loadavg", sysload))) { + do_loadavg = 0; + error("DISABLED: system.load"); + } else { + + st = rrdset_find_bytype("system", "load"); + if (unlikely(!st)) { + st = rrdset_create("system", "load", NULL, "load", NULL, "System Load Average", "load", 100, (update_every < MIN_LOADAVG_UPDATE_EVERY) ? MIN_LOADAVG_UPDATE_EVERY : update_every, RRDSET_TYPE_LINE); + rrddim_add(st, "load1", NULL, 1, 1000, RRDDIM_ABSOLUTE); + rrddim_add(st, "load5", NULL, 1, 1000, RRDDIM_ABSOLUTE); + rrddim_add(st, "load15", NULL, 1, 1000, RRDDIM_ABSOLUTE); + } + else rrdset_next(st); + + rrddim_set(st, "load1", (collected_number) ((double)sysload.ldavg[0] / sysload.fscale * 1000)); + rrddim_set(st, "load5", (collected_number) ((double)sysload.ldavg[1] / sysload.fscale * 1000)); + rrddim_set(st, "load15", (collected_number) ((double)sysload.ldavg[2] / sysload.fscale * 1000)); + rrdset_done(st); + } + } + + last_loadavg_usec = st->update_every * USEC_PER_SEC; + } + else last_loadavg_usec -= dt; + + // -------------------------------------------------------------------- + + if (likely(do_all_processes | do_processes | do_committed)) { + if (unlikely(GETSYSCTL("vm.vmtotal", vmtotal_data))) { + do_all_processes = 0; + error("DISABLED: system.active_processes"); + do_processes = 0; + error("DISABLED: system.processes"); + do_committed = 0; + error("DISABLED: mem.committed"); + } else { + if (likely(do_all_processes)) { + + st = rrdset_find_bytype("system", "active_processes"); + if (unlikely(!st)) { + st = rrdset_create("system", "active_processes", NULL, "processes", NULL, "System Active Processes", "processes", 750, update_every, RRDSET_TYPE_LINE); + rrddim_add(st, "active", NULL, 1, 1, RRDDIM_ABSOLUTE); + } + else rrdset_next(st); + + rrddim_set(st, "active", (vmtotal_data.t_rq + vmtotal_data.t_dw + vmtotal_data.t_pw + vmtotal_data.t_sl + vmtotal_data.t_sw)); + rrdset_done(st); + } + + // -------------------------------------------------------------------- + + if (likely(do_processes)) { + + st = rrdset_find_bytype("system", "processes"); + if (unlikely(!st)) { + st = rrdset_create("system", "processes", NULL, "processes", NULL, "System Processes", "processes", 600, update_every, RRDSET_TYPE_LINE); + + rrddim_add(st, "running", NULL, 1, 1, RRDDIM_ABSOLUTE); + rrddim_add(st, "blocked", NULL, -1, 1, RRDDIM_ABSOLUTE); + } + else rrdset_next(st); + + rrddim_set(st, "running", vmtotal_data.t_rq); + rrddim_set(st, "blocked", (vmtotal_data.t_dw + vmtotal_data.t_pw)); + rrdset_done(st); + } + + // -------------------------------------------------------------------- + + if (likely(do_committed)) { + st = rrdset_find("mem.committed"); + if (unlikely(!st)) { + st = rrdset_create("mem", "committed", NULL, "system", NULL, "Committed (Allocated) Memory", "MB", 5000, update_every, RRDSET_TYPE_AREA); + st->isdetail = 1; + + rrddim_add(st, "Committed_AS", NULL, system_pagesize, MEGA_FACTOR, RRDDIM_ABSOLUTE); + } + else rrdset_next(st); + + rrddim_set(st, "Committed_AS", vmtotal_data.t_rm); + rrdset_done(st); + } + } + } + + // -------------------------------------------------------------------- + + if (likely(do_cpu)) { + if (unlikely(CPUSTATES != 5)) { + error("FREEBSD: There are %d CPU states (5 was expected)", CPUSTATES); + do_cpu = 0; + error("DISABLED: system.cpu"); + } else { + if (unlikely(GETSYSCTL("kern.cp_time", cp_time))) { + do_cpu = 0; + error("DISABLED: system.cpu"); + } else { + + st = rrdset_find_bytype("system", "cpu"); + if (unlikely(!st)) { + st = rrdset_create("system", "cpu", NULL, "cpu", "system.cpu", "Total CPU utilization", "percentage", 100, update_every, RRDSET_TYPE_STACKED); + + rrddim_add(st, "user", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL); + rrddim_add(st, "nice", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL); + rrddim_add(st, "system", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL); + rrddim_add(st, "interrupt", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL); + rrddim_add(st, "idle", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL); + rrddim_hide(st, "idle"); + } + else rrdset_next(st); + + rrddim_set(st, "user", cp_time[0]); + rrddim_set(st, "nice", cp_time[1]); + rrddim_set(st, "system", cp_time[2]); + rrddim_set(st, "interrupt", cp_time[3]); + rrddim_set(st, "idle", cp_time[4]); + rrdset_done(st); + } + } + } + + // -------------------------------------------------------------------- + + if (likely(do_cpu_cores)) { + if (unlikely(CPUSTATES != 5)) { + error("FREEBSD: There are %d CPU states (5 was expected)", CPUSTATES); + do_cpu_cores = 0; + error("DISABLED: cpu.cpuXX"); + } else { + if (unlikely(GETSYSCTL("kern.smp.cpus", ncpus))) { + do_cpu_cores = 0; + error("DISABLED: cpu.cpuXX"); + } else { + pcpu_cp_time = reallocz(pcpu_cp_time, sizeof(cp_time) * ncpus); + + for (i = 0; i < ncpus; i++) { + if (unlikely(getsysctl("kern.cp_times", pcpu_cp_time, sizeof(cp_time) * ncpus))) { + do_cpu_cores = 0; + error("DISABLED: cpu.cpuXX"); + break; + } + if (unlikely(ncpus > 9999)) { + error("FREEBSD: There are more than 4 digits in cpu cores number"); + do_cpu_cores = 0; + error("DISABLED: cpu.cpuXX"); + break; + } + snprintfz(cpuid, 8, "cpu%d", i); + + st = rrdset_find_bytype("cpu", cpuid); + if (unlikely(!st)) { + st = rrdset_create("cpu", cpuid, NULL, "utilization", "cpu.cpu", "Core utilization", "percentage", 1000, update_every, RRDSET_TYPE_STACKED); + + rrddim_add(st, "user", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL); + rrddim_add(st, "nice", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL); + rrddim_add(st, "system", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL); + rrddim_add(st, "interrupt", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL); + rrddim_add(st, "idle", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL); + rrddim_hide(st, "idle"); + } + else rrdset_next(st); + + rrddim_set(st, "user", pcpu_cp_time[i * 5 + 0]); + rrddim_set(st, "nice", pcpu_cp_time[i * 5 + 1]); + rrddim_set(st, "system", pcpu_cp_time[i * 5 + 2]); + rrddim_set(st, "interrupt", pcpu_cp_time[i * 5 + 3]); + rrddim_set(st, "idle", pcpu_cp_time[i * 5 + 4]); + rrdset_done(st); + } + } + } + } + + // -------------------------------------------------------------------- + + if (likely(do_interrupts)) { + if (unlikely(sysctlbyname("hw.intrcnt", NULL, &intrcnt_size, NULL, 0) == -1)) { + error("FREEBSD: sysctl(hw.intrcnt...) failed: %s", strerror(errno)); + do_interrupts = 0; + error("DISABLED: system.intr"); + } else { + nintr = intrcnt_size / sizeof(u_long); + intrcnt = reallocz(intrcnt, nintr * sizeof(u_long)); + if (unlikely(getsysctl("hw.intrcnt", intrcnt, nintr * sizeof(u_long)))){ + do_interrupts = 0; + error("DISABLED: system.intr"); + } else { + for (i = 0; i < nintr; i++) + totalintr += intrcnt[i]; + + st = rrdset_find_bytype("system", "intr"); + if (unlikely(!st)) { + st = rrdset_create("system", "intr", NULL, "interrupts", NULL, "Total Hardware Interrupts", "interrupts/s", 900, update_every, RRDSET_TYPE_LINE); + st->isdetail = 1; + + rrddim_add(st, "interrupts", NULL, 1, 1, RRDDIM_INCREMENTAL); + } + else rrdset_next(st); + + rrddim_set(st, "interrupts", totalintr); + rrdset_done(st); + + // -------------------------------------------------------------------- + + size = nintr * (MAXCOMLEN +1); + intrnames = reallocz(intrnames, size); + if (unlikely(getsysctl("hw.intrnames", intrnames, size))) { + do_interrupts = 0; + error("DISABLED: system.intr"); + } else { + st = rrdset_find_bytype("system", "interrupts"); + if (unlikely(!st)) + st = rrdset_create("system", "interrupts", NULL, "interrupts", NULL, "System interrupts", "interrupts/s", + 1000, update_every, RRDSET_TYPE_STACKED); + else + rrdset_next(st); + + for (i = 0; i < nintr; i++) { + p = intrnames + i * (MAXCOMLEN + 1); + if (unlikely((intrcnt[i] != 0) && (*(char*)p != 0))) { + rd = rrddim_find(st, p); + if (unlikely(!rd)) + rd = rrddim_add(st, p, NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_set_by_pointer(st, rd, intrcnt[i]); + } + } + rrdset_done(st); + } + } + } + } + + // -------------------------------------------------------------------- + + if (likely(do_dev_intr)) { + if (unlikely(GETSYSCTL("vm.stats.sys.v_intr", u_int_data))) { + do_dev_intr = 0; + error("DISABLED: system.dev_intr"); + } else { + + st = rrdset_find_bytype("system", "dev_intr"); + if (unlikely(!st)) { + st = rrdset_create("system", "dev_intr", NULL, "interrupts", NULL, "Device Interrupts", "interrupts/s", 1000, update_every, RRDSET_TYPE_LINE); + + rrddim_add(st, "interrupts", NULL, 1, 1, RRDDIM_INCREMENTAL); + } + else rrdset_next(st); + + rrddim_set(st, "interrupts", u_int_data); + rrdset_done(st); + } + } + + // -------------------------------------------------------------------- + + if (likely(do_soft_intr)) { + if (unlikely(GETSYSCTL("vm.stats.sys.v_soft", u_int_data))) { + do_soft_intr = 0; + error("DISABLED: system.dev_intr"); + } else { + + st = rrdset_find_bytype("system", "soft_intr"); + if (unlikely(!st)) { + st = rrdset_create("system", "soft_intr", NULL, "interrupts", NULL, "Software Interrupts", "interrupts/s", 1100, update_every, RRDSET_TYPE_LINE); + + rrddim_add(st, "interrupts", NULL, 1, 1, RRDDIM_INCREMENTAL); + } + else rrdset_next(st); + + rrddim_set(st, "interrupts", u_int_data); + rrdset_done(st); + } + } + + // -------------------------------------------------------------------- + + if (likely(do_context)) { + if (unlikely(GETSYSCTL("vm.stats.sys.v_swtch", u_int_data))) { + do_context = 0; + error("DISABLED: system.ctxt"); + } else { + + st = rrdset_find_bytype("system", "ctxt"); + if (unlikely(!st)) { + st = rrdset_create("system", "ctxt", NULL, "processes", NULL, "CPU Context Switches", "context switches/s", 800, update_every, RRDSET_TYPE_LINE); + + rrddim_add(st, "switches", NULL, 1, 1, RRDDIM_INCREMENTAL); + } + else rrdset_next(st); + + rrddim_set(st, "switches", u_int_data); + rrdset_done(st); + } + } + + // -------------------------------------------------------------------- + + if (likely(do_forks)) { + if (unlikely(GETSYSCTL("vm.stats.vm.v_forks", u_int_data))) { + do_forks = 0; + error("DISABLED: system.forks"); + } else { + + st = rrdset_find_bytype("system", "forks"); + if (unlikely(!st)) { + st = rrdset_create("system", "forks", NULL, "processes", NULL, "Started Processes", "processes/s", 700, update_every, RRDSET_TYPE_LINE); + st->isdetail = 1; + + rrddim_add(st, "started", NULL, 1, 1, RRDDIM_INCREMENTAL); + } + else rrdset_next(st); + + rrddim_set(st, "started", u_int_data); + rrdset_done(st); + } + } + + // -------------------------------------------------------------------- + + if (likely(do_disk_io)) { + if (unlikely(GETSYSCTL("kern.devstat.numdevs", numdevs))) { + do_disk_io = 0; + error("DISABLED: disk.io"); + } else { + devstat_data = reallocz(devstat_data, sizeof(long) + sizeof(struct devstat) * numdevs); // there is generation number before devstat structures + if (unlikely(getsysctl("kern.devstat.all", devstat_data, sizeof(long) + sizeof(struct devstat) * numdevs))) { + do_disk_io = 0; + error("DISABLED: disk.io"); + } else { + dstat = devstat_data + sizeof(long); // skip generation number + collected_number total_disk_kbytes_read = 0; + collected_number total_disk_kbytes_write = 0; + + for (i = 0; i < numdevs; i++) { + if (((dstat[i].device_type & DEVSTAT_TYPE_MASK) == DEVSTAT_TYPE_DIRECT) || ((dstat[i].device_type & DEVSTAT_TYPE_MASK) == DEVSTAT_TYPE_STORARRAY)) { + sprintf(disk, "%s%d", dstat[i].device_name, dstat[i].unit_number); + + // -------------------------------------------------------------------- + + st = rrdset_find_bytype(RRD_TYPE_DISK, disk); + if (unlikely(!st)) { + st = rrdset_create(RRD_TYPE_DISK, disk, NULL, disk, "disk.io", "Disk I/O Bandwidth", "kilobytes/s", 2000, update_every, RRDSET_TYPE_AREA); + + rrddim_add(st, "reads", NULL, 1, 1024, RRDDIM_INCREMENTAL); + rrddim_add(st, "writes", NULL, -1, 1024, RRDDIM_INCREMENTAL); + } + else rrdset_next(st); + + total_disk_kbytes_read += dstat[i].bytes[DEVSTAT_READ]/KILO_FACTOR; + total_disk_kbytes_write += dstat[i].bytes[DEVSTAT_WRITE]/KILO_FACTOR; + prev_dstat.bytes_read = rrddim_set(st, "reads", dstat[i].bytes[DEVSTAT_READ]); + prev_dstat.bytes_write = rrddim_set(st, "writes", dstat[i].bytes[DEVSTAT_WRITE]); + rrdset_done(st); + + // -------------------------------------------------------------------- + + st = rrdset_find_bytype("disk_ops", disk); + if (unlikely(!st)) { + st = rrdset_create("disk_ops", disk, NULL, disk, "disk.ops", "Disk Completed I/O Operations", "operations/s", 2001, update_every, RRDSET_TYPE_LINE); + st->isdetail = 1; + + rrddim_add(st, "reads", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "writes", NULL, -1, 1, RRDDIM_INCREMENTAL); + } + else rrdset_next(st); + + prev_dstat.operations_read = rrddim_set(st, "reads", dstat[i].operations[DEVSTAT_READ]); + prev_dstat.operations_write = rrddim_set(st, "writes", dstat[i].operations[DEVSTAT_WRITE]); + rrdset_done(st); + + // -------------------------------------------------------------------- + + st = rrdset_find_bytype("disk_qops", disk); + if (unlikely(!st)) { + st = rrdset_create("disk_qops", disk, NULL, disk, "disk.qops", "Disk Current I/O Operations", "operations", 2002, update_every, RRDSET_TYPE_LINE); + st->isdetail = 1; + + rrddim_add(st, "operations", NULL, 1, 1, RRDDIM_ABSOLUTE); + } + else rrdset_next(st); + + rrddim_set(st, "operations", dstat[i].start_count - dstat[i].end_count); + rrdset_done(st); + + // -------------------------------------------------------------------- + + st = rrdset_find_bytype("disk_util", disk); + if (unlikely(!st)) { + st = rrdset_create("disk_util", disk, NULL, disk, "disk.util", "Disk Utilization Time", "% of time working", 2004, update_every, RRDSET_TYPE_AREA); + st->isdetail = 1; + + rrddim_add(st, "utilization", NULL, 1, 10, RRDDIM_INCREMENTAL); + } + else rrdset_next(st); + + cur_dstat.busy_time_ms = dstat[i].busy_time.sec * 1000 + dstat[i].busy_time.frac * BINTIME_SCALE; + prev_dstat.busy_time_ms = rrddim_set(st, "utilization", cur_dstat.busy_time_ms); + rrdset_done(st); + + // -------------------------------------------------------------------- + + st = rrdset_find_bytype("disk_iotime", disk); + if (unlikely(!st)) { + st = rrdset_create("disk_iotime", disk, NULL, disk, "disk.iotime", "Disk Total I/O Time", "milliseconds/s", 2022, update_every, RRDSET_TYPE_LINE); + st->isdetail = 1; + + rrddim_add(st, "reads", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "writes", NULL, -1, 1, RRDDIM_INCREMENTAL); + } + else rrdset_next(st); + + cur_dstat.duration_read_ms = dstat[i].duration[DEVSTAT_READ].sec * 1000 + dstat[i].duration[DEVSTAT_READ].frac * BINTIME_SCALE; + cur_dstat.duration_write_ms = dstat[i].duration[DEVSTAT_WRITE].sec * 1000 + dstat[i].duration[DEVSTAT_READ].frac * BINTIME_SCALE; + prev_dstat.duration_read_ms = rrddim_set(st, "reads", cur_dstat.duration_read_ms); + prev_dstat.duration_write_ms = rrddim_set(st, "writes", cur_dstat.duration_write_ms); + rrdset_done(st); + + // -------------------------------------------------------------------- + // calculate differential charts + // only if this is not the first time we run + + if (likely(dt)) { + + // -------------------------------------------------------------------- + + st = rrdset_find_bytype("disk_await", disk); + if (unlikely(!st)) { + st = rrdset_create("disk_await", disk, NULL, disk, "disk.await", "Average Completed I/O Operation Time", "ms per operation", 2005, update_every, RRDSET_TYPE_LINE); + st->isdetail = 1; + + rrddim_add(st, "reads", NULL, 1, 1, RRDDIM_ABSOLUTE); + rrddim_add(st, "writes", NULL, -1, 1, RRDDIM_ABSOLUTE); + } + else rrdset_next(st); + + rrddim_set(st, "reads", (dstat[i].operations[DEVSTAT_READ] - prev_dstat.operations_read) ? + (cur_dstat.duration_read_ms - prev_dstat.duration_read_ms) / (dstat[i].operations[DEVSTAT_READ] - prev_dstat.operations_read) : 0); + rrddim_set(st, "writes", (dstat[i].operations[DEVSTAT_WRITE] - prev_dstat.operations_write) ? + (cur_dstat.duration_write_ms - prev_dstat.duration_write_ms) / (dstat[i].operations[DEVSTAT_WRITE] - prev_dstat.operations_write) : 0); + rrdset_done(st); + + // -------------------------------------------------------------------- + + st = rrdset_find_bytype("disk_avgsz", disk); + if (unlikely(!st)) { + st = rrdset_create("disk_avgsz", disk, NULL, disk, "disk.avgsz", "Average Completed I/O Operation Bandwidth", "kilobytes per operation", 2006, update_every, RRDSET_TYPE_AREA); + st->isdetail = 1; + + rrddim_add(st, "reads", NULL, 1, 1024, RRDDIM_ABSOLUTE); + rrddim_add(st, "writes", NULL, -1, 1024, RRDDIM_ABSOLUTE); + } + else rrdset_next(st); + + rrddim_set(st, "reads", (dstat[i].operations[DEVSTAT_READ] - prev_dstat.operations_read) ? + (dstat[i].bytes[DEVSTAT_READ] - prev_dstat.bytes_read) / (dstat[i].operations[DEVSTAT_READ] - prev_dstat.operations_read) : 0); + rrddim_set(st, "writes", (dstat[i].operations[DEVSTAT_WRITE] - prev_dstat.operations_write) ? + (dstat[i].bytes[DEVSTAT_WRITE] - prev_dstat.bytes_write) / (dstat[i].operations[DEVSTAT_WRITE] - prev_dstat.operations_write) : 0); + rrdset_done(st); + + // -------------------------------------------------------------------- + + st = rrdset_find_bytype("disk_svctm", disk); + if (unlikely(!st)) { + st = rrdset_create("disk_svctm", disk, NULL, disk, "disk.svctm", "Average Service Time", "ms per operation", 2007, update_every, RRDSET_TYPE_LINE); + st->isdetail = 1; + + rrddim_add(st, "svctm", NULL, 1, 1, RRDDIM_ABSOLUTE); + } + else rrdset_next(st); + + rrddim_set(st, "svctm", ((dstat[i].operations[DEVSTAT_READ] - prev_dstat.operations_read) + (dstat[i].operations[DEVSTAT_WRITE] - prev_dstat.operations_write)) ? + (cur_dstat.busy_time_ms - prev_dstat.busy_time_ms) / ((dstat[i].operations[DEVSTAT_READ] - prev_dstat.operations_read) + (dstat[i].operations[DEVSTAT_WRITE] - prev_dstat.operations_write)) : 0); + rrdset_done(st); + } + } + } + + // -------------------------------------------------------------------- + + st = rrdset_find_bytype("system", "io"); + if (unlikely(!st)) { + st = rrdset_create("system", "io", NULL, "disk", NULL, "Disk I/O", "kilobytes/s", 150, update_every, RRDSET_TYPE_AREA); + rrddim_add(st, "in", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "out", NULL, -1, 1, RRDDIM_INCREMENTAL); + } + else rrdset_next(st); + + rrddim_set(st, "in", total_disk_kbytes_read); + rrddim_set(st, "out", total_disk_kbytes_write); + rrdset_done(st); + } + } + } + + // -------------------------------------------------------------------- + + + if (likely(do_swap)) { + mibsize = sizeof mib / sizeof mib[0]; + if (unlikely(sysctlnametomib("vm.swap_info", mib, &mibsize) == -1)) { + error("FREEBSD: sysctl(%s...) failed: %s", "vm.swap_info", strerror(errno)); + do_swap = 0; + error("DISABLED: system.swap"); + } else { + for (i = 0; ; i++) { + mib[mibsize] = i; + size = sizeof(xsw); + if (unlikely(sysctl(mib, mibsize + 1, &xsw, &size, NULL, 0) == -1 )) { + if (unlikely(errno != ENOENT)) { + error("FREEBSD: sysctl(%s...) failed: %s", "vm.swap_info", strerror(errno)); + do_swap = 0; + error("DISABLED: system.swap"); + } else { + if (unlikely(size != sizeof(xsw))) { + error("FREEBSD: sysctl(%s...) expected %lu, got %lu", "vm.swap_info", (unsigned long)sizeof(xsw), (unsigned long)size); + do_swap = 0; + error("DISABLED: system.swap"); + } else break; + } + } + total_xsw.bytes_used += xsw.xsw_used; + total_xsw.bytes_total += xsw.xsw_nblks; + } + + if (likely(do_swap)) { + st = rrdset_find("system.swap"); + if (unlikely(!st)) { + st = rrdset_create("system", "swap", NULL, "swap", NULL, "System Swap", "MB", 201, update_every, RRDSET_TYPE_STACKED); + st->isdetail = 1; + + rrddim_add(st, "free", NULL, system_pagesize, MEGA_FACTOR, RRDDIM_ABSOLUTE); + rrddim_add(st, "used", NULL, system_pagesize, MEGA_FACTOR, RRDDIM_ABSOLUTE); + } + else rrdset_next(st); + + rrddim_set(st, "used", total_xsw.bytes_used); + rrddim_set(st, "free", total_xsw.bytes_total - total_xsw.bytes_used); + rrdset_done(st); + } + } + } + + // -------------------------------------------------------------------- + + if (likely(do_ram)) { + if (unlikely(GETSYSCTL("vm.stats.vm.v_active_count", vmmeter_data.v_active_count) || + GETSYSCTL("vm.stats.vm.v_inactive_count", vmmeter_data.v_inactive_count) || + GETSYSCTL("vm.stats.vm.v_wire_count", vmmeter_data.v_wire_count) || + GETSYSCTL("vm.stats.vm.v_cache_count", vmmeter_data.v_cache_count) || + GETSYSCTL("vfs.bufspace", vfs_bufspace_count) || + GETSYSCTL("vm.stats.vm.v_free_count", vmmeter_data.v_free_count))) { + do_ram = 0; + error("DISABLED: system.ram"); + } else { + st = rrdset_find("system.ram"); + if (unlikely(!st)) { + st = rrdset_create("system", "ram", NULL, "ram", NULL, "System RAM", "MB", 200, update_every, RRDSET_TYPE_STACKED); + + rrddim_add(st, "active", NULL, system_pagesize, MEGA_FACTOR, RRDDIM_ABSOLUTE); + rrddim_add(st, "inactive", NULL, system_pagesize, MEGA_FACTOR, RRDDIM_ABSOLUTE); + rrddim_add(st, "wired", NULL, system_pagesize, MEGA_FACTOR, RRDDIM_ABSOLUTE); + rrddim_add(st, "cache", NULL, system_pagesize, MEGA_FACTOR, RRDDIM_ABSOLUTE); + rrddim_add(st, "buffers", NULL, 1, MEGA_FACTOR, RRDDIM_ABSOLUTE); + rrddim_add(st, "free", NULL, system_pagesize, MEGA_FACTOR, RRDDIM_ABSOLUTE); + } + else rrdset_next(st); + + rrddim_set(st, "active", vmmeter_data.v_active_count); + rrddim_set(st, "inactive", vmmeter_data.v_inactive_count); + rrddim_set(st, "wired", vmmeter_data.v_wire_count); + rrddim_set(st, "cache", vmmeter_data.v_cache_count); + rrddim_set(st, "buffers", vfs_bufspace_count); + rrddim_set(st, "free", vmmeter_data.v_free_count); + rrdset_done(st); + } + } + + // -------------------------------------------------------------------- + + if (likely(do_swapio)) { + if (unlikely(GETSYSCTL("vm.stats.vm.v_swappgsin", vmmeter_data.v_swappgsin) || GETSYSCTL("vm.stats.vm.v_swappgsout", vmmeter_data.v_swappgsout))) { + do_swapio = 0; + error("DISABLED: system.swapio"); + } else { + st = rrdset_find("system.swapio"); + if (unlikely(!st)) { + st = rrdset_create("system", "swapio", NULL, "swap", NULL, "Swap I/O", "kilobytes/s", 250, update_every, RRDSET_TYPE_AREA); + + rrddim_add(st, "in", NULL, system_pagesize, 1024, RRDDIM_INCREMENTAL); + rrddim_add(st, "out", NULL, -system_pagesize, 1024, RRDDIM_INCREMENTAL); + } + else rrdset_next(st); + + rrddim_set(st, "in", vmmeter_data.v_swappgsin); + rrddim_set(st, "out", vmmeter_data.v_swappgsout); + rrdset_done(st); + } + } + + // -------------------------------------------------------------------- + + if (likely(do_pgfaults)) { + if (unlikely(GETSYSCTL("vm.stats.vm.v_vm_faults", vmmeter_data.v_vm_faults) || + GETSYSCTL("vm.stats.vm.v_io_faults", vmmeter_data.v_io_faults) || + GETSYSCTL("vm.stats.vm.v_cow_faults", vmmeter_data.v_cow_faults) || + GETSYSCTL("vm.stats.vm.v_cow_optim", vmmeter_data.v_cow_optim) || + GETSYSCTL("vm.stats.vm.v_intrans", vmmeter_data.v_intrans))) { + do_pgfaults = 0; + error("DISABLED: mem.pgfaults"); + } else { + st = rrdset_find("mem.pgfaults"); + if (unlikely(!st)) { + st = rrdset_create("mem", "pgfaults", NULL, "system", NULL, "Memory Page Faults", "page faults/s", 500, update_every, RRDSET_TYPE_LINE); + st->isdetail = 1; + + rrddim_add(st, "memory", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "io_requiring", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "cow", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "cow_optimized", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "in_transit", NULL, 1, 1, RRDDIM_INCREMENTAL); + } + else rrdset_next(st); + + rrddim_set(st, "memory", vmmeter_data.v_vm_faults); + rrddim_set(st, "io_requiring", vmmeter_data.v_io_faults); + rrddim_set(st, "cow", vmmeter_data.v_cow_faults); + rrddim_set(st, "cow_optimized", vmmeter_data.v_cow_optim); + rrddim_set(st, "in_transit", vmmeter_data.v_intrans); + rrdset_done(st); + } + } + + // -------------------------------------------------------------------- + + if (likely(do_ipc_semaphores)) { + if (unlikely(GETSYSCTL("kern.ipc.semmni", ipc_sem.semmni))) { + do_ipc_semaphores = 0; + error("DISABLED: system.ipc_semaphores"); + error("DISABLED: system.ipc_semaphore_arrays"); + } else { + ipc_sem_data = reallocz(ipc_sem_data, sizeof(struct semid_kernel) * ipc_sem.semmni); + if (unlikely(getsysctl("kern.ipc.sema", ipc_sem_data, sizeof(struct semid_kernel) * ipc_sem.semmni))) { + do_ipc_semaphores = 0; + error("DISABLED: system.ipc_semaphores"); + error("DISABLED: system.ipc_semaphore_arrays"); + } else { + for (i = 0; i < ipc_sem.semmni; i++) { + if (unlikely(ipc_sem_data[i].u.sem_perm.mode & SEM_ALLOC)) { + ipc_sem.sets += 1; + ipc_sem.semaphores += ipc_sem_data[i].u.sem_nsems; + } + } + + // -------------------------------------------------------------------- + + st = rrdset_find("system.ipc_semaphores"); + if (unlikely(!st)) { + st = rrdset_create("system", "ipc_semaphores", NULL, "ipc semaphores", NULL, "IPC Semaphores", "semaphores", 1000, rrd_update_every, RRDSET_TYPE_AREA); + rrddim_add(st, "semaphores", NULL, 1, 1, RRDDIM_ABSOLUTE); + } + else rrdset_next(st); + + rrddim_set(st, "semaphores", ipc_sem.semaphores); + rrdset_done(st); + + // -------------------------------------------------------------------- + + st = rrdset_find("system.ipc_semaphore_arrays"); + if (unlikely(!st)) { + st = rrdset_create("system", "ipc_semaphore_arrays", NULL, "ipc semaphores", NULL, "IPC Semaphore Arrays", "arrays", 1000, rrd_update_every, RRDSET_TYPE_AREA); + rrddim_add(st, "arrays", NULL, 1, 1, RRDDIM_ABSOLUTE); + } + else rrdset_next(st); + + rrddim_set(st, "arrays", ipc_sem.sets); + rrdset_done(st); + } + } + } + + // -------------------------------------------------------------------- + + if (likely(do_ipc_shared_mem)) { + if (unlikely(GETSYSCTL("kern.ipc.shmmni", ipc_shm.shmmni))) { + do_ipc_shared_mem = 0; + error("DISABLED: system.ipc_shared_mem_segs"); + error("DISABLED: system.ipc_shared_mem_size"); + } else { + ipc_shm_data = reallocz(ipc_shm_data, sizeof(struct shmid_kernel) * ipc_shm.shmmni); + if (unlikely(getsysctl("kern.ipc.shmsegs", ipc_shm_data, sizeof(struct shmid_kernel) * ipc_shm.shmmni))) { + do_ipc_shared_mem = 0; + error("DISABLED: system.ipc_shared_mem_segs"); + error("DISABLED: system.ipc_shared_mem_size"); + } else { + for (i = 0; i < ipc_shm.shmmni; i++) { + if (unlikely(ipc_shm_data[i].u.shm_perm.mode & 0x0800)) { + ipc_shm.segs += 1; + ipc_shm.segsize += ipc_shm_data[i].u.shm_segsz; + } + } + + // -------------------------------------------------------------------- + + st = rrdset_find("system.ipc_shared_mem_segs"); + if (unlikely(!st)) { + st = rrdset_create("system", "ipc_shared_mem_segs", NULL, "ipc shared memory", NULL, "IPC Shared Memory Segments", "segments", 1000, rrd_update_every, RRDSET_TYPE_AREA); + rrddim_add(st, "segments", NULL, 1, 1, RRDDIM_ABSOLUTE); + } + else rrdset_next(st); + + rrddim_set(st, "segments", ipc_shm.segs); + rrdset_done(st); + + // -------------------------------------------------------------------- + + st = rrdset_find("system.ipc_shared_mem_size"); + if (unlikely(!st)) { + st = rrdset_create("system", "ipc_shared_mem_size", NULL, "ipc shared memory", NULL, "IPC Shared Memory Segments Size", "kilobytes", 1000, rrd_update_every, RRDSET_TYPE_AREA); + rrddim_add(st, "allocated", NULL, 1, 1024, RRDDIM_ABSOLUTE); + } + else rrdset_next(st); + + rrddim_set(st, "allocated", ipc_shm.segsize); + rrdset_done(st); + } + } + } + + // -------------------------------------------------------------------- + + if (likely(do_ipc_msg_queues)) { + if (unlikely(GETSYSCTL("kern.ipc.msgmni", ipc_msq.msgmni))) { + do_ipc_msg_queues = 0; + error("DISABLED: system.ipc_msq_queues"); + error("DISABLED: system.ipc_msq_messages"); + error("DISABLED: system.ipc_msq_size"); + } else { + ipc_msq_data = reallocz(ipc_msq_data, sizeof(struct msqid_kernel) * ipc_msq.msgmni); + if (unlikely(getsysctl("kern.ipc.msqids", ipc_msq_data, sizeof(struct msqid_kernel) * ipc_msq.msgmni))) { + do_ipc_msg_queues = 0; + error("DISABLED: system.ipc_msq_queues"); + error("DISABLED: system.ipc_msq_messages"); + error("DISABLED: system.ipc_msq_size"); + } else { + for (i = 0; i < ipc_msq.msgmni; i++) { + if (unlikely(ipc_msq_data[i].u.msg_qbytes != 0)) { + ipc_msq.queues += 1; + ipc_msq.messages += ipc_msq_data[i].u.msg_qnum; + ipc_msq.usedsize += ipc_msq_data[i].u.msg_cbytes; + ipc_msq.allocsize += ipc_msq_data[i].u.msg_qbytes; + } + } + + // -------------------------------------------------------------------- + + st = rrdset_find("system.ipc_msq_queues"); + if (unlikely(!st)) { + st = rrdset_create("system", "ipc_msq_queues", NULL, "ipc message queues", NULL, "Number of IPC Message Queues", "queues", 990, rrd_update_every, RRDSET_TYPE_AREA); + rrddim_add(st, "queues", NULL, 1, 1, RRDDIM_ABSOLUTE); + } + else rrdset_next(st); + + rrddim_set(st, "queues", ipc_msq.queues); + rrdset_done(st); + + // -------------------------------------------------------------------- + + st = rrdset_find("system.ipc_msq_messages"); + if (unlikely(!st)) { + st = rrdset_create("system", "ipc_msq_messages", NULL, "ipc message queues", NULL, "Number of Messages in IPC Message Queues", "messages", 1000, rrd_update_every, RRDSET_TYPE_AREA); + rrddim_add(st, "messages", NULL, 1, 1, RRDDIM_ABSOLUTE); + } + else rrdset_next(st); + + rrddim_set(st, "messages", ipc_msq.messages); + rrdset_done(st); + + // -------------------------------------------------------------------- + + st = rrdset_find("system.ipc_msq_size"); + if (unlikely(!st)) { + st = rrdset_create("system", "ipc_msq_size", NULL, "ipc message queues", NULL, "Size of IPC Message Queues", "bytes", 1100, rrd_update_every, RRDSET_TYPE_LINE); + rrddim_add(st, "allocated", NULL, 1, 1, RRDDIM_ABSOLUTE); + rrddim_add(st, "used", NULL, 1, 1, RRDDIM_ABSOLUTE); + } + else rrdset_next(st); + + rrddim_set(st, "allocated", ipc_msq.allocsize); + rrddim_set(st, "used", ipc_msq.usedsize); + rrdset_done(st); + + } + } + } + + // -------------------------------------------------------------------- + + if (likely(do_netisr || do_netisr_per_core)) { + if (unlikely(GETSYSCTL("kern.smp.cpus", ncpus))) { + common_error = 1; + } else if (unlikely(ncpus > 9999)) { + error("FREEBSD: There are more than 4 digits in cpu cores number"); + common_error = 1; + } else if (unlikely(sysctlbyname("net.isr.workstream", NULL, &netisr_workstream_size, NULL, 0) == -1)) { + error("FREEBSD: sysctl(net.isr.workstream...) failed: %s", strerror(errno)); + common_error = 1; + } else if (unlikely(sysctlbyname("net.isr.work", NULL, &netisr_work_size, NULL, 0) == -1)) { + error("FREEBSD: sysctl(net.isr.work...) failed: %s", strerror(errno)); + common_error = 1; + } else { + num_netisr_workstreams = netisr_workstream_size / sizeof(struct sysctl_netisr_workstream); + netisr_workstream = reallocz(netisr_workstream, num_netisr_workstreams * sizeof(struct sysctl_netisr_workstream)); + if (unlikely(getsysctl("net.isr.workstream", netisr_workstream, num_netisr_workstreams * sizeof(struct sysctl_netisr_workstream)))){ + common_error = 1; + } else { + num_netisr_works = netisr_work_size / sizeof(struct sysctl_netisr_work); + netisr_work = reallocz(netisr_work, num_netisr_works * sizeof(struct sysctl_netisr_work)); + if (unlikely(getsysctl("net.isr.work", netisr_work, num_netisr_works * sizeof(struct sysctl_netisr_work)))){ + common_error = 1; + } + } + } + if (unlikely(common_error)) { + do_netisr = 0; + error("DISABLED: system.softnet_stat"); + do_netisr_per_core = 0; + error("DISABLED: system.cpuX_softnet_stat"); + common_error = 0; + } else { + netisr_stats = reallocz(netisr_stats, (ncpus + 1) * sizeof(struct netisr_stats)); + bzero(netisr_stats, (ncpus + 1) * sizeof(struct netisr_stats)); + for (i = 0; i < num_netisr_workstreams; i++) { + for (n = 0; n < num_netisr_works; n++) { + if (netisr_workstream[i].snws_wsid == netisr_work[n].snw_wsid) { + netisr_stats[netisr_workstream[i].snws_cpu].dispatched += netisr_work[n].snw_dispatched; + netisr_stats[netisr_workstream[i].snws_cpu].hybrid_dispatched += netisr_work[n].snw_hybrid_dispatched; + netisr_stats[netisr_workstream[i].snws_cpu].qdrops += netisr_work[n].snw_qdrops; + netisr_stats[netisr_workstream[i].snws_cpu].queued += netisr_work[n].snw_queued; + } + } + } + for (i = 0; i < ncpus; i++) { + netisr_stats[ncpus].dispatched += netisr_stats[i].dispatched; + netisr_stats[ncpus].hybrid_dispatched += netisr_stats[i].hybrid_dispatched; + netisr_stats[ncpus].qdrops += netisr_stats[i].qdrops; + netisr_stats[ncpus].queued += netisr_stats[i].queued; + } + } + } + + // -------------------------------------------------------------------- + + if (likely(do_netisr)) { + st = rrdset_find_bytype("system", "softnet_stat"); + if (unlikely(!st)) { + st = rrdset_create("system", "softnet_stat", NULL, "softnet_stat", NULL, "System softnet_stat", "events/s", 955, update_every, RRDSET_TYPE_LINE); + rrddim_add(st, "dispatched", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "hybrid_dispatched", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "qdrops", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "queued", NULL, 1, 1, RRDDIM_INCREMENTAL); + } + else rrdset_next(st); + + rrddim_set(st, "dispatched", netisr_stats[ncpus].dispatched); + rrddim_set(st, "hybrid_dispatched", netisr_stats[ncpus].hybrid_dispatched); + rrddim_set(st, "qdrops", netisr_stats[ncpus].qdrops); + rrddim_set(st, "queued", netisr_stats[ncpus].queued); + rrdset_done(st); + } + + // -------------------------------------------------------------------- + + if (likely(do_netisr_per_core)) { + for (i = 0; i < ncpus ;i++) { + snprintfz(netstat_cpuid, 21, "cpu%d_softnet_stat", i); + + st = rrdset_find_bytype("cpu", netstat_cpuid); + if (unlikely(!st)) { + st = rrdset_create("cpu", netstat_cpuid, NULL, "softnet_stat", NULL, "Per CPU netisr statistics", "events/s", 1101 + i, update_every, RRDSET_TYPE_LINE); + rrddim_add(st, "dispatched", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "hybrid_dispatched", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "qdrops", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "queued", NULL, 1, 1, RRDDIM_INCREMENTAL); + } + else rrdset_next(st); + + rrddim_set(st, "dispatched", netisr_stats[i].dispatched); + rrddim_set(st, "hybrid_dispatched", netisr_stats[i].hybrid_dispatched); + rrddim_set(st, "qdrops", netisr_stats[i].qdrops); + rrddim_set(st, "queued", netisr_stats[i].queued); + rrdset_done(st); + } + } + + // -------------------------------------------------------------------- + + if (likely(do_bandwidth)) { + if (unlikely(getifaddrs(&ifap))) { + error("FREEBSD: getifaddrs()"); + do_bandwidth = 0; + error("DISABLED: system.ipv4"); + } else { + iftot.ift_ibytes = iftot.ift_obytes = 0; + for (ifa = ifap; ifa; ifa = ifa->ifa_next) { + if (ifa->ifa_addr->sa_family != AF_INET) + continue; + iftot.ift_ibytes += IFA_DATA(ibytes); + iftot.ift_obytes += IFA_DATA(obytes); + } + + st = rrdset_find("system.ipv4"); + if (unlikely(!st)) { + st = rrdset_create("system", "ipv4", NULL, "network", NULL, "IPv4 Bandwidth", "kilobits/s", 500, update_every, RRDSET_TYPE_AREA); + + rrddim_add(st, "InOctets", "received", 8, 1024, RRDDIM_INCREMENTAL); + rrddim_add(st, "OutOctets", "sent", -8, 1024, RRDDIM_INCREMENTAL); + } + else rrdset_next(st); + + rrddim_set(st, "InOctets", iftot.ift_ibytes); + rrddim_set(st, "OutOctets", iftot.ift_obytes); + rrdset_done(st); + + // -------------------------------------------------------------------- + + iftot.ift_ibytes = iftot.ift_obytes = 0; + for (ifa = ifap; ifa; ifa = ifa->ifa_next) { + if (ifa->ifa_addr->sa_family != AF_INET6) + continue; + iftot.ift_ibytes += IFA_DATA(ibytes); + iftot.ift_obytes += IFA_DATA(obytes); + } + + st = rrdset_find("system.ipv6"); + if (unlikely(!st)) { + st = rrdset_create("system", "ipv6", NULL, "network", NULL, "IPv6 Bandwidth", "kilobits/s", 500, update_every, RRDSET_TYPE_AREA); + + rrddim_add(st, "received", NULL, 8, 1024, RRDDIM_INCREMENTAL); + rrddim_add(st, "sent", NULL, -8, 1024, RRDDIM_INCREMENTAL); + } + else rrdset_next(st); + + rrddim_set(st, "sent", iftot.ift_obytes); + rrddim_set(st, "received", iftot.ift_ibytes); + rrdset_done(st); + + for (ifa = ifap; ifa; ifa = ifa->ifa_next) { + if (ifa->ifa_addr->sa_family != AF_LINK) + continue; + + // -------------------------------------------------------------------- + + st = rrdset_find_bytype("net", ifa->ifa_name); + if (unlikely(!st)) { + st = rrdset_create("net", ifa->ifa_name, NULL, ifa->ifa_name, "net.net", "Bandwidth", "kilobits/s", 7000, update_every, RRDSET_TYPE_AREA); + + rrddim_add(st, "received", NULL, 8, 1024, RRDDIM_INCREMENTAL); + rrddim_add(st, "sent", NULL, -8, 1024, RRDDIM_INCREMENTAL); + } + else rrdset_next(st); + + rrddim_set(st, "received", IFA_DATA(ibytes)); + rrddim_set(st, "sent", IFA_DATA(obytes)); + rrdset_done(st); + + // -------------------------------------------------------------------- + + st = rrdset_find_bytype("net_packets", ifa->ifa_name); + if (unlikely(!st)) { + st = rrdset_create("net_packets", ifa->ifa_name, NULL, ifa->ifa_name, "net.packets", "Packets", "packets/s", 7001, update_every, RRDSET_TYPE_LINE); + st->isdetail = 1; + + rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "multicast_received", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "multicast_sent", NULL, -1, 1, RRDDIM_INCREMENTAL); + } + else rrdset_next(st); + + rrddim_set(st, "received", IFA_DATA(ipackets)); + rrddim_set(st, "sent", IFA_DATA(opackets)); + rrddim_set(st, "multicast_received", IFA_DATA(imcasts)); + rrddim_set(st, "multicast_sent", IFA_DATA(omcasts)); + rrdset_done(st); + + // -------------------------------------------------------------------- + + st = rrdset_find_bytype("net_errors", ifa->ifa_name); + if (unlikely(!st)) { + st = rrdset_create("net_errors", ifa->ifa_name, NULL, ifa->ifa_name, "net.errors", "Interface Errors", "errors/s", 7002, update_every, RRDSET_TYPE_LINE); + st->isdetail = 1; + + rrddim_add(st, "inbound", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "outbound", NULL, -1, 1, RRDDIM_INCREMENTAL); + } + else rrdset_next(st); + + rrddim_set(st, "inbound", IFA_DATA(ierrors)); + rrddim_set(st, "outbound", IFA_DATA(oerrors)); + rrdset_done(st); + + // -------------------------------------------------------------------- + + st = rrdset_find_bytype("net_drops", ifa->ifa_name); + if (unlikely(!st)) { + st = rrdset_create("net_drops", ifa->ifa_name, NULL, ifa->ifa_name, "net.drops", "Interface Drops", "drops/s", 7003, update_every, RRDSET_TYPE_LINE); + st->isdetail = 1; + + rrddim_add(st, "inbound", NULL, 1, 1, RRDDIM_INCREMENTAL); +#if __FreeBSD__ >= 11 + rrddim_add(st, "outbound", NULL, -1, 1, RRDDIM_INCREMENTAL); +#endif + } + else rrdset_next(st); + + rrddim_set(st, "inbound", IFA_DATA(iqdrops)); +#if __FreeBSD__ >= 11 + rrddim_set(st, "outbound", IFA_DATA(oqdrops)); +#endif + rrdset_done(st); + + // -------------------------------------------------------------------- + + st = rrdset_find_bytype("net_events", ifa->ifa_name); + if (unlikely(!st)) { + st = rrdset_create("net_events", ifa->ifa_name, NULL, ifa->ifa_name, "net.events", "Network Interface Events", "events/s", 7006, update_every, RRDSET_TYPE_LINE); + st->isdetail = 1; + + rrddim_add(st, "frames", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "collisions", NULL, -1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "carrier", NULL, -1, 1, RRDDIM_INCREMENTAL); + } + else rrdset_next(st); + + rrddim_set(st, "collisions", IFA_DATA(collisions)); + rrdset_done(st); + } + + freeifaddrs(ifap); + } + } + + // -------------------------------------------------------------------- + + // see http://net-snmp.sourceforge.net/docs/mibs/tcp.html + if (likely(do_tcp_sockets)) { + if (unlikely(GETSYSCTL("net.inet.tcp.states", tcps_states))) { + do_tcp_sockets = 0; + error("DISABLED: ipv4.tcpsock"); + } else { + if (likely(do_tcp_sockets)) { + st = rrdset_find("ipv4.tcpsock"); + if (unlikely(!st)) { + st = rrdset_create("ipv4", "tcpsock", NULL, "tcp", NULL, "IPv4 TCP Connections", + "active connections", 2500, update_every, RRDSET_TYPE_LINE); + + rrddim_add(st, "CurrEstab", "connections", 1, 1, RRDDIM_ABSOLUTE); + } else + rrdset_next(st); + + rrddim_set(st, "CurrEstab", tcps_states[TCPS_ESTABLISHED]); + rrdset_done(st); + } + } + } + + // -------------------------------------------------------------------- + + // see http://net-snmp.sourceforge.net/docs/mibs/tcp.html + if (likely(do_tcp_packets || do_tcp_errors || do_tcp_handshake || do_tcpext_connaborts || do_tcpext_ofo || do_tcpext_syscookies || do_ecn)) { + if (unlikely(GETSYSCTL("net.inet.tcp.stats", tcpstat))){ + do_tcp_packets = 0; + error("DISABLED: ipv4.tcppackets"); + do_tcp_errors = 0; + error("DISABLED: ipv4.tcperrors"); + do_tcp_handshake = 0; + error("DISABLED: ipv4.tcphandshake"); + do_tcpext_connaborts = 0; + error("DISABLED: ipv4.tcpconnaborts"); + do_tcpext_ofo = 0; + error("DISABLED: ipv4.tcpofo"); + do_tcpext_syscookies = 0; + error("DISABLED: ipv4.tcpsyncookies"); + do_ecn = 0; + error("DISABLED: ipv4.ecnpkts"); + } else { + if (likely(do_tcp_packets)) { + st = rrdset_find("ipv4.tcppackets"); + if (unlikely(!st)) { + st = rrdset_create("ipv4", "tcppackets", NULL, "tcp", NULL, "IPv4 TCP Packets", + "packets/s", + 2600, update_every, RRDSET_TYPE_LINE); + + rrddim_add(st, "InSegs", "received", 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "OutSegs", "sent", -1, 1, RRDDIM_INCREMENTAL); + } else + rrdset_next(st); + + rrddim_set(st, "InSegs", tcpstat.tcps_rcvtotal); + rrddim_set(st, "OutSegs", tcpstat.tcps_sndtotal); + rrdset_done(st); + } + + // -------------------------------------------------------------------- + + if (likely(do_tcp_errors)) { + st = rrdset_find("ipv4.tcperrors"); + if (unlikely(!st)) { + st = rrdset_create("ipv4", "tcperrors", NULL, "tcp", NULL, "IPv4 TCP Errors", + "packets/s", + 2700, update_every, RRDSET_TYPE_LINE); + st->isdetail = 1; + + rrddim_add(st, "InErrs", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "InCsumErrors", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "RetransSegs", NULL, -1, 1, RRDDIM_INCREMENTAL); + } else + rrdset_next(st); + +#if __FreeBSD__ >= 11 + rrddim_set(st, "InErrs", tcpstat.tcps_rcvbadoff + tcpstat.tcps_rcvreassfull + tcpstat.tcps_rcvshort); +#else + rrddim_set(st, "InErrs", tcpstat.tcps_rcvbadoff + tcpstat.tcps_rcvshort); +#endif + rrddim_set(st, "InCsumErrors", tcpstat.tcps_rcvbadsum); + rrddim_set(st, "RetransSegs", tcpstat.tcps_sndrexmitpack); + rrdset_done(st); + } + + // -------------------------------------------------------------------- + + if (likely(do_tcp_handshake)) { + st = rrdset_find("ipv4.tcphandshake"); + if (unlikely(!st)) { + st = rrdset_create("ipv4", "tcphandshake", NULL, "tcp", NULL, + "IPv4 TCP Handshake Issues", + "events/s", 2900, update_every, RRDSET_TYPE_LINE); + st->isdetail = 1; + + rrddim_add(st, "EstabResets", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "ActiveOpens", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "PassiveOpens", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "AttemptFails", NULL, 1, 1, RRDDIM_INCREMENTAL); + } else + rrdset_next(st); + + rrddim_set(st, "EstabResets", tcpstat.tcps_drops); + rrddim_set(st, "ActiveOpens", tcpstat.tcps_connattempt); + rrddim_set(st, "PassiveOpens", tcpstat.tcps_accepts); + rrddim_set(st, "AttemptFails", tcpstat.tcps_conndrops); + rrdset_done(st); + } + + // -------------------------------------------------------------------- + + if (do_tcpext_connaborts == CONFIG_ONDEMAND_YES || (do_tcpext_connaborts == CONFIG_ONDEMAND_ONDEMAND && (tcpstat.tcps_rcvpackafterwin || tcpstat.tcps_rcvafterclose || tcpstat.tcps_rcvmemdrop || tcpstat.tcps_persistdrop || tcpstat.tcps_finwait2_drops))) { + do_tcpext_connaborts = CONFIG_ONDEMAND_YES; + st = rrdset_find("ipv4.tcpconnaborts"); + if (unlikely(!st)) { + st = rrdset_create("ipv4", "tcpconnaborts", NULL, "tcp", NULL, "TCP Connection Aborts", "connections/s", 3010, update_every, RRDSET_TYPE_LINE); + + rrddim_add(st, "TCPAbortOnData", "baddata", 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "TCPAbortOnClose", "userclosed", 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "TCPAbortOnMemory", "nomemory", 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "TCPAbortOnTimeout", "timeout", 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "TCPAbortOnLinger", "linger", 1, 1, RRDDIM_INCREMENTAL); + } + else rrdset_next(st); + + rrddim_set(st, "TCPAbortOnData", tcpstat.tcps_rcvpackafterwin); + rrddim_set(st, "TCPAbortOnClose", tcpstat.tcps_rcvafterclose); + rrddim_set(st, "TCPAbortOnMemory", tcpstat.tcps_rcvmemdrop); + rrddim_set(st, "TCPAbortOnTimeout", tcpstat.tcps_persistdrop); + rrddim_set(st, "TCPAbortOnLinger", tcpstat.tcps_finwait2_drops); + rrdset_done(st); + } + + // -------------------------------------------------------------------- + + if (do_tcpext_ofo == CONFIG_ONDEMAND_YES || (do_tcpext_ofo == CONFIG_ONDEMAND_ONDEMAND && tcpstat.tcps_rcvoopack)) { + do_tcpext_ofo = CONFIG_ONDEMAND_YES; + st = rrdset_find("ipv4.tcpofo"); + if (unlikely(!st)) { + st = rrdset_create("ipv4", "tcpofo", NULL, "tcp", NULL, "TCP Out-Of-Order Queue", "packets/s", 3050, update_every, RRDSET_TYPE_LINE); + + rrddim_add(st, "TCPOFOQueue", "inqueue", 1, 1, RRDDIM_INCREMENTAL); + } + else rrdset_next(st); + + rrddim_set(st, "TCPOFOQueue", tcpstat.tcps_rcvoopack); + rrdset_done(st); + } + + // -------------------------------------------------------------------- + + if (do_tcpext_syscookies == CONFIG_ONDEMAND_YES || (do_tcpext_syscookies == CONFIG_ONDEMAND_ONDEMAND && (tcpstat.tcps_sc_sendcookie || tcpstat.tcps_sc_recvcookie || tcpstat.tcps_sc_zonefail))) { + do_tcpext_syscookies = CONFIG_ONDEMAND_YES; + + st = rrdset_find("ipv4.tcpsyncookies"); + if (unlikely(!st)) { + st = rrdset_create("ipv4", "tcpsyncookies", NULL, "tcp", NULL, "TCP SYN Cookies", "packets/s", 3100, update_every, RRDSET_TYPE_LINE); + + rrddim_add(st, "SyncookiesRecv", "received", 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "SyncookiesSent", "sent", -1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "SyncookiesFailed", "failed", -1, 1, RRDDIM_INCREMENTAL); + } + else rrdset_next(st); + + rrddim_set(st, "SyncookiesRecv", tcpstat.tcps_sc_recvcookie); + rrddim_set(st, "SyncookiesSent", tcpstat.tcps_sc_sendcookie); + rrddim_set(st, "SyncookiesFailed", tcpstat.tcps_sc_zonefail); + rrdset_done(st); + } + + // -------------------------------------------------------------------- + + if (do_ecn == CONFIG_ONDEMAND_YES || (do_ecn == CONFIG_ONDEMAND_ONDEMAND && (tcpstat.tcps_ecn_ce || tcpstat.tcps_ecn_ect0 || tcpstat.tcps_ecn_ect1))) { + do_ecn = CONFIG_ONDEMAND_YES; + st = rrdset_find("ipv4.ecnpkts"); + if (unlikely(!st)) { + st = rrdset_create("ipv4", "ecnpkts", NULL, "ecn", NULL, "IPv4 ECN Statistics", "packets/s", 8700, update_every, RRDSET_TYPE_LINE); + st->isdetail = 1; + + rrddim_add(st, "InCEPkts", "CEP", 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "InNoECTPkts", "NoECTP", -1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "InECT0Pkts", "ECTP0", 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "InECT1Pkts", "ECTP1", 1, 1, RRDDIM_INCREMENTAL); + } + else rrdset_next(st); + + rrddim_set(st, "InCEPkts", tcpstat.tcps_ecn_ce); + rrddim_set(st, "InNoECTPkts", tcpstat.tcps_ecn_ce - (tcpstat.tcps_ecn_ect0 + tcpstat.tcps_ecn_ect1)); + rrddim_set(st, "InECT0Pkts", tcpstat.tcps_ecn_ect0); + rrddim_set(st, "InECT1Pkts", tcpstat.tcps_ecn_ect1); + rrdset_done(st); + } + + } + } + + // -------------------------------------------------------------------- + + // see http://net-snmp.sourceforge.net/docs/mibs/udp.html + if (likely(do_udp_packets || do_udp_errors)) { + if (unlikely(GETSYSCTL("net.inet.udp.stats", udpstat))) { + do_udp_packets = 0; + error("DISABLED: ipv4.udppackets"); + do_udp_errors = 0; + error("DISABLED: ipv4.udperrors"); + } else { + if (likely(do_udp_packets)) { + st = rrdset_find("ipv4.udppackets"); + if (unlikely(!st)) { + st = rrdset_create("ipv4", "udppackets", NULL, "udp", NULL, "IPv4 UDP Packets", + "packets/s", 2601, update_every, RRDSET_TYPE_LINE); + + rrddim_add(st, "InDatagrams", "received", 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "OutDatagrams", "sent", -1, 1, RRDDIM_INCREMENTAL); + } else + rrdset_next(st); + + rrddim_set(st, "InDatagrams", udpstat.udps_ipackets); + rrddim_set(st, "OutDatagrams", udpstat.udps_opackets); + rrdset_done(st); + } + + // -------------------------------------------------------------------- + + if (likely(do_udp_errors)) { + st = rrdset_find("ipv4.udperrors"); + if (unlikely(!st)) { + st = rrdset_create("ipv4", "udperrors", NULL, "udp", NULL, "IPv4 UDP Errors", "events/s", + 2701, update_every, RRDSET_TYPE_LINE); + st->isdetail = 1; + + rrddim_add(st, "RcvbufErrors", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "InErrors", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "NoPorts", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "InCsumErrors", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "IgnoredMulti", NULL, 1, 1, RRDDIM_INCREMENTAL); + } else + rrdset_next(st); + + rrddim_set(st, "InErrors", udpstat.udps_hdrops + udpstat.udps_badlen); + rrddim_set(st, "NoPorts", udpstat.udps_noport); + rrddim_set(st, "RcvbufErrors", udpstat.udps_fullsock); + rrddim_set(st, "InCsumErrors", udpstat.udps_badsum + udpstat.udps_nosum); + rrddim_set(st, "IgnoredMulti", udpstat.udps_filtermcast); + rrdset_done(st); + } + } + } + + // -------------------------------------------------------------------- + + if (likely(do_icmp_packets || do_icmpmsg)) { + if (unlikely(GETSYSCTL("net.inet.icmp.stats", icmpstat))) { + do_icmp_packets = 0; + error("DISABLED: ipv4.icmp"); + error("DISABLED: ipv4.icmp_errors"); + do_icmpmsg = 0; + error("DISABLED: ipv4.icmpmsg"); + } else { + for (i = 0; i <= ICMP_MAXTYPE; i++) { + icmp_total.msgs_in += icmpstat.icps_inhist[i]; + icmp_total.msgs_out += icmpstat.icps_outhist[i]; + } + icmp_total.msgs_in += icmpstat.icps_badcode + icmpstat.icps_badlen + icmpstat.icps_checksum + icmpstat.icps_tooshort; + + // -------------------------------------------------------------------- + + if (likely(do_icmp_packets)) { + st = rrdset_find("ipv4.icmp"); + if (unlikely(!st)) { + st = rrdset_create("ipv4", "icmp", NULL, "icmp", NULL, "IPv4 ICMP Packets", "packets/s", + 2602, + update_every, RRDSET_TYPE_LINE); + + rrddim_add(st, "InMsgs", "received", 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "OutMsgs", "sent", -1, 1, RRDDIM_INCREMENTAL); + } else + rrdset_next(st); + + rrddim_set(st, "InMsgs", icmp_total.msgs_in); + rrddim_set(st, "OutMsgs", icmp_total.msgs_out); + + rrdset_done(st); + + // -------------------------------------------------------------------- + + st = rrdset_find("ipv4.icmp_errors"); + if (unlikely(!st)) { + st = rrdset_create("ipv4", "icmp_errors", NULL, "icmp", NULL, "IPv4 ICMP Errors", + "packets/s", + 2603, update_every, RRDSET_TYPE_LINE); + + rrddim_add(st, "InErrors", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "OutErrors", NULL, -1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "InCsumErrors", NULL, 1, 1, RRDDIM_INCREMENTAL); + } else + rrdset_next(st); + + rrddim_set(st, "InErrors", icmpstat.icps_badcode + icmpstat.icps_badlen + icmpstat.icps_checksum + icmpstat.icps_tooshort); + rrddim_set(st, "OutErrors", icmpstat.icps_error); + rrddim_set(st, "InCsumErrors", icmpstat.icps_checksum); + + rrdset_done(st); + } + + // -------------------------------------------------------------------- + + if (likely(do_icmpmsg)) { + st = rrdset_find("ipv4.icmpmsg"); + if (unlikely(!st)) { + st = rrdset_create("ipv4", "icmpmsg", NULL, "icmp", NULL, "IPv4 ICMP Messsages", + "packets/s", 2604, update_every, RRDSET_TYPE_LINE); + + rrddim_add(st, "InEchoReps", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "OutEchoReps", NULL, -1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "InEchos", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "OutEchos", NULL, -1, 1, RRDDIM_INCREMENTAL); + } else + rrdset_next(st); + + rrddim_set(st, "InEchoReps", icmpstat.icps_inhist[ICMP_ECHOREPLY]); + rrddim_set(st, "OutEchoReps", icmpstat.icps_outhist[ICMP_ECHOREPLY]); + rrddim_set(st, "InEchos", icmpstat.icps_inhist[ICMP_ECHO]); + rrddim_set(st, "OutEchos", icmpstat.icps_outhist[ICMP_ECHO]); + + rrdset_done(st); + } + } + } + + // -------------------------------------------------------------------- + + // see also http://net-snmp.sourceforge.net/docs/mibs/ip.html + if (likely(do_ip_packets || do_ip_fragsout || do_ip_fragsin || do_ip_errors)) { + if (unlikely(GETSYSCTL("net.inet.ip.stats", ipstat))) { + do_ip_packets = 0; + error("DISABLED: ipv4.packets"); + do_ip_fragsout = 0; + error("DISABLED: ipv4.fragsout"); + do_ip_fragsin = 0; + error("DISABLED: ipv4.fragsin"); + do_ip_errors = 0; + error("DISABLED: ipv4.errors"); + } else { + if (likely(do_ip_packets)) { + st = rrdset_find("ipv4.packets"); + if (unlikely(!st)) { + st = rrdset_create("ipv4", "packets", NULL, "packets", NULL, "IPv4 Packets", "packets/s", + 3000, update_every, RRDSET_TYPE_LINE); + + rrddim_add(st, "InReceives", "received", 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "OutRequests", "sent", -1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "ForwDatagrams", "forwarded", 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "InDelivers", "delivered", 1, 1, RRDDIM_INCREMENTAL); + } else + rrdset_next(st); + + rrddim_set(st, "OutRequests", ipstat.ips_localout); + rrddim_set(st, "InReceives", ipstat.ips_total); + rrddim_set(st, "ForwDatagrams", ipstat.ips_forward); + rrddim_set(st, "InDelivers", ipstat.ips_delivered); + rrdset_done(st); + } + + // -------------------------------------------------------------------- + + if (likely(do_ip_fragsout)) { + st = rrdset_find("ipv4.fragsout"); + if (unlikely(!st)) { + st = rrdset_create("ipv4", "fragsout", NULL, "fragments", NULL, "IPv4 Fragments Sent", + "packets/s", 3010, update_every, RRDSET_TYPE_LINE); + st->isdetail = 1; + + rrddim_add(st, "FragOKs", "ok", 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "FragFails", "failed", -1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "FragCreates", "created", 1, 1, RRDDIM_INCREMENTAL); + } else + rrdset_next(st); + + rrddim_set(st, "FragOKs", ipstat.ips_fragmented); + rrddim_set(st, "FragFails", ipstat.ips_cantfrag); + rrddim_set(st, "FragCreates", ipstat.ips_ofragments); + rrdset_done(st); + } + + // -------------------------------------------------------------------- + + if (likely(do_ip_fragsin)) { + st = rrdset_find("ipv4.fragsin"); + if (unlikely(!st)) { + st = rrdset_create("ipv4", "fragsin", NULL, "fragments", NULL, + "IPv4 Fragments Reassembly", + "packets/s", 3011, update_every, RRDSET_TYPE_LINE); + st->isdetail = 1; + + rrddim_add(st, "ReasmOKs", "ok", 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "ReasmFails", "failed", -1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "ReasmReqds", "all", 1, 1, RRDDIM_INCREMENTAL); + } else + rrdset_next(st); + + rrddim_set(st, "ReasmOKs", ipstat.ips_fragments); + rrddim_set(st, "ReasmFails", ipstat.ips_fragdropped); + rrddim_set(st, "ReasmReqds", ipstat.ips_reassembled); + rrdset_done(st); + } + + // -------------------------------------------------------------------- + + if (likely(do_ip_errors)) { + st = rrdset_find("ipv4.errors"); + if (unlikely(!st)) { + st = rrdset_create("ipv4", "errors", NULL, "errors", NULL, "IPv4 Errors", "packets/s", + 3002, + update_every, RRDSET_TYPE_LINE); + st->isdetail = 1; + + rrddim_add(st, "InDiscards", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "OutDiscards", NULL, -1, 1, RRDDIM_INCREMENTAL); + + rrddim_add(st, "InHdrErrors", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "OutNoRoutes", NULL, -1, 1, RRDDIM_INCREMENTAL); + + rrddim_add(st, "InAddrErrors", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "InUnknownProtos", NULL, 1, 1, RRDDIM_INCREMENTAL); + } else + rrdset_next(st); + + rrddim_set(st, "InDiscards", ipstat.ips_badsum + ipstat.ips_tooshort + ipstat.ips_toosmall + ipstat.ips_toolong); + rrddim_set(st, "OutDiscards", ipstat.ips_odropped); + rrddim_set(st, "InHdrErrors", ipstat.ips_badhlen + ipstat.ips_badlen + ipstat.ips_badoptions + ipstat.ips_badvers); + rrddim_set(st, "InAddrErrors", ipstat.ips_badaddr); + rrddim_set(st, "InUnknownProtos", ipstat.ips_noproto); + rrddim_set(st, "OutNoRoutes", ipstat.ips_noroute); + rrdset_done(st); + } + } + } + + // -------------------------------------------------------------------- + + if (likely(do_ip6_packets || do_ip6_fragsout || do_ip6_fragsin || do_ip6_errors)) { + if (unlikely(GETSYSCTL("net.inet6.ip6.stats", ip6stat))) { + do_ip6_packets = 0; + error("DISABLED: ipv6.packets"); + do_ip6_fragsout = 0; + error("DISABLED: ipv6.fragsout"); + do_ip6_fragsin = 0; + error("DISABLED: ipv6.fragsin"); + do_ip6_errors = 0; + error("DISABLED: ipv6.errors"); + } else { + if (do_ip6_packets == CONFIG_ONDEMAND_YES || (do_ip6_packets == CONFIG_ONDEMAND_ONDEMAND && + (ip6stat.ip6s_localout || ip6stat.ip6s_total || + ip6stat.ip6s_forward || ip6stat.ip6s_delivered))) { + do_ip6_packets = CONFIG_ONDEMAND_YES; + st = rrdset_find("ipv6.packets"); + if (unlikely(!st)) { + st = rrdset_create("ipv6", "packets", NULL, "packets", NULL, "IPv6 Packets", "packets/s", 3000, + update_every, RRDSET_TYPE_LINE); + + rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "forwarded", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "delivers", NULL, -1, 1, RRDDIM_INCREMENTAL); + } else + rrdset_next(st); + + rrddim_set(st, "sent", ip6stat.ip6s_localout); + rrddim_set(st, "received", ip6stat.ip6s_total); + rrddim_set(st, "forwarded", ip6stat.ip6s_forward); + rrddim_set(st, "delivers", ip6stat.ip6s_delivered); + rrdset_done(st); + } + + // -------------------------------------------------------------------- + + if (do_ip6_fragsout == CONFIG_ONDEMAND_YES || (do_ip6_fragsout == CONFIG_ONDEMAND_ONDEMAND && + (ip6stat.ip6s_fragmented || ip6stat.ip6s_cantfrag || + ip6stat.ip6s_ofragments))) { + do_ip6_fragsout = CONFIG_ONDEMAND_YES; + st = rrdset_find("ipv6.fragsout"); + if (unlikely(!st)) { + st = rrdset_create("ipv6", "fragsout", NULL, "fragments", NULL, "IPv6 Fragments Sent", + "packets/s", 3010, update_every, RRDSET_TYPE_LINE); + st->isdetail = 1; + + rrddim_add(st, "ok", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "failed", NULL, -1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "all", NULL, 1, 1, RRDDIM_INCREMENTAL); + } else + rrdset_next(st); + + rrddim_set(st, "ok", ip6stat.ip6s_fragmented); + rrddim_set(st, "failed", ip6stat.ip6s_cantfrag); + rrddim_set(st, "all", ip6stat.ip6s_ofragments); + rrdset_done(st); + } + + // -------------------------------------------------------------------- + + if (do_ip6_fragsin == CONFIG_ONDEMAND_YES || (do_ip6_fragsin == CONFIG_ONDEMAND_ONDEMAND && + (ip6stat.ip6s_reassembled || ip6stat.ip6s_fragdropped || + ip6stat.ip6s_fragtimeout || ip6stat.ip6s_fragments))) { + do_ip6_fragsin = CONFIG_ONDEMAND_YES; + st = rrdset_find("ipv6.fragsin"); + if (unlikely(!st)) { + st = rrdset_create("ipv6", "fragsin", NULL, "fragments", NULL, "IPv6 Fragments Reassembly", + "packets/s", 3011, update_every, RRDSET_TYPE_LINE); + st->isdetail = 1; + + rrddim_add(st, "ok", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "failed", NULL, -1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "timeout", NULL, -1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "all", NULL, 1, 1, RRDDIM_INCREMENTAL); + } else + rrdset_next(st); + + rrddim_set(st, "ok", ip6stat.ip6s_reassembled); + rrddim_set(st, "failed", ip6stat.ip6s_fragdropped); + rrddim_set(st, "timeout", ip6stat.ip6s_fragtimeout); + rrddim_set(st, "all", ip6stat.ip6s_fragments); + rrdset_done(st); + } + + // -------------------------------------------------------------------- + + if (do_ip6_errors == CONFIG_ONDEMAND_YES || (do_ip6_errors == CONFIG_ONDEMAND_ONDEMAND && ( + ip6stat.ip6s_toosmall || + ip6stat.ip6s_odropped || + ip6stat.ip6s_badoptions || + ip6stat.ip6s_badvers || + ip6stat.ip6s_exthdrtoolong || + ip6stat.ip6s_sources_none || + ip6stat.ip6s_tooshort || + ip6stat.ip6s_cantforward || + ip6stat.ip6s_noroute))) { + do_ip6_errors = CONFIG_ONDEMAND_YES; + st = rrdset_find("ipv6.errors"); + if (unlikely(!st)) { + st = rrdset_create("ipv6", "errors", NULL, "errors", NULL, "IPv6 Errors", "packets/s", 3002, + update_every, RRDSET_TYPE_LINE); + st->isdetail = 1; + + rrddim_add(st, "InDiscards", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "OutDiscards", NULL, -1, 1, RRDDIM_INCREMENTAL); + + rrddim_add(st, "InHdrErrors", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "InAddrErrors", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "InTruncatedPkts", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "InNoRoutes", NULL, 1, 1, RRDDIM_INCREMENTAL); + + rrddim_add(st, "OutNoRoutes", NULL, -1, 1, RRDDIM_INCREMENTAL); + } else + rrdset_next(st); + + rrddim_set(st, "InDiscards", ip6stat.ip6s_toosmall); + rrddim_set(st, "OutDiscards", ip6stat.ip6s_odropped); + + rrddim_set(st, "InHdrErrors", + ip6stat.ip6s_badoptions + ip6stat.ip6s_badvers + ip6stat.ip6s_exthdrtoolong); + rrddim_set(st, "InAddrErrors", ip6stat.ip6s_sources_none); + rrddim_set(st, "InTruncatedPkts", ip6stat.ip6s_tooshort); + rrddim_set(st, "InNoRoutes", ip6stat.ip6s_cantforward); + + rrddim_set(st, "OutNoRoutes", ip6stat.ip6s_noroute); + rrdset_done(st); + } + } + } + + // -------------------------------------------------------------------- + + if (likely(do_icmp6 || do_icmp6_redir || do_icmp6_errors || do_icmp6_echos || do_icmp6_router || do_icmp6_neighbor || do_icmp6_types)) { + if (unlikely(GETSYSCTL("net.inet6.icmp6.stats", icmp6stat))) { + do_icmp6 = 0; + error("DISABLED: ipv6.icmp"); + } else { + for (i = 0; i <= ICMP6_MAXTYPE; i++) { + icmp6_total.msgs_in += icmp6stat.icp6s_inhist[i]; + icmp6_total.msgs_out += icmp6stat.icp6s_outhist[i]; + } + icmp6_total.msgs_in += icmp6stat.icp6s_badcode + icmp6stat.icp6s_badlen + icmp6stat.icp6s_checksum + icmp6stat.icp6s_tooshort; + if (do_icmp6 == CONFIG_ONDEMAND_YES || (do_icmp6 == CONFIG_ONDEMAND_ONDEMAND && (icmp6_total.msgs_in || icmp6_total.msgs_out))) { + do_icmp6 = CONFIG_ONDEMAND_YES; + st = rrdset_find("ipv6.icmp"); + if (unlikely(!st)) { + st = rrdset_create("ipv6", "icmp", NULL, "icmp", NULL, "IPv6 ICMP Messages", + "messages/s", 10000, update_every, RRDSET_TYPE_LINE); + + rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL); + } else + rrdset_next(st); + + rrddim_set(st, "sent", icmp6_total.msgs_in); + rrddim_set(st, "received", icmp6_total.msgs_out); + rrdset_done(st); + } + + // -------------------------------------------------------------------- + + if (do_icmp6_redir == CONFIG_ONDEMAND_YES || (do_icmp6_redir == CONFIG_ONDEMAND_ONDEMAND && (icmp6stat.icp6s_inhist[ND_REDIRECT] || icmp6stat.icp6s_outhist[ND_REDIRECT]))) { + do_icmp6_redir = CONFIG_ONDEMAND_YES; + st = rrdset_find("ipv6.icmpredir"); + if (unlikely(!st)) { + st = rrdset_create("ipv6", "icmpredir", NULL, "icmp", NULL, "IPv6 ICMP Redirects", + "redirects/s", 10050, update_every, RRDSET_TYPE_LINE); + + rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL); + } else + rrdset_next(st); + + rrddim_set(st, "sent", icmp6stat.icp6s_inhist[ND_REDIRECT]); + rrddim_set(st, "received", icmp6stat.icp6s_outhist[ND_REDIRECT]); + rrdset_done(st); + } + + // -------------------------------------------------------------------- + + if (do_icmp6_errors == CONFIG_ONDEMAND_YES || (do_icmp6_errors == CONFIG_ONDEMAND_ONDEMAND && ( + icmp6stat.icp6s_badcode || + icmp6stat.icp6s_badlen || + icmp6stat.icp6s_checksum || + icmp6stat.icp6s_tooshort || + icmp6stat.icp6s_error || + icmp6stat.icp6s_inhist[ICMP6_DST_UNREACH] || + icmp6stat.icp6s_inhist[ICMP6_TIME_EXCEEDED] || + icmp6stat.icp6s_inhist[ICMP6_PARAM_PROB] || + icmp6stat.icp6s_outhist[ICMP6_DST_UNREACH] || + icmp6stat.icp6s_outhist[ICMP6_TIME_EXCEEDED] || + icmp6stat.icp6s_outhist[ICMP6_PARAM_PROB]))) { + do_icmp6_errors = CONFIG_ONDEMAND_YES; + st = rrdset_find("ipv6.icmperrors"); + if (unlikely(!st)) { + st = rrdset_create("ipv6", "icmperrors", NULL, "icmp", NULL, "IPv6 ICMP Errors", "errors/s", 10100, update_every, RRDSET_TYPE_LINE); + + rrddim_add(st, "InErrors", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "OutErrors", NULL, -1, 1, RRDDIM_INCREMENTAL); + + rrddim_add(st, "InCsumErrors", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "InDestUnreachs", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "InPktTooBigs", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "InTimeExcds", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "InParmProblems", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "OutDestUnreachs", NULL, -1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "OutTimeExcds", NULL, -1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "OutParmProblems", NULL, -1, 1, RRDDIM_INCREMENTAL); + } else + rrdset_next(st); + + rrddim_set(st, "InErrors", icmp6stat.icp6s_badcode + icmp6stat.icp6s_badlen + icmp6stat.icp6s_checksum + icmp6stat.icp6s_tooshort); + rrddim_set(st, "OutErrors", icmp6stat.icp6s_error); + rrddim_set(st, "InCsumErrors", icmp6stat.icp6s_checksum); + rrddim_set(st, "InDestUnreachs", icmp6stat.icp6s_inhist[ICMP6_DST_UNREACH]); + rrddim_set(st, "InPktTooBigs", icmp6stat.icp6s_badlen); + rrddim_set(st, "InTimeExcds", icmp6stat.icp6s_inhist[ICMP6_TIME_EXCEEDED]); + rrddim_set(st, "InParmProblems", icmp6stat.icp6s_inhist[ICMP6_PARAM_PROB]); + rrddim_set(st, "OutDestUnreachs", icmp6stat.icp6s_outhist[ICMP6_DST_UNREACH]); + rrddim_set(st, "OutTimeExcds", icmp6stat.icp6s_outhist[ICMP6_TIME_EXCEEDED]); + rrddim_set(st, "OutParmProblems", icmp6stat.icp6s_outhist[ICMP6_PARAM_PROB]); + rrdset_done(st); + } + + // -------------------------------------------------------------------- + + if (do_icmp6_echos == CONFIG_ONDEMAND_YES || (do_icmp6_echos == CONFIG_ONDEMAND_ONDEMAND && ( + icmp6stat.icp6s_inhist[ICMP6_ECHO_REQUEST] || + icmp6stat.icp6s_outhist[ICMP6_ECHO_REQUEST] || + icmp6stat.icp6s_inhist[ICMP6_ECHO_REPLY] || + icmp6stat.icp6s_outhist[ICMP6_ECHO_REPLY]))) { + do_icmp6_echos = CONFIG_ONDEMAND_YES; + st = rrdset_find("ipv6.icmpechos"); + if (unlikely(!st)) { + st = rrdset_create("ipv6", "icmpechos", NULL, "icmp", NULL, "IPv6 ICMP Echo", "messages/s", 10200, update_every, RRDSET_TYPE_LINE); + + rrddim_add(st, "InEchos", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "OutEchos", NULL, -1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "InEchoReplies", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "OutEchoReplies", NULL, -1, 1, RRDDIM_INCREMENTAL); + } else + rrdset_next(st); + + rrddim_set(st, "InEchos", icmp6stat.icp6s_inhist[ICMP6_ECHO_REQUEST]); + rrddim_set(st, "OutEchos", icmp6stat.icp6s_outhist[ICMP6_ECHO_REQUEST]); + rrddim_set(st, "InEchoReplies", icmp6stat.icp6s_inhist[ICMP6_ECHO_REPLY]); + rrddim_set(st, "OutEchoReplies", icmp6stat.icp6s_outhist[ICMP6_ECHO_REPLY]); + rrdset_done(st); + } + + // -------------------------------------------------------------------- + + if (do_icmp6_router == CONFIG_ONDEMAND_YES || (do_icmp6_router == CONFIG_ONDEMAND_ONDEMAND && ( + icmp6stat.icp6s_inhist[ND_ROUTER_SOLICIT] || + icmp6stat.icp6s_outhist[ND_ROUTER_SOLICIT] || + icmp6stat.icp6s_inhist[ND_ROUTER_ADVERT] || + icmp6stat.icp6s_outhist[ND_ROUTER_ADVERT]))) { + do_icmp6_router = CONFIG_ONDEMAND_YES; + st = rrdset_find("ipv6.icmprouter"); + if (unlikely(!st)) { + st = rrdset_create("ipv6", "icmprouter", NULL, "icmp", NULL, "IPv6 Router Messages", "messages/s", 10400, update_every, RRDSET_TYPE_LINE); + + rrddim_add(st, "InSolicits", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "OutSolicits", NULL, -1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "InAdvertisements", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "OutAdvertisements", NULL, -1, 1, RRDDIM_INCREMENTAL); + } else + rrdset_next(st); + + rrddim_set(st, "InSolicits", icmp6stat.icp6s_inhist[ND_ROUTER_SOLICIT]); + rrddim_set(st, "OutSolicits", icmp6stat.icp6s_outhist[ND_ROUTER_SOLICIT]); + rrddim_set(st, "InAdvertisements", icmp6stat.icp6s_inhist[ND_ROUTER_ADVERT]); + rrddim_set(st, "OutAdvertisements", icmp6stat.icp6s_outhist[ND_ROUTER_ADVERT]); + rrdset_done(st); + } + + // -------------------------------------------------------------------- + + if (do_icmp6_neighbor == CONFIG_ONDEMAND_YES || (do_icmp6_neighbor == CONFIG_ONDEMAND_ONDEMAND && ( + icmp6stat.icp6s_inhist[ND_NEIGHBOR_SOLICIT] || + icmp6stat.icp6s_outhist[ND_NEIGHBOR_SOLICIT] || + icmp6stat.icp6s_inhist[ND_NEIGHBOR_ADVERT] || + icmp6stat.icp6s_outhist[ND_NEIGHBOR_ADVERT]))) { + do_icmp6_neighbor = CONFIG_ONDEMAND_YES; + st = rrdset_find("ipv6.icmpneighbor"); + if (unlikely(!st)) { + st = rrdset_create("ipv6", "icmpneighbor", NULL, "icmp", NULL, "IPv6 Neighbor Messages", "messages/s", 10500, update_every, RRDSET_TYPE_LINE); + + rrddim_add(st, "InSolicits", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "OutSolicits", NULL, -1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "InAdvertisements", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "OutAdvertisements", NULL, -1, 1, RRDDIM_INCREMENTAL); + } else + rrdset_next(st); + + rrddim_set(st, "InSolicits", icmp6stat.icp6s_inhist[ND_NEIGHBOR_SOLICIT]); + rrddim_set(st, "OutSolicits", icmp6stat.icp6s_outhist[ND_NEIGHBOR_SOLICIT]); + rrddim_set(st, "InAdvertisements", icmp6stat.icp6s_inhist[ND_NEIGHBOR_ADVERT]); + rrddim_set(st, "OutAdvertisements", icmp6stat.icp6s_outhist[ND_NEIGHBOR_ADVERT]); + rrdset_done(st); + } + + // -------------------------------------------------------------------- + + if (do_icmp6_types == CONFIG_ONDEMAND_YES || (do_icmp6_types == CONFIG_ONDEMAND_ONDEMAND && ( + icmp6stat.icp6s_inhist[1] || + icmp6stat.icp6s_inhist[128] || + icmp6stat.icp6s_inhist[129] || + icmp6stat.icp6s_inhist[136] || + icmp6stat.icp6s_outhist[1] || + icmp6stat.icp6s_outhist[128] || + icmp6stat.icp6s_outhist[129] || + icmp6stat.icp6s_outhist[133] || + icmp6stat.icp6s_outhist[135] || + icmp6stat.icp6s_outhist[136]))) { + do_icmp6_types = CONFIG_ONDEMAND_YES; + st = rrdset_find("ipv6.icmptypes"); + if (unlikely(!st)) { + st = rrdset_create("ipv6", "icmptypes", NULL, "icmp", NULL, "IPv6 ICMP Types", + "messages/s", 10700, update_every, RRDSET_TYPE_LINE); + + rrddim_add(st, "InType1", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "InType128", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "InType129", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "InType136", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "OutType1", NULL, -1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "OutType128", NULL, -1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "OutType129", NULL, -1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "OutType133", NULL, -1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "OutType135", NULL, -1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "OutType143", NULL, -1, 1, RRDDIM_INCREMENTAL); + } else + rrdset_next(st); + + rrddim_set(st, "InType1", icmp6stat.icp6s_inhist[1]); + rrddim_set(st, "InType128", icmp6stat.icp6s_inhist[128]); + rrddim_set(st, "InType129", icmp6stat.icp6s_inhist[129]); + rrddim_set(st, "InType136", icmp6stat.icp6s_inhist[136]); + rrddim_set(st, "OutType1", icmp6stat.icp6s_outhist[1]); + rrddim_set(st, "OutType128", icmp6stat.icp6s_outhist[128]); + rrddim_set(st, "OutType129", icmp6stat.icp6s_outhist[129]); + rrddim_set(st, "OutType133", icmp6stat.icp6s_outhist[133]); + rrddim_set(st, "OutType135", icmp6stat.icp6s_outhist[135]); + rrddim_set(st, "OutType143", icmp6stat.icp6s_outhist[143]); + rrdset_done(st); + } + } + } + + // -------------------------------------------------------------------------- + + if (likely(do_space || do_inodes)) { + // there is no mount info in sysctl MIBs + if (unlikely(!(mntsize = getmntinfo(&mntbuf, MNT_NOWAIT)))) { + error("FREEBSD: getmntinfo() failed"); + do_space = 0; + error("DISABLED: disk_space.X"); + do_inodes = 0; + error("DISABLED: disk_inodes.X"); + } else { + for (i = 0; i < mntsize; i++) { + if (mntbuf[i].f_flags == MNT_RDONLY || + mntbuf[i].f_blocks == 0 || + // taken from gnulib/mountlist.c and shortened to FreeBSD related fstypes + strcmp(mntbuf[i].f_fstypename, "autofs") == 0 || + strcmp(mntbuf[i].f_fstypename, "procfs") == 0 || + strcmp(mntbuf[i].f_fstypename, "subfs") == 0 || + strcmp(mntbuf[i].f_fstypename, "devfs") == 0 || + strcmp(mntbuf[i].f_fstypename, "none") == 0) + continue; + + // -------------------------------------------------------------------------- + + if (likely(do_space)) { + st = rrdset_find_bytype("disk_space", mntbuf[i].f_mntonname); + if (unlikely(!st)) { + snprintfz(title, 4096, "Disk Space Usage for %s [%s]", mntbuf[i].f_mntonname, mntbuf[i].f_mntfromname); + st = rrdset_create("disk_space", mntbuf[i].f_mntonname, NULL, mntbuf[i].f_mntonname, "disk.space", title, "GB", 2023, + update_every, + RRDSET_TYPE_STACKED); + + rrddim_add(st, "avail", NULL, mntbuf[i].f_bsize, GIGA_FACTOR, RRDDIM_ABSOLUTE); + rrddim_add(st, "used", NULL, mntbuf[i].f_bsize, GIGA_FACTOR, RRDDIM_ABSOLUTE); + rrddim_add(st, "reserved_for_root", "reserved for root", mntbuf[i].f_bsize, GIGA_FACTOR, + RRDDIM_ABSOLUTE); + } else + rrdset_next(st); + + rrddim_set(st, "avail", (collected_number) mntbuf[i].f_bavail); + rrddim_set(st, "used", (collected_number) (mntbuf[i].f_blocks - mntbuf[i].f_bfree)); + rrddim_set(st, "reserved_for_root", (collected_number) (mntbuf[i].f_bfree - mntbuf[i].f_bavail)); + rrdset_done(st); + } + + // -------------------------------------------------------------------------- + + if (likely(do_inodes)) { + st = rrdset_find_bytype("disk_inodes", mntbuf[i].f_mntonname); + if (unlikely(!st)) { + snprintfz(title, 4096, "Disk Files (inodes) Usage for %s [%s]", mntbuf[i].f_mntonname, mntbuf[i].f_mntfromname); + st = rrdset_create("disk_inodes", mntbuf[i].f_mntonname, NULL, mntbuf[i].f_mntonname, "disk.inodes", title, "Inodes", 2024, + update_every, RRDSET_TYPE_STACKED); + + rrddim_add(st, "avail", NULL, 1, 1, RRDDIM_ABSOLUTE); + rrddim_add(st, "used", NULL, 1, 1, RRDDIM_ABSOLUTE); + rrddim_add(st, "reserved_for_root", "reserved for root", 1, 1, RRDDIM_ABSOLUTE); + } else + rrdset_next(st); + + rrddim_set(st, "avail", (collected_number) mntbuf[i].f_ffree); + rrddim_set(st, "used", (collected_number) (mntbuf[i].f_files - mntbuf[i].f_ffree)); + rrdset_done(st); + } + } + } + } + + // -------------------------------------------------------------------- + + if (likely(do_uptime)) { + if (unlikely(GETSYSCTL("kern.boottime", boot_time))) { + do_uptime = 0; + error("DISABLED: system.uptime"); + } else { + clock_gettime(CLOCK_REALTIME, &cur_time); + st = rrdset_find("system.uptime"); + + if(unlikely(!st)) { + st = rrdset_create("system", "uptime", NULL, "uptime", NULL, "System Uptime", "seconds", 1000, update_every, RRDSET_TYPE_LINE); + rrddim_add(st, "uptime", NULL, 1, 1, RRDDIM_ABSOLUTE); + } + else rrdset_next(st); + + rrddim_set(st, "uptime", cur_time.tv_sec - boot_time.tv_sec); + rrdset_done(st); + } + } + + return 0; +} diff --git a/src/global_statistics.c b/src/global_statistics.c index bb2b1f08f..a698615f4 100644 --- a/src/global_statistics.c +++ b/src/global_statistics.c @@ -131,7 +131,7 @@ void global_statistics_charts(void) { if (!stcpu_thread) stcpu_thread = rrdset_find("netdata.plugin_proc_cpu"); if (!stcpu_thread) { - stcpu_thread = rrdset_create("netdata", "plugin_proc_cpu", NULL, "proc.internal", NULL, + stcpu_thread = rrdset_create("netdata", "plugin_proc_cpu", NULL, "proc", NULL, "NetData Proc Plugin CPU usage", "milliseconds/s", 132000, rrd_update_every, RRDSET_TYPE_STACKED); diff --git a/src/health.c b/src/health.c index 596b143a4..193312eec 100644..100755 --- a/src/health.c +++ b/src/health.c @@ -6,6 +6,7 @@ struct health_options { const char *health_default_exec; const char *health_default_recipient; const char *log_filename; + size_t log_entries_written; FILE *log_fp; }; @@ -13,6 +14,7 @@ static struct health_options health = { .health_default_exec = PLUGINS_DIR "/alarm-notify.sh", .health_default_recipient = "root", .log_filename = VARLIB_DIR "/health/alarm_log.db", + .log_entries_written = 0, .log_fp = NULL }; @@ -30,11 +32,11 @@ static inline int health_alarm_log_open(void) { if(health.log_fp) { if (setvbuf(health.log_fp, NULL, _IOLBF, 0) != 0) - error("Cannot set line buffering on health log file."); + error("Health: cannot set line buffering on health log file."); return 0; } - error("Cannot open health log file '%s'. Health data will be lost in case of netdata or server crash.", health.log_filename); + error("Health: cannot open health log file '%s'. Health data will be lost in case of netdata or server crash.", health.log_filename); return -1; } @@ -45,51 +47,309 @@ static inline void health_alarm_log_close(void) { } } -static inline void health_log_recreate(void) { - if(health.log_fp != NULL) { +static inline void health_log_rotate(void) { + static size_t rotate_every = 0; + + if(unlikely(rotate_every == 0)) { + rotate_every = (size_t)config_get_number("health", "rotate log every lines", 2000); + if(rotate_every < 100) rotate_every = 100; + } + + if(unlikely(health.log_entries_written > rotate_every)) { health_alarm_log_close(); + char old_filename[FILENAME_MAX + 1]; + snprintfz(old_filename, FILENAME_MAX, "%s.old", health.log_filename); + + if(unlink(old_filename) == -1 && errno != ENOENT) + error("Health: cannot remove old alarms log file '%s'", old_filename); + + if(link(health.log_filename, old_filename) == -1 && errno != ENOENT) + error("Health: cannot move file '%s' to '%s'.", health.log_filename, old_filename); + + if(unlink(health.log_filename) == -1 && errno != ENOENT) + error("Health: cannot remove old alarms log file '%s'", health.log_filename); + // open it with truncate health.log_fp = fopen(health.log_filename, "w"); - if(health.log_fp) fclose(health.log_fp); - else error("Cannot truncate health log '%s'", health.log_filename); + + if(health.log_fp) + fclose(health.log_fp); + else + error("Health: cannot truncate health log '%s'", health.log_filename); health.log_fp = NULL; + health.log_entries_written = 0; health_alarm_log_open(); } } static inline void health_alarm_log_save(RRDHOST *host, ALARM_ENTRY *ae) { - (void)host; - (void)ae; - -/* if(likely(health.log_fp)) { - if(unlikely(fprintf(health.log_fp, "A\t%s\t%08x\t%08x\t%08x\t%08x\t%08x\t%08x\t%s\t%s\t%s\t%s\t%s\t%08x\n", - host->hostname, - ae->unique_id, - ae->alarm_id, - ae->alarm_event_id, - (uint32_t)ae->when, - (uint32_t)ae->duration, - (uint32_t)ae->non_clear_duration, - (uint32_t)ae->exec_run_timestamp, - ae->name, - ae->chart, - ae->family, - ae->exec, - ae->recipient - ) < 0)) + health_log_rotate(); + + if(likely(health.log_fp)) { + if(unlikely(fprintf(health.log_fp + , "%c\t%s" + "\t%08x\t%08x\t%08x\t%08x\t%08x" + "\t%08x\t%08x\t%08x" + "\t%08x\t%08x\t%08x" + "\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s" + "\t%d\t%d\t%d\t%d" + "\t%Lf\t%Lf" + "\n" + , (ae->flags & HEALTH_ENTRY_FLAG_SAVED)?'U':'A' + , host->hostname + + , ae->unique_id + , ae->alarm_id + , ae->alarm_event_id + , ae->updated_by_id + , ae->updates_id + + , (uint32_t)ae->when + , (uint32_t)ae->duration + , (uint32_t)ae->non_clear_duration + , (uint32_t)ae->flags + , (uint32_t)ae->exec_run_timestamp + , (uint32_t)ae->delay_up_to_timestamp + + , (ae->name)?ae->name:"" + , (ae->chart)?ae->chart:"" + , (ae->family)?ae->family:"" + , (ae->exec)?ae->exec:"" + , (ae->recipient)?ae->recipient:"" + , (ae->source)?ae->source:"" + , (ae->units)?ae->units:"" + , (ae->info)?ae->info:"" + + , ae->exec_code + , ae->new_status + , ae->old_status + , ae->delay + + , (long double)ae->new_value + , (long double)ae->old_value + ) < 0)) error("Health: failed to save alarm log entry. Health data may be lost in case of abnormal restart."); + else { + ae->flags |= HEALTH_ENTRY_FLAG_SAVED; + health.log_entries_written++; + } } -*/ +} + +static inline ssize_t health_alarm_log_read(RRDHOST *host, FILE *fp, const char *filename) { + static uint32_t max_unique_id = 0, max_alarm_id = 0; + + errno = 0; + + char *s, *buf = mallocz(65536 + 1); + size_t line = 0, len = 0; + ssize_t loaded = 0, updated = 0, errored = 0, duplicate = 0; + + pthread_rwlock_rdlock(&host->health_log.alarm_log_rwlock); + + while((s = fgets_trim_len(buf, 65536, fp, &len))) { + health.log_entries_written++; + line++; + + int max_entries = 30, entries = 0; + char *pointers[max_entries]; + + pointers[entries++] = s++; + while(*s) { + if(unlikely(*s == '\t')) { + *s = '\0'; + pointers[entries++] = ++s; + if(entries >= max_entries) { + error("Health: line %zu of file '%s' has more than %d entries. Ignoring excessive entries.", line, filename, max_entries); + break; + } + } + else s++; + } + + if(likely(*pointers[0] == 'U' || *pointers[0] == 'A')) { + ALARM_ENTRY *ae = NULL; + + if(entries < 26) { + error("Health: line %zu of file '%s' should have at least 26 entries, but it has %d. Ignoring it.", line, filename, entries); + errored++; + continue; + } + + // check that we have valid ids + uint32_t unique_id = (uint32_t)strtoul(pointers[2], NULL, 16); + if(!unique_id) { + error("Health: line %zu of file '%s' states alarm entry with invalid unique id %u (%s). Ignoring it.", line, filename, unique_id, pointers[2]); + errored++; + continue; + } + + uint32_t alarm_id = (uint32_t)strtoul(pointers[3], NULL, 16); + if(!alarm_id) { + error("Health: line %zu of file '%s' states alarm entry for invalid alarm id %u (%s). Ignoring it.", line, filename, alarm_id, pointers[3]); + errored++; + continue; + } + + if(unlikely(*pointers[0] == 'A')) { + // make sure it is properly numbered + if(unlikely(host->health_log.alarms && unique_id < host->health_log.alarms->unique_id)) { + error("Health: line %zu of file '%s' has alarm log entry with %u in wrong order. Ignoring it.", line, filename, unique_id); + errored++; + continue; + } + + ae = callocz(1, sizeof(ALARM_ENTRY)); + } + else if(unlikely(*pointers[0] == 'U')) { + // find the original + for(ae = host->health_log.alarms; ae; ae = ae->next) { + if(unlikely(unique_id == ae->unique_id)) { + if(unlikely(*pointers[0] == 'A')) { + error("Health: line %zu of file '%s' adds duplicate alarm log entry with unique id %u. Using the later." + , line, filename, unique_id); + *pointers[0] = 'U'; + duplicate++; + } + break; + } + else if(unlikely(unique_id > ae->unique_id)) { + // no need to continue + // the linked list is sorted + ae = NULL; + break; + } + } + + // if not found, skip this line + if(!ae) { + // error("Health: line %zu of file '%s' updates alarm log entry with unique id %u, but it is not found.", line, filename, unique_id); + continue; + } + } + + // check for a possible host missmatch + //if(strcmp(pointers[1], host->hostname)) + // error("Health: line %zu of file '%s' provides an alarm for host '%s' but this is named '%s'.", line, filename, pointers[1], host->hostname); + + ae->unique_id = unique_id; + ae->alarm_id = alarm_id; + ae->alarm_event_id = (uint32_t)strtoul(pointers[4], NULL, 16); + ae->updated_by_id = (uint32_t)strtoul(pointers[5], NULL, 16); + ae->updates_id = (uint32_t)strtoul(pointers[6], NULL, 16); + + ae->when = (uint32_t)strtoul(pointers[7], NULL, 16); + ae->duration = (uint32_t)strtoul(pointers[8], NULL, 16); + ae->non_clear_duration = (uint32_t)strtoul(pointers[9], NULL, 16); + + ae->flags = (uint32_t)strtoul(pointers[10], NULL, 16); + ae->flags |= HEALTH_ENTRY_FLAG_SAVED; + + ae->exec_run_timestamp = (uint32_t)strtoul(pointers[11], NULL, 16); + ae->delay_up_to_timestamp = (uint32_t)strtoul(pointers[12], NULL, 16); + + if(unlikely(ae->name)) freez(ae->name); + ae->name = strdupz(pointers[13]); + ae->hash_name = simple_hash(ae->name); + + if(unlikely(ae->chart)) freez(ae->chart); + ae->chart = strdupz(pointers[14]); + ae->hash_chart = simple_hash(ae->chart); + + if(unlikely(ae->family)) freez(ae->family); + ae->family = strdupz(pointers[15]); + + if(unlikely(ae->exec)) freez(ae->exec); + ae->exec = strdupz(pointers[16]); + if(!*ae->exec) { freez(ae->exec); ae->exec = NULL; } + + if(unlikely(ae->recipient)) freez(ae->recipient); + ae->recipient = strdupz(pointers[17]); + if(!*ae->recipient) { freez(ae->recipient); ae->recipient = NULL; } + + if(unlikely(ae->source)) freez(ae->source); + ae->source = strdupz(pointers[18]); + if(!*ae->source) { freez(ae->source); ae->source = NULL; } + + if(unlikely(ae->units)) freez(ae->units); + ae->units = strdupz(pointers[19]); + if(!*ae->units) { freez(ae->units); ae->units = NULL; } + + if(unlikely(ae->info)) freez(ae->info); + ae->info = strdupz(pointers[20]); + if(!*ae->info) { freez(ae->info); ae->info = NULL; } + + ae->exec_code = str2i(pointers[21]); + ae->new_status = str2i(pointers[22]); + ae->old_status = str2i(pointers[23]); + ae->delay = str2i(pointers[24]); + + ae->new_value = str2l(pointers[25]); + ae->old_value = str2l(pointers[26]); + + // add it to host if not already there + if(unlikely(*pointers[0] == 'A')) { + ae->next = host->health_log.alarms; + host->health_log.alarms = ae; + loaded++; + } + else updated++; + + if(unlikely(ae->unique_id > max_unique_id)) + max_unique_id = ae->unique_id; + + if(unlikely(ae->alarm_id >= max_alarm_id)) + max_alarm_id = ae->alarm_id; + } + else { + error("Health: line %zu of file '%s' is invalid (unrecognized entry type '%s').", line, filename, pointers[0]); + errored++; + } + } + + pthread_rwlock_unlock(&host->health_log.alarm_log_rwlock); + + freez(buf); + + if(!max_unique_id) max_unique_id = (uint32_t)now_realtime_sec(); + if(!max_alarm_id) max_alarm_id = (uint32_t)now_realtime_sec(); + + host->health_log.next_log_id = max_unique_id + 1; + host->health_log.next_alarm_id = max_alarm_id + 1; + + debug(D_HEALTH, "Health: loaded file '%s' with %zd new alarm entries, updated %zd alarms, errors %zd entries, duplicate %zd", filename, loaded, updated, errored, duplicate); + return loaded; } static inline void health_alarm_log_load(RRDHOST *host) { - (void)host; + health_alarm_log_close(); + char filename[FILENAME_MAX + 1]; + snprintfz(filename, FILENAME_MAX, "%s.old", health.log_filename); + FILE *fp = fopen(filename, "r"); + if(!fp) + error("Health: cannot open health file: %s", filename); + else { + health_alarm_log_read(host, fp, filename); + fclose(fp); + } + + health.log_entries_written = 0; + fp = fopen(health.log_filename, "r"); + if(!fp) + error("Health: cannot open health file: %s", health.log_filename); + else { + health_alarm_log_read(host, fp, health.log_filename); + fclose(fp); + } + + health_alarm_log_open(); } + // ---------------------------------------------------------------------------- // health alarm log management @@ -152,8 +412,8 @@ static inline void health_alarm_log(RRDHOST *host, ALARM_ENTRY *t; for(t = host->health_log.alarms ; t ; t = t->next) { if(t != ae && t->alarm_id == ae->alarm_id) { - if(!(t->notifications & HEALTH_ENTRY_NOTIFICATIONS_UPDATED) && !t->updated_by_id) { - t->notifications |= HEALTH_ENTRY_NOTIFICATIONS_UPDATED; + if(!(t->flags & HEALTH_ENTRY_FLAG_UPDATED) && !t->updated_by_id) { + t->flags |= HEALTH_ENTRY_FLAG_UPDATED; t->updated_by_id = ae->unique_id; ae->updates_id = t->unique_id; @@ -163,10 +423,9 @@ static inline void health_alarm_log(RRDHOST *host, health_alarm_log_save(host, t); } - else { - // no need to continue - break; - } + + // no need to continue + break; } } pthread_rwlock_unlock(&host->health_log.alarm_log_rwlock); @@ -226,14 +485,17 @@ static inline void rrdvar_free(RRDHOST *host, avl_tree_lock *tree, RRDVAR *rv) { if(!rv) return; - if(tree) - rrdvar_index_del(tree, rv); + if(tree) { + debug(D_VARIABLES, "Deleting variable '%s'", rv->name); + if(unlikely(!rrdvar_index_del(tree, rv))) + error("Attempted to delete variable '%s' from host '%s', but it is not found.", rv->name, host->hostname); + } freez(rv->name); freez(rv); } -static inline RRDVAR *rrdvar_create_and_index(const char *scope, avl_tree_lock *tree, const char *name, int type, calculated_number *value) { +static inline RRDVAR *rrdvar_create_and_index(const char *scope, avl_tree_lock *tree, const char *name, int type, void *value) { char *variable = strdupz(name); rrdvar_fix_name(variable); uint32_t hash = simple_hash(variable); @@ -258,8 +520,13 @@ static inline RRDVAR *rrdvar_create_and_index(const char *scope, avl_tree_lock * debug(D_VARIABLES, "Variable '%s' created in scope '%s'", variable, scope); } else { + debug(D_VARIABLES, "Variable '%s' is already found in scope '%s'.", variable, scope); + // already exists freez(variable); + + // this is important + // it must return NULL - not the existing variable - or double-free will happen rv = NULL; } @@ -267,10 +534,69 @@ static inline RRDVAR *rrdvar_create_and_index(const char *scope, avl_tree_lock * } // ---------------------------------------------------------------------------- +// CUSTOM VARIABLES + +RRDVAR *rrdvar_custom_host_variable_create(RRDHOST *host, const char *name) { + calculated_number *v = callocz(1, sizeof(calculated_number)); + *v = NAN; + RRDVAR *rv = rrdvar_create_and_index("host", &host->variables_root_index, name, RRDVAR_TYPE_CALCULATED_ALLOCATED, v); + if(unlikely(!rv)) { + free(v); + error("Requested variable '%s' already exists - possibly 2 plugins will be updating it at the same time", name); + + char *variable = strdupz(name); + rrdvar_fix_name(variable); + uint32_t hash = simple_hash(variable); + + rv = rrdvar_index_find(&host->variables_root_index, variable, hash); + } + + return rv; +} + +void rrdvar_custom_host_variable_destroy(RRDHOST *host, const char *name) { + char *variable = strdupz(name); + rrdvar_fix_name(variable); + uint32_t hash = simple_hash(variable); + + RRDVAR *rv = rrdvar_index_find(&host->variables_root_index, variable, hash); + freez(variable); + + if(!rv) { + error("Attempted to remove variable '%s' from host '%s', but it does not exist.", name, host->hostname); + return; + } + + if(rv->type != RRDVAR_TYPE_CALCULATED_ALLOCATED) { + error("Attempted to remove variable '%s' from host '%s', but it does not a custom allocated variable.", name, host->hostname); + return; + } + + if(!rrdvar_index_del(&host->variables_root_index, rv)) { + error("Attempted to remove variable '%s' from host '%s', but it cannot be found.", name, host->hostname); + return; + } + + freez(rv->name); + freez(rv->value); + freez(rv); +} + +void rrdvar_custom_host_variable_set(RRDVAR *rv, calculated_number value) { + if(rv->type != RRDVAR_TYPE_CALCULATED_ALLOCATED) + error("requested to set variable '%s' to value " CALCULATED_NUMBER_FORMAT " but the variable is not a custom one.", rv->name, value); + else { + calculated_number *v = rv->value; + *v = value; + } +} + +// ---------------------------------------------------------------------------- // RRDVAR lookup -calculated_number rrdvar2number(RRDVAR *rv) { +static calculated_number rrdvar2number(RRDVAR *rv) { switch(rv->type) { + case RRDVAR_TYPE_CALCULATED_ALLOCATED: case RRDVAR_TYPE_CALCULATED: { calculated_number *n = (calculated_number *)rv->value; return *n; @@ -302,11 +628,6 @@ calculated_number rrdvar2number(RRDVAR *rv) { } } -void dump_variable(void *data) { - RRDVAR *rv = (RRDVAR *)data; - debug(D_HEALTH, "%50s : %20.5Lf", rv->name, rrdvar2number(rv)); -} - int health_variable_lookup(const char *variable, uint32_t hash, RRDCALC *rc, calculated_number *result) { RRDSET *st = rc->rrdset; RRDVAR *rv; @@ -331,162 +652,219 @@ int health_variable_lookup(const char *variable, uint32_t hash, RRDCALC *rc, cal return 1; } - debug(D_HEALTH, "Available local chart '%s' variables:", st->id); - avl_traverse_lock(&st->variables_root_index, dump_variable); + return 0; +} - debug(D_HEALTH, "Available family '%s' variables:", st->rrdfamily->family); - avl_traverse_lock(&st->rrdfamily->variables_root_index, dump_variable); +// ---------------------------------------------------------------------------- +// RRDVAR to JSON - debug(D_HEALTH, "Available host '%s' variables:", st->rrdhost->hostname); - avl_traverse_lock(&st->rrdhost->variables_root_index, dump_variable); +struct variable2json_helper { + BUFFER *buf; + size_t counter; +}; + +static int single_variable2json(void *entry, void *data) { + struct variable2json_helper *helper = (struct variable2json_helper *)data; + RRDVAR *rv = (RRDVAR *)entry; + calculated_number value = rrdvar2number(rv); + + if(unlikely(isnan(value) || isinf(value))) + buffer_sprintf(helper->buf, "%s\n\t\t\"%s\": null", helper->counter?",":"", rv->name); + else + buffer_sprintf(helper->buf, "%s\n\t\t\"%s\": %0.5Lf", helper->counter?",":"", rv->name, (long double)value); + + helper->counter++; return 0; } +void health_api_v1_chart_variables2json(RRDSET *st, BUFFER *buf) { + struct variable2json_helper helper = { + .buf = buf, + .counter = 0 + }; + + buffer_sprintf(buf, "{\n\t\"chart\": \"%s\",\n\t\"chart_name\": \"%s\",\n\t\"chart_context\": \"%s\",\n\t\"chart_variables\": {", st->id, st->name, st->context); + avl_traverse_lock(&st->variables_root_index, single_variable2json, (void *)&helper); + buffer_sprintf(buf, "\n\t},\n\t\"family\": \"%s\",\n\t\"family_variables\": {", st->family); + helper.counter = 0; + avl_traverse_lock(&st->rrdfamily->variables_root_index, single_variable2json, (void *)&helper); + buffer_sprintf(buf, "\n\t},\n\t\"host\": \"%s\",\n\t\"host_variables\": {", st->rrdhost->hostname); + helper.counter = 0; + avl_traverse_lock(&st->rrdhost->variables_root_index, single_variable2json, (void *)&helper); + buffer_strcat(buf, "\n\t}\n}\n"); +} + + // ---------------------------------------------------------------------------- -// RRDSETVAR management +// RRDDIMVAR management +// DIMENSION VARIABLES -RRDSETVAR *rrdsetvar_create(RRDSET *st, const char *variable, int type, void *value, uint32_t options) { - debug(D_VARIABLES, "RRDVARSET create for chart id '%s' name '%s' with variable name '%s'", st->id, st->name, variable); - RRDSETVAR *rs = (RRDSETVAR *)callocz(1, sizeof(RRDSETVAR)); +#define RRDDIMVAR_ID_MAX 1024 - char buffer[RRDVAR_MAX_LENGTH + 1]; - snprintfz(buffer, RRDVAR_MAX_LENGTH, "%s.%s", st->id, variable); - rs->fullid = strdupz(buffer); +static inline void rrddimvar_free_variables(RRDDIMVAR *rs) { + RRDDIM *rd = rs->rrddim; + RRDSET *st = rd->rrdset; - snprintfz(buffer, RRDVAR_MAX_LENGTH, "%s.%s", st->name, variable); - rs->fullname = strdupz(buffer); + // CHART VARIABLES FOR THIS DIMENSION - rs->variable = strdupz(variable); + rrdvar_free(st->rrdhost, &st->variables_root_index, rs->var_local_id); + rs->var_local_id = NULL; - rs->type = type; - rs->value = value; - rs->options = options; - rs->rrdset = st; + rrdvar_free(st->rrdhost, &st->variables_root_index, rs->var_local_name); + rs->var_local_name = NULL; - rs->local = rrdvar_create_and_index("local", &st->variables_root_index, rs->variable, rs->type, rs->value); - rs->family = rrdvar_create_and_index("family", &st->rrdfamily->variables_root_index, rs->fullid, rs->type, rs->value); - rs->host = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, rs->fullid, rs->type, rs->value); - rs->family_name = rrdvar_create_and_index("family", &st->rrdfamily->variables_root_index, rs->fullname, rs->type, rs->value); - rs->host_name = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, rs->fullname, rs->type, rs->value); + // FAMILY VARIABLES FOR THIS DIMENSION - rs->next = st->variables; - st->variables = rs; + rrdvar_free(st->rrdhost, &st->rrdfamily->variables_root_index, rs->var_family_id); + rs->var_family_id = NULL; - return rs; -} + rrdvar_free(st->rrdhost, &st->rrdfamily->variables_root_index, rs->var_family_name); + rs->var_family_name = NULL; -void rrdsetvar_rename_all(RRDSET *st) { - debug(D_VARIABLES, "RRDSETVAR rename for chart id '%s' name '%s'", st->id, st->name); + rrdvar_free(st->rrdhost, &st->rrdfamily->variables_root_index, rs->var_family_contextid); + rs->var_family_contextid = NULL; - // only these 2 can change name - // rs->family_name - // rs->host_name + rrdvar_free(st->rrdhost, &st->rrdfamily->variables_root_index, rs->var_family_contextname); + rs->var_family_contextname = NULL; - char buffer[RRDVAR_MAX_LENGTH + 1]; - RRDSETVAR *rs, *next = st->variables; - while((rs = next)) { - next = rs->next; + // HOST VARIABLES FOR THIS DIMENSION - snprintfz(buffer, RRDVAR_MAX_LENGTH, "%s.%s", st->name, rs->variable); + rrdvar_free(st->rrdhost, &st->rrdhost->variables_root_index, rs->var_host_chartidid); + rs->var_host_chartidid = NULL; - if (strcmp(buffer, rs->fullname)) { - // name changed - rrdvar_free(st->rrdhost, &st->rrdfamily->variables_root_index, rs->family_name); - rrdvar_free(st->rrdhost, &st->rrdhost->variables_root_index, rs->host_name); + rrdvar_free(st->rrdhost, &st->rrdhost->variables_root_index, rs->var_host_chartidname); + rs->var_host_chartidname = NULL; - freez(rs->fullname); - rs->fullname = strdupz(st->name); - rs->family_name = rrdvar_create_and_index("family", &st->rrdfamily->variables_root_index, rs->fullname, rs->type, rs->value); - rs->host_name = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, rs->fullname, rs->type, rs->value); - } - } + rrdvar_free(st->rrdhost, &st->rrdhost->variables_root_index, rs->var_host_chartnameid); + rs->var_host_chartnameid = NULL; - rrdsetcalc_link_matching(st); -} + rrdvar_free(st->rrdhost, &st->rrdhost->variables_root_index, rs->var_host_chartnamename); + rs->var_host_chartnamename = NULL; -void rrdsetvar_free(RRDSETVAR *rs) { - RRDSET *st = rs->rrdset; - debug(D_VARIABLES, "RRDSETVAR free for chart id '%s' name '%s', variable '%s'", st->id, st->name, rs->variable); + // KEYS - rrdvar_free(st->rrdhost, &st->variables_root_index, rs->local); - rrdvar_free(st->rrdhost, &st->rrdfamily->variables_root_index, rs->family); - rrdvar_free(st->rrdhost, &st->rrdhost->variables_root_index, rs->host); - rrdvar_free(st->rrdhost, &st->rrdfamily->variables_root_index, rs->family_name); - rrdvar_free(st->rrdhost, &st->rrdhost->variables_root_index, rs->host_name); + freez(rs->key_id); + rs->key_id = NULL; - if(st->variables == rs) { - st->variables = rs->next; - } - else { - RRDSETVAR *t; - for (t = st->variables; t && t->next != rs; t = t->next); - if(!t) error("RRDSETVAR '%s' not found in chart '%s' variables linked list", rs->fullname, st->id); - else t->next = rs->next; - } + freez(rs->key_name); + rs->key_name = NULL; - freez(rs->fullid); - freez(rs->fullname); - freez(rs->variable); - freez(rs); -} + freez(rs->key_fullidid); + rs->key_fullidid = NULL; -// ---------------------------------------------------------------------------- -// RRDDIMVAR management + freez(rs->key_fullidname); + rs->key_fullidname = NULL; -#define RRDDIMVAR_ID_MAX 1024 + freez(rs->key_contextid); + rs->key_contextid = NULL; -RRDDIMVAR *rrddimvar_create(RRDDIM *rd, int type, const char *prefix, const char *suffix, void *value, uint32_t options) { - RRDSET *st = rd->rrdset; + freez(rs->key_contextname); + rs->key_contextname = NULL; - debug(D_VARIABLES, "RRDDIMSET create for chart id '%s' name '%s', dimension id '%s', name '%s%s%s'", st->id, st->name, rd->id, (prefix)?prefix:"", rd->name, (suffix)?suffix:""); + freez(rs->key_fullnameid); + rs->key_fullnameid = NULL; - if(!prefix) prefix = ""; - if(!suffix) suffix = ""; + freez(rs->key_fullnamename); + rs->key_fullnamename = NULL; +} + +static inline void rrddimvar_create_variables(RRDDIMVAR *rs) { + rrddimvar_free_variables(rs); + + RRDDIM *rd = rs->rrddim; + RRDSET *st = rd->rrdset; char buffer[RRDDIMVAR_ID_MAX + 1]; - RRDDIMVAR *rs = (RRDDIMVAR *)callocz(1, sizeof(RRDDIMVAR)); - rs->prefix = strdupz(prefix); - rs->suffix = strdupz(suffix); + // KEYS snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s%s%s", rs->prefix, rd->id, rs->suffix); - rs->id = strdupz(buffer); + rs->key_id = strdupz(buffer); snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s%s%s", rs->prefix, rd->name, rs->suffix); - rs->name = strdupz(buffer); + rs->key_name = strdupz(buffer); + + snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", st->id, rs->key_id); + rs->key_fullidid = strdupz(buffer); + + snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", st->id, rs->key_name); + rs->key_fullidname = strdupz(buffer); + + snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", st->context, rs->key_id); + rs->key_contextid = strdupz(buffer); + + snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", st->context, rs->key_name); + rs->key_contextname = strdupz(buffer); + + snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", st->name, rs->key_id); + rs->key_fullnameid = strdupz(buffer); + + snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", st->name, rs->key_name); + rs->key_fullnamename = strdupz(buffer); + + // CHART VARIABLES FOR THIS DIMENSION + // ----------------------------------- + // + // dimensions are available as: + // - $id + // - $name + + rs->var_local_id = rrdvar_create_and_index("local", &st->variables_root_index, rs->key_id, rs->type, rs->value); + rs->var_local_name = rrdvar_create_and_index("local", &st->variables_root_index, rs->key_name, rs->type, rs->value); + + // FAMILY VARIABLES FOR THIS DIMENSION + // ----------------------------------- + // + // dimensions are available as: + // - $id (only the first, when multiple overlap) + // - $name (only the first, when multiple overlap) + // - $chart-context.id + // - $chart-context.name + + rs->var_family_id = rrdvar_create_and_index("family", &st->rrdfamily->variables_root_index, rs->key_id, rs->type, rs->value); + rs->var_family_name = rrdvar_create_and_index("family", &st->rrdfamily->variables_root_index, rs->key_name, rs->type, rs->value); + rs->var_family_contextid = rrdvar_create_and_index("family", &st->rrdfamily->variables_root_index, rs->key_contextid, rs->type, rs->value); + rs->var_family_contextname = rrdvar_create_and_index("family", &st->rrdfamily->variables_root_index, rs->key_contextname, rs->type, rs->value); + + // HOST VARIABLES FOR THIS DIMENSION + // ----------------------------------- + // + // dimensions are available as: + // - $chart-id.id + // - $chart-id.name + // - $chart-name.id + // - $chart-name.name + + rs->var_host_chartidid = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, rs->key_fullidid, rs->type, rs->value); + rs->var_host_chartidname = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, rs->key_fullidname, rs->type, rs->value); + rs->var_host_chartnameid = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, rs->key_fullnameid, rs->type, rs->value); + rs->var_host_chartnamename = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, rs->key_fullnamename, rs->type, rs->value); +} - snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", rd->rrdset->id, rs->id); - rs->fullidid = strdupz(buffer); +RRDDIMVAR *rrddimvar_create(RRDDIM *rd, int type, const char *prefix, const char *suffix, void *value, uint32_t options) { + RRDSET *st = rd->rrdset; - snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", rd->rrdset->id, rs->name); - rs->fullidname = strdupz(buffer); + debug(D_VARIABLES, "RRDDIMSET create for chart id '%s' name '%s', dimension id '%s', name '%s%s%s'", st->id, st->name, rd->id, (prefix)?prefix:"", rd->name, (suffix)?suffix:""); - snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", rd->rrdset->name, rs->id); - rs->fullnameid = strdupz(buffer); + if(!prefix) prefix = ""; + if(!suffix) suffix = ""; + + RRDDIMVAR *rs = (RRDDIMVAR *)callocz(1, sizeof(RRDDIMVAR)); - snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", rd->rrdset->name, rs->name); - rs->fullnamename = strdupz(buffer); + rs->prefix = strdupz(prefix); + rs->suffix = strdupz(suffix); rs->type = type; rs->value = value; rs->options = options; rs->rrddim = rd; - rs->local_id = rrdvar_create_and_index("local", &st->variables_root_index, rs->id, rs->type, rs->value); - rs->local_name = rrdvar_create_and_index("local", &st->variables_root_index, rs->name, rs->type, rs->value); - - rs->family_id = rrdvar_create_and_index("family", &st->rrdfamily->variables_root_index, rs->id, rs->type, rs->value); - rs->family_name = rrdvar_create_and_index("family", &st->rrdfamily->variables_root_index, rs->name, rs->type, rs->value); - - rs->host_fullidid = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, rs->fullidid, rs->type, rs->value); - rs->host_fullidname = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, rs->fullidname, rs->type, rs->value); - rs->host_fullnameid = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, rs->fullnameid, rs->type, rs->value); - rs->host_fullnamename = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, rs->fullnamename, rs->type, rs->value); - rs->next = rd->variables; rd->variables = rs; + rrddimvar_create_variables(rs); + return rs; } @@ -497,41 +875,7 @@ void rrddimvar_rename_all(RRDDIM *rd) { RRDDIMVAR *rs, *next = rd->variables; while((rs = next)) { next = rs->next; - - if (strcmp(rd->name, rs->name)) { - char buffer[RRDDIMVAR_ID_MAX + 1]; - // name changed - - // name - rrdvar_free(st->rrdhost, &st->variables_root_index, rs->local_name); - freez(rs->name); - snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s%s%s", rs->prefix, rd->name, rs->suffix); - rs->name = strdupz(buffer); - rs->local_name = rrdvar_create_and_index("local", &st->variables_root_index, rs->name, rs->type, rs->value); - - rrdvar_free(st->rrdhost, &st->rrdhost->variables_root_index, rs->host_fullidname); - freez(rs->fullidname); - snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", st->id, rs->name); - rs->fullidname = strdupz(buffer); - rs->host_fullidname = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, - rs->fullidname, rs->type, rs->value); - - // fullnameid - rrdvar_free(st->rrdhost, &st->rrdhost->variables_root_index, rs->host_fullnameid); - freez(rs->fullnameid); - snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", st->name, rs->id); - rs->fullnameid = strdupz(buffer); - rs->host_fullnameid = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, - rs->fullnameid, rs->type, rs->value); - - // fullnamename - rrdvar_free(st->rrdhost, &st->rrdhost->variables_root_index, rs->host_fullnamename); - freez(rs->fullnamename); - snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", st->name, rs->name); - rs->fullnamename = strdupz(buffer); - rs->host_fullnamename = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, - rs->fullnamename, rs->type, rs->value); - } + rrddimvar_create_variables(rs); } } @@ -540,16 +884,7 @@ void rrddimvar_free(RRDDIMVAR *rs) { RRDSET *st = rd->rrdset; debug(D_VARIABLES, "RRDDIMSET free for chart id '%s' name '%s', dimension id '%s', name '%s', prefix='%s', suffix='%s'", st->id, st->name, rd->id, rd->name, rs->prefix, rs->suffix); - rrdvar_free(st->rrdhost, &st->variables_root_index, rs->local_id); - rrdvar_free(st->rrdhost, &st->variables_root_index, rs->local_name); - - rrdvar_free(st->rrdhost, &st->rrdfamily->variables_root_index, rs->family_id); - rrdvar_free(st->rrdhost, &st->rrdfamily->variables_root_index, rs->family_name); - - rrdvar_free(st->rrdhost, &st->rrdhost->variables_root_index, rs->host_fullidid); - rrdvar_free(st->rrdhost, &st->rrdhost->variables_root_index, rs->host_fullidname); - rrdvar_free(st->rrdhost, &st->rrdhost->variables_root_index, rs->host_fullnameid); - rrdvar_free(st->rrdhost, &st->rrdhost->variables_root_index, rs->host_fullnamename); + rrddimvar_free_variables(rs); if(rd->variables == rs) { debug(D_VARIABLES, "RRDDIMSET removing first entry for chart id '%s' name '%s', dimension id '%s', name '%s'", st->id, st->name, rd->id, rd->name); @@ -559,25 +894,136 @@ void rrddimvar_free(RRDDIMVAR *rs) { debug(D_VARIABLES, "RRDDIMSET removing non-first entry for chart id '%s' name '%s', dimension id '%s', name '%s'", st->id, st->name, rd->id, rd->name); RRDDIMVAR *t; for (t = rd->variables; t && t->next != rs; t = t->next) ; - if(!t) error("RRDDIMVAR '%s' not found in dimension '%s/%s' variables linked list", rs->name, st->id, rd->id); + if(!t) error("RRDDIMVAR '%s' not found in dimension '%s/%s' variables linked list", rs->key_name, st->id, rd->id); else t->next = rs->next; } freez(rs->prefix); freez(rs->suffix); - freez(rs->id); - freez(rs->name); - freez(rs->fullidid); - freez(rs->fullidname); - freez(rs->fullnameid); - freez(rs->fullnamename); + freez(rs); +} + +// ---------------------------------------------------------------------------- +// RRDSETVAR management +// CHART VARIABLES + +static inline void rrdsetvar_free_variables(RRDSETVAR *rs) { + RRDSET *st = rs->rrdset; + + // CHART + + rrdvar_free(st->rrdhost, &st->variables_root_index, rs->var_local); + rs->var_local = NULL; + + // FAMILY + + rrdvar_free(st->rrdhost, &st->rrdfamily->variables_root_index, rs->var_family); + rs->var_family = NULL; + + rrdvar_free(st->rrdhost, &st->rrdhost->variables_root_index, rs->var_host); + rs->var_host = NULL; + + // HOST + + rrdvar_free(st->rrdhost, &st->rrdfamily->variables_root_index, rs->var_family_name); + rs->var_family_name = NULL; + + rrdvar_free(st->rrdhost, &st->rrdhost->variables_root_index, rs->var_host_name); + rs->var_host_name = NULL; + + // KEYS + + freez(rs->key_fullid); + rs->key_fullid = NULL; + + freez(rs->key_fullname); + rs->key_fullname = NULL; +} + +static inline void rrdsetvar_create_variables(RRDSETVAR *rs) { + rrdsetvar_free_variables(rs); + + RRDSET *st = rs->rrdset; + + // KEYS + + char buffer[RRDVAR_MAX_LENGTH + 1]; + snprintfz(buffer, RRDVAR_MAX_LENGTH, "%s.%s", st->id, rs->variable); + rs->key_fullid = strdupz(buffer); + + snprintfz(buffer, RRDVAR_MAX_LENGTH, "%s.%s", st->name, rs->variable); + rs->key_fullname = strdupz(buffer); + + // CHART + + rs->var_local = rrdvar_create_and_index("local", &st->variables_root_index, rs->variable, rs->type, rs->value); + + // FAMILY + + rs->var_family = rrdvar_create_and_index("family", &st->rrdfamily->variables_root_index, rs->key_fullid, rs->type, rs->value); + rs->var_family_name = rrdvar_create_and_index("family", &st->rrdfamily->variables_root_index, rs->key_fullname, rs->type, rs->value); + + // HOST + + rs->var_host = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, rs->key_fullid, rs->type, rs->value); + rs->var_host_name = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, rs->key_fullname, rs->type, rs->value); + +} + +RRDSETVAR *rrdsetvar_create(RRDSET *st, const char *variable, int type, void *value, uint32_t options) { + debug(D_VARIABLES, "RRDVARSET create for chart id '%s' name '%s' with variable name '%s'", st->id, st->name, variable); + RRDSETVAR *rs = (RRDSETVAR *)callocz(1, sizeof(RRDSETVAR)); + + rs->variable = strdupz(variable); + rs->type = type; + rs->value = value; + rs->options = options; + rs->rrdset = st; + + rs->next = st->variables; + st->variables = rs; + + rrdsetvar_create_variables(rs); + + return rs; +} + +void rrdsetvar_rename_all(RRDSET *st) { + debug(D_VARIABLES, "RRDSETVAR rename for chart id '%s' name '%s'", st->id, st->name); + + RRDSETVAR *rs, *next = st->variables; + while((rs = next)) { + next = rs->next; + rrdsetvar_create_variables(rs); + } + + rrdsetcalc_link_matching(st); +} + +void rrdsetvar_free(RRDSETVAR *rs) { + RRDSET *st = rs->rrdset; + debug(D_VARIABLES, "RRDSETVAR free for chart id '%s' name '%s', variable '%s'", st->id, st->name, rs->variable); + + if(st->variables == rs) { + st->variables = rs->next; + } + else { + RRDSETVAR *t; + for (t = st->variables; t && t->next != rs; t = t->next); + if(!t) error("RRDSETVAR '%s' not found in chart '%s' variables linked list", rs->key_fullname, st->id); + else t->next = rs->next; + } + + rrdsetvar_free_variables(rs); + + freez(rs->variable); freez(rs); } // ---------------------------------------------------------------------------- // RRDCALC management -static inline const char *rrdcalc_status2string(int status) { +inline const char *rrdcalc_status2string(int status) { switch(status) { case RRDCALC_STATUS_REMOVED: return "REMOVED"; @@ -609,7 +1055,7 @@ static inline const char *rrdcalc_status2string(int status) { static void rrdsetcalc_link(RRDSET *st, RRDCALC *rc) { debug(D_HEALTH, "Health linking alarm '%s.%s' to chart '%s' of host '%s'", rc->chart?rc->chart:"NOCHART", rc->name, st->id, st->rrdhost->hostname); - rc->last_status_change = time(NULL); + rc->last_status_change = now_realtime_sec(); rc->rrdset = st; rc->rrdset_next = st->alarms; @@ -648,7 +1094,7 @@ static void rrdsetcalc_link(RRDSET *st, RRDCALC *rc) { if(!rc->units) rc->units = strdupz(st->units); { - time_t now = time(NULL); + time_t now = now_realtime_sec(); health_alarm_log(st->rrdhost, rc->id, rc->next_event_id++, now, rc->name, rc->rrdset->id, rc->rrdset->family, rc->exec, rc->recipient, now - rc->last_status_change, rc->old_value, rc->value, rc->status, RRDCALC_STATUS_UNINITIALIZED, rc->source, rc->units, rc->info, 0); } } @@ -686,7 +1132,7 @@ inline void rrdsetcalc_unlink(RRDCALC *rc) { } { - time_t now = time(NULL); + time_t now = now_realtime_sec(); health_alarm_log(st->rrdhost, rc->id, rc->next_event_id++, now, rc->name, rc->rrdset->id, rc->rrdset->family, rc->exec, rc->recipient, now - rc->last_status_change, rc->old_value, rc->value, rc->status, RRDCALC_STATUS_REMOVED, rc->source, rc->units, rc->info, 0); } @@ -954,7 +1400,8 @@ void rrdcalctemplate_link_matching(RRDSET *st) { RRDCALCTEMPLATE *rt; for(rt = st->rrdhost->templates; rt ; rt = rt->next) { - if(rt->hash_context == st->hash_context && !strcmp(rt->context, st->context)) { + if(rt->hash_context == st->hash_context && !strcmp(rt->context, st->context) + && (!rt->family_pattern || simple_pattern_matches(rt->family_pattern, st->family))) { RRDCALC *rc = rrdcalc_create(st->rrdhost, rt, st->id); if(unlikely(!rc)) error("Health tried to create alarm from template '%s', but it failed", rt->name); @@ -990,6 +1437,9 @@ static inline void rrdcalctemplate_free(RRDHOST *host, RRDCALCTEMPLATE *rt) { expression_free(rt->warning); expression_free(rt->critical); + freez(rt->family_match); + simple_pattern_free(rt->family_pattern); + freez(rt->name); freez(rt->exec); freez(rt->recipient); @@ -1009,6 +1459,7 @@ static inline void rrdcalctemplate_free(RRDHOST *host, RRDCALCTEMPLATE *rt) { #define HEALTH_ALARM_KEY "alarm" #define HEALTH_TEMPLATE_KEY "template" #define HEALTH_ON_KEY "on" +#define HEALTH_FAMILIES_KEY "families" #define HEALTH_LOOKUP_KEY "lookup" #define HEALTH_CALC_KEY "calc" #define HEALTH_EVERY_KEY "every" @@ -1353,6 +1804,16 @@ static inline int health_parse_db_lookup( return 1; } +static inline char *tabs2spaces(char *s) { + char *t = s; + while(*t) { + if(unlikely(*t == '\t')) *t = ' '; + t++; + } + + return s; +} + static inline char *health_source_file(size_t line, const char *path, const char *filename) { char buffer[FILENAME_MAX + 1]; snprintfz(buffer, FILENAME_MAX, "%zu@%s/%s", line, path, filename); @@ -1369,13 +1830,14 @@ static inline void strip_quotes(char *s) { int health_readfile(const char *path, const char *filename) { debug(D_HEALTH, "Health configuration reading file '%s/%s'", path, filename); - static uint32_t hash_alarm = 0, hash_template = 0, hash_on = 0, hash_calc = 0, hash_green = 0, hash_red = 0, hash_warn = 0, hash_crit = 0, hash_exec = 0, hash_every = 0, hash_lookup = 0, hash_units = 0, hash_info = 0, hash_recipient = 0, hash_delay = 0; + static uint32_t hash_alarm = 0, hash_template = 0, hash_on = 0, hash_families = 0, hash_calc = 0, hash_green = 0, hash_red = 0, hash_warn = 0, hash_crit = 0, hash_exec = 0, hash_every = 0, hash_lookup = 0, hash_units = 0, hash_info = 0, hash_recipient = 0, hash_delay = 0; char buffer[HEALTH_CONF_MAX_LINE + 1]; if(unlikely(!hash_alarm)) { hash_alarm = simple_uhash(HEALTH_ALARM_KEY); hash_template = simple_uhash(HEALTH_TEMPLATE_KEY); hash_on = simple_uhash(HEALTH_ON_KEY); + hash_families = simple_uhash(HEALTH_FAMILIES_KEY); hash_calc = simple_uhash(HEALTH_CALC_KEY); hash_lookup = simple_uhash(HEALTH_LOOKUP_KEY); hash_green = simple_uhash(HEALTH_GREEN_KEY); @@ -1405,10 +1867,8 @@ int health_readfile(const char *path, const char *filename) { while((s = fgets(&buffer[append], (int)(HEALTH_CONF_MAX_LINE - append), fp)) || append) { int stop_appending = !s; line++; - // info("Line %zu of file '%s/%s': '%s'", line, path, filename, s); s = trim(buffer); if(!s) continue; - // info("Trimmed line %zu of file '%s/%s': '%s'", line, path, filename, s); append = strlen(s); if(!stop_appending && s[append - 1] == '\\') { @@ -1445,7 +1905,6 @@ int health_readfile(const char *path, const char *filename) { continue; } - // info("Health file '%s/%s', key '%s', value '%s'", path, filename, key, value); uint32_t hash = simple_uhash(key); if(hash == hash_alarm && !strcasecmp(key, HEALTH_ALARM_KEY)) { @@ -1460,7 +1919,7 @@ int health_readfile(const char *path, const char *filename) { rc = callocz(1, sizeof(RRDCALC)); rc->next_event_id = 1; - rc->name = strdupz(value); + rc->name = tabs2spaces(strdupz(value)); rc->hash = simple_hash(rc->name); rc->source = health_source_file(line, path, filename); rc->green = NAN; @@ -1483,7 +1942,7 @@ int health_readfile(const char *path, const char *filename) { rrdcalctemplate_free(&localhost, rt); rt = callocz(1, sizeof(RRDCALCTEMPLATE)); - rt->name = strdupz(value); + rt->name = tabs2spaces(strdupz(value)); rt->hash_name = simple_hash(rt->name); rt->source = health_source_file(line, path, filename); rt->green = NAN; @@ -1497,12 +1956,12 @@ int health_readfile(const char *path, const char *filename) { if(hash == hash_on && !strcasecmp(key, HEALTH_ON_KEY)) { if(rc->chart) { if(strcmp(rc->chart, value)) - info("Health configuration at line %zu of file '%s/%s' for alarm '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').", - line, path, filename, rc->name, key, rc->chart, value, value); + error("Health configuration at line %zu of file '%s/%s' for alarm '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').", + line, path, filename, rc->name, key, rc->chart, value, value); freez(rc->chart); } - rc->chart = strdupz(value); + rc->chart = tabs2spaces(strdupz(value)); rc->hash_chart = simple_hash(rc->chart); } else if(hash == hash_lookup && !strcasecmp(key, HEALTH_LOOKUP_KEY)) { @@ -1512,14 +1971,14 @@ int health_readfile(const char *path, const char *filename) { } else if(hash == hash_every && !strcasecmp(key, HEALTH_EVERY_KEY)) { if(!health_parse_duration(value, &rc->update_every)) - info("Health configuration at line %zu of file '%s/%s' for alarm '%s' at key '%s' cannot parse duration: '%s'.", + error("Health configuration at line %zu of file '%s/%s' for alarm '%s' at key '%s' cannot parse duration: '%s'.", line, path, filename, rc->name, key, value); } else if(hash == hash_green && !strcasecmp(key, HEALTH_GREEN_KEY)) { char *e; rc->green = strtold(value, &e); if(e && *e) { - info("Health configuration at line %zu of file '%s/%s' for alarm '%s' at key '%s' leaves this string unmatched: '%s'.", + error("Health configuration at line %zu of file '%s/%s' for alarm '%s' at key '%s' leaves this string unmatched: '%s'.", line, path, filename, rc->name, key, e); } } @@ -1527,7 +1986,7 @@ int health_readfile(const char *path, const char *filename) { char *e; rc->red = strtold(value, &e); if(e && *e) { - info("Health configuration at line %zu of file '%s/%s' for alarm '%s' at key '%s' leaves this string unmatched: '%s'.", + error("Health configuration at line %zu of file '%s/%s' for alarm '%s' at key '%s' leaves this string unmatched: '%s'.", line, path, filename, rc->name, key, e); } } @@ -1561,43 +2020,43 @@ int health_readfile(const char *path, const char *filename) { else if(hash == hash_exec && !strcasecmp(key, HEALTH_EXEC_KEY)) { if(rc->exec) { if(strcmp(rc->exec, value)) - info("Health configuration at line %zu of file '%s/%s' for alarm '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').", + error("Health configuration at line %zu of file '%s/%s' for alarm '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').", line, path, filename, rc->name, key, rc->exec, value, value); freez(rc->exec); } - rc->exec = strdupz(value); + rc->exec = tabs2spaces(strdupz(value)); } else if(hash == hash_recipient && !strcasecmp(key, HEALTH_RECIPIENT_KEY)) { if(rc->recipient) { if(strcmp(rc->recipient, value)) - info("Health configuration at line %zu of file '%s/%s' for alarm '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').", + error("Health configuration at line %zu of file '%s/%s' for alarm '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').", line, path, filename, rc->name, key, rc->recipient, value, value); freez(rc->recipient); } - rc->recipient = strdupz(value); + rc->recipient = tabs2spaces(strdupz(value)); } else if(hash == hash_units && !strcasecmp(key, HEALTH_UNITS_KEY)) { if(rc->units) { if(strcmp(rc->units, value)) - info("Health configuration at line %zu of file '%s/%s' for alarm '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').", + error("Health configuration at line %zu of file '%s/%s' for alarm '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').", line, path, filename, rc->name, key, rc->units, value, value); freez(rc->units); } - rc->units = strdupz(value); + rc->units = tabs2spaces(strdupz(value)); strip_quotes(rc->units); } else if(hash == hash_info && !strcasecmp(key, HEALTH_INFO_KEY)) { if(rc->info) { if(strcmp(rc->info, value)) - info("Health configuration at line %zu of file '%s/%s' for alarm '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').", + error("Health configuration at line %zu of file '%s/%s' for alarm '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').", line, path, filename, rc->name, key, rc->info, value, value); freez(rc->info); } - rc->info = strdupz(value); + rc->info = tabs2spaces(strdupz(value)); strip_quotes(rc->info); } else if(hash == hash_delay && !strcasecmp(key, HEALTH_DELAY_KEY)) { @@ -1612,29 +2071,35 @@ int health_readfile(const char *path, const char *filename) { if(hash == hash_on && !strcasecmp(key, HEALTH_ON_KEY)) { if(rt->context) { if(strcmp(rt->context, value)) - info("Health configuration at line %zu of file '%s/%s' for template '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').", - line, path, filename, rt->name, key, rt->context, value, value); + error("Health configuration at line %zu of file '%s/%s' for template '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').", + line, path, filename, rt->name, key, rt->context, value, value); freez(rt->context); } - rt->context = strdupz(value); + rt->context = tabs2spaces(strdupz(value)); rt->hash_context = simple_hash(rt->context); } + else if(hash == hash_families && !strcasecmp(key, HEALTH_FAMILIES_KEY)) { + freez(rt->family_match); + simple_pattern_free(rt->family_pattern); + + rt->family_match = tabs2spaces(strdupz(value)); + rt->family_pattern = simple_pattern_create(rt->family_match, SIMPLE_PATTERN_EXACT); + } else if(hash == hash_lookup && !strcasecmp(key, HEALTH_LOOKUP_KEY)) { health_parse_db_lookup(line, path, filename, value, &rt->group, &rt->after, &rt->before, - &rt->update_every, - &rt->options, &rt->dimensions); + &rt->update_every, &rt->options, &rt->dimensions); } else if(hash == hash_every && !strcasecmp(key, HEALTH_EVERY_KEY)) { if(!health_parse_duration(value, &rt->update_every)) - info("Health configuration at line %zu of file '%s/%s' for template '%s' at key '%s' cannot parse duration: '%s'.", + error("Health configuration at line %zu of file '%s/%s' for template '%s' at key '%s' cannot parse duration: '%s'.", line, path, filename, rt->name, key, value); } else if(hash == hash_green && !strcasecmp(key, HEALTH_GREEN_KEY)) { char *e; rt->green = strtold(value, &e); if(e && *e) { - info("Health configuration at line %zu of file '%s/%s' for template '%s' at key '%s' leaves this string unmatched: '%s'.", + error("Health configuration at line %zu of file '%s/%s' for template '%s' at key '%s' leaves this string unmatched: '%s'.", line, path, filename, rt->name, key, e); } } @@ -1642,7 +2107,7 @@ int health_readfile(const char *path, const char *filename) { char *e; rt->red = strtold(value, &e); if(e && *e) { - info("Health configuration at line %zu of file '%s/%s' for template '%s' at key '%s' leaves this string unmatched: '%s'.", + error("Health configuration at line %zu of file '%s/%s' for template '%s' at key '%s' leaves this string unmatched: '%s'.", line, path, filename, rt->name, key, e); } } @@ -1676,43 +2141,43 @@ int health_readfile(const char *path, const char *filename) { else if(hash == hash_exec && !strcasecmp(key, HEALTH_EXEC_KEY)) { if(rt->exec) { if(strcmp(rt->exec, value)) - info("Health configuration at line %zu of file '%s/%s' for template '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').", + error("Health configuration at line %zu of file '%s/%s' for template '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').", line, path, filename, rt->name, key, rt->exec, value, value); freez(rt->exec); } - rt->exec = strdupz(value); + rt->exec = tabs2spaces(strdupz(value)); } else if(hash == hash_recipient && !strcasecmp(key, HEALTH_RECIPIENT_KEY)) { if(rt->recipient) { if(strcmp(rt->recipient, value)) - info("Health configuration at line %zu of file '%s/%s' for template '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').", + error("Health configuration at line %zu of file '%s/%s' for template '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').", line, path, filename, rt->name, key, rt->recipient, value, value); freez(rt->recipient); } - rt->recipient = strdupz(value); + rt->recipient = tabs2spaces(strdupz(value)); } else if(hash == hash_units && !strcasecmp(key, HEALTH_UNITS_KEY)) { if(rt->units) { if(strcmp(rt->units, value)) - info("Health configuration at line %zu of file '%s/%s' for template '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').", + error("Health configuration at line %zu of file '%s/%s' for template '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').", line, path, filename, rt->name, key, rt->units, value, value); freez(rt->units); } - rt->units = strdupz(value); + rt->units = tabs2spaces(strdupz(value)); strip_quotes(rt->units); } else if(hash == hash_info && !strcasecmp(key, HEALTH_INFO_KEY)) { if(rt->info) { if(strcmp(rt->info, value)) - info("Health configuration at line %zu of file '%s/%s' for template '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').", + error("Health configuration at line %zu of file '%s/%s' for template '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').", line, path, filename, rt->name, key, rt->info, value, value); freez(rt->info); } - rt->info = strdupz(value); + rt->info = tabs2spaces(strdupz(value)); strip_quotes(rt->info); } else if(hash == hash_delay && !strcasecmp(key, HEALTH_DELAY_KEY)) { @@ -1798,7 +2263,16 @@ void health_init(void) { return; } + char *pathname = config_get("health", "health db directory", VARLIB_DIR "/health"); + if(mkdir(pathname, 0770) == -1 && errno != EEXIST) + fatal("Cannot create directory '%s'.", pathname); + + char filename[FILENAME_MAX + 1]; + snprintfz(filename, FILENAME_MAX, "%s/health-log.db", pathname); + health.log_filename = config_get("health", "health db file", filename); + health_alarm_log_load(&localhost); + health_alarm_log_open(); char *path = health_config_dir(); @@ -1865,10 +2339,10 @@ static inline void health_alarm_entry2json_nolock(BUFFER *wb, ALARM_ENTRY *ae, R ae->name, ae->chart, ae->family, - (ae->notifications & HEALTH_ENTRY_NOTIFICATIONS_PROCESSED)?"true":"false", - (ae->notifications & HEALTH_ENTRY_NOTIFICATIONS_UPDATED)?"true":"false", + (ae->flags & HEALTH_ENTRY_FLAG_PROCESSED)?"true":"false", + (ae->flags & HEALTH_ENTRY_FLAG_UPDATED)?"true":"false", (unsigned long)ae->exec_run_timestamp, - (ae->notifications & HEALTH_ENTRY_NOTIFICATIONS_EXEC_FAILED)?"true":"false", + (ae->flags & HEALTH_ENTRY_FLAG_EXEC_FAILED)?"true":"false", ae->exec?ae->exec:health.health_default_exec, ae->recipient?ae->recipient:health.health_default_recipient, ae->exec_code, @@ -2032,7 +2506,7 @@ void health_alarms2json(RRDHOST *host, BUFFER *wb, int all) { host->hostname, (host->health_log.next_log_id > 0)?(host->health_log.next_log_id - 1):0, health_enabled?"true":"false", - (unsigned long)time(NULL)); + (unsigned long)now_realtime_sec()); RRDCALC *rc; for(i = 0, rc = host->alarms; rc ; rc = rc->next) { @@ -2085,7 +2559,7 @@ void health_reload(void) { ALARM_ENTRY *t; for(t = localhost.health_log.alarms ; t ; t = t->next) { if(t->new_status != RRDCALC_STATUS_REMOVED) - t->notifications |= HEALTH_ENTRY_NOTIFICATIONS_UPDATED; + t->flags |= HEALTH_ENTRY_FLAG_UPDATED; } // reset all thresholds to all charts @@ -2115,34 +2589,52 @@ void health_reload(void) { // health main thread and friends static inline int rrdcalc_value2status(calculated_number n) { - if(isnan(n)) return RRDCALC_STATUS_UNDEFINED; + if(isnan(n) || isinf(n)) return RRDCALC_STATUS_UNDEFINED; if(n) return RRDCALC_STATUS_RAISED; return RRDCALC_STATUS_CLEAR; } -static inline void health_alarm_execute(RRDHOST *host, ALARM_ENTRY *ae) { - ae->notifications |= HEALTH_ENTRY_NOTIFICATIONS_PROCESSED; +#define ALARM_EXEC_COMMAND_LENGTH 8192 - // find the previous notification for the same alarm - ALARM_ENTRY *t; - for(t = ae->next; t ;t = t->next) { - if(t->alarm_id == ae->alarm_id && t->notifications & HEALTH_ENTRY_NOTIFICATIONS_EXEC_RUN) - break; - } +static inline void health_alarm_execute(RRDHOST *host, ALARM_ENTRY *ae) { + ae->flags |= HEALTH_ENTRY_FLAG_PROCESSED; - if(t && t->new_status == ae->new_status) { - // don't send the same notification again - info("Health not sending again notification for alarm '%s.%s' status %s", ae->chart, ae->name, rrdcalc_status2string(ae->new_status)); + if(unlikely(ae->new_status < RRDCALC_STATUS_CLEAR)) { + // do not send notifications for internal statuses goto done; } - if((ae->old_status == RRDCALC_STATUS_UNDEFINED && ae->new_status == RRDCALC_STATUS_UNINITIALIZED) - || (ae->old_status == RRDCALC_STATUS_UNINITIALIZED && ae->new_status == RRDCALC_STATUS_CLEAR)) { - info("Health not sending notification for first initialization of alarm '%s.%s' status %s", ae->chart, ae->name, rrdcalc_status2string(ae->new_status)); - goto done; + // find the previous notification for the same alarm + // which we have run the exec script + { + uint32_t id = ae->alarm_id; + ALARM_ENTRY *t; + for(t = ae->next; t ; t = t->next) { + if(t->alarm_id == id && t->flags & HEALTH_ENTRY_FLAG_EXEC_RUN) + break; + } + + if(likely(t)) { + // we have executed this alarm notification in the past + if(t && t->new_status == ae->new_status) { + // don't send the notification for the same status again + debug(D_HEALTH, "Health not sending again notification for alarm '%s.%s' status %s", ae->chart, ae->name + , rrdcalc_status2string(ae->new_status)); + goto done; + } + } + else { + // we have not executed this alarm notification in the past + // so, don't send CLEAR notifications + if(unlikely(ae->new_status == RRDCALC_STATUS_CLEAR)) { + debug(D_HEALTH, "Health not sending notification for first initialization of alarm '%s.%s' status %s" + , ae->chart, ae->name, rrdcalc_status2string(ae->new_status)); + goto done; + } + } } - char buffer[FILENAME_MAX + 1]; + static char command_to_run[ALARM_EXEC_COMMAND_LENGTH + 1]; pid_t command_pid; const char *exec = ae->exec; @@ -2151,7 +2643,7 @@ static inline void health_alarm_execute(RRDHOST *host, ALARM_ENTRY *ae) { const char *recipient = ae->recipient; if(!recipient) recipient = health.health_default_recipient; - snprintfz(buffer, FILENAME_MAX, "exec %s '%s' '%s' '%u' '%u' '%u' '%lu' '%s' '%s' '%s' '%s' '%s' '%0.0Lf' '%0.0Lf' '%s' '%u' '%u' '%s' '%s'", + snprintfz(command_to_run, ALARM_EXEC_COMMAND_LENGTH, "exec %s '%s' '%s' '%u' '%u' '%u' '%lu' '%s' '%s' '%s' '%s' '%s' '%0.0Lf' '%0.0Lf' '%s' '%u' '%u' '%s' '%s'", exec, recipient, host->hostname, @@ -2173,23 +2665,23 @@ static inline void health_alarm_execute(RRDHOST *host, ALARM_ENTRY *ae) { ae->info?ae->info:"" ); - ae->notifications |= HEALTH_ENTRY_NOTIFICATIONS_EXEC_RUN; - ae->exec_run_timestamp = time(NULL); + ae->flags |= HEALTH_ENTRY_FLAG_EXEC_RUN; + ae->exec_run_timestamp = now_realtime_sec(); - debug(D_HEALTH, "executing command '%s'", buffer); - FILE *fp = mypopen(buffer, &command_pid); + debug(D_HEALTH, "executing command '%s'", command_to_run); + FILE *fp = mypopen(command_to_run, &command_pid); if(!fp) { - error("HEALTH: Cannot popen(\"%s\", \"r\").", buffer); + error("HEALTH: Cannot popen(\"%s\", \"r\").", command_to_run); goto done; } debug(D_HEALTH, "HEALTH reading from command"); - char *s = fgets(buffer, FILENAME_MAX, fp); + char *s = fgets(command_to_run, FILENAME_MAX, fp); (void)s; ae->exec_code = mypclose(fp, command_pid); debug(D_HEALTH, "done executing command - returned with code %d", ae->exec_code); if(ae->exec_code != 0) - ae->notifications |= HEALTH_ENTRY_NOTIFICATIONS_EXEC_FAILED; + ae->flags |= HEALTH_ENTRY_FLAG_EXEC_FAILED; done: health_alarm_log_save(host, ae); @@ -2197,7 +2689,7 @@ done: } static inline void health_process_notifications(RRDHOST *host, ALARM_ENTRY *ae) { - info("Health alarm '%s.%s' = %0.2Lf - changed status from %s to %s", + debug(D_HEALTH, "Health alarm '%s.%s' = %0.2Lf - changed status from %s to %s", ae->chart?ae->chart:"NOCHART", ae->name, ae->new_value, rrdcalc_status2string(ae->old_status), @@ -2210,15 +2702,15 @@ static inline void health_process_notifications(RRDHOST *host, ALARM_ENTRY *ae) static inline void health_alarm_log_process(RRDHOST *host) { static uint32_t stop_at_id = 0; uint32_t first_waiting = (host->health_log.alarms)?host->health_log.alarms->unique_id:0; - time_t now = time(NULL); + time_t now = now_realtime_sec(); pthread_rwlock_rdlock(&host->health_log.alarm_log_rwlock); ALARM_ENTRY *ae; for(ae = host->health_log.alarms; ae && ae->unique_id >= stop_at_id ; ae = ae->next) { if(unlikely( - !(ae->notifications & HEALTH_ENTRY_NOTIFICATIONS_PROCESSED) && - !(ae->notifications & HEALTH_ENTRY_NOTIFICATIONS_UPDATED) + !(ae->flags & HEALTH_ENTRY_FLAG_PROCESSED) && + !(ae->flags & HEALTH_ENTRY_FLAG_UPDATED) )) { if(unlikely(ae->unique_id < first_waiting)) @@ -2272,40 +2764,61 @@ static inline void health_alarm_log_process(RRDHOST *host) { } static inline int rrdcalc_isrunnable(RRDCALC *rc, time_t now, time_t *next_run) { - if (unlikely(!rc->rrdset)) { + if(unlikely(!rc->rrdset)) { debug(D_HEALTH, "Health not running alarm '%s.%s'. It is not linked to a chart.", rc->chart?rc->chart:"NOCHART", rc->name); return 0; } - if (unlikely(!rc->rrdset->last_collected_time.tv_sec)) { - debug(D_HEALTH, "Health not running alarm '%s.%s'. Chart is not yet collected.", rc->chart?rc->chart:"NOCHART", rc->name); + if(unlikely(rc->next_update > now)) { + if (unlikely(*next_run > rc->next_update)) { + // update the next_run time of the main loop + // to run this alarm precisely the time required + *next_run = rc->next_update; + } + + debug(D_HEALTH, "Health not examining alarm '%s.%s' yet (will do in %d secs).", rc->chart?rc->chart:"NOCHART", rc->name, (int) (rc->next_update - now)); return 0; } - if (unlikely(!rc->update_every)) { + if(unlikely(!rc->update_every)) { debug(D_HEALTH, "Health not running alarm '%s.%s'. It does not have an update frequency", rc->chart?rc->chart:"NOCHART", rc->name); return 0; } - if (unlikely(rc->next_update > now)) { - if (unlikely(*next_run > rc->next_update)) - *next_run = rc->next_update; + if(unlikely(!rc->rrdset->last_collected_time.tv_sec || rc->rrdset->counter_done < 2)) { + debug(D_HEALTH, "Health not running alarm '%s.%s'. Chart is not fully collected yet.", rc->chart?rc->chart:"NOCHART", rc->name); + return 0; + } - debug(D_HEALTH, "Health not examining alarm '%s.%s' yet (will do in %d secs).", rc->chart?rc->chart:"NOCHART", rc->name, (int) (rc->next_update - now)); + int update_every = rc->rrdset->update_every; + time_t first = rrdset_first_entry_t(rc->rrdset); + time_t last = rrdset_last_entry_t(rc->rrdset); + + if(unlikely(now + update_every < first /* || now - update_every > last */)) { + debug(D_HEALTH + , "Health not examining alarm '%s.%s' yet (wanted time is out of bounds - we need %lu but got %lu - %lu)." + , rc->chart ? rc->chart : "NOCHART", rc->name, (unsigned long) now, (unsigned long) first + , (unsigned long) last); return 0; } - // FIXME - // we should check that the DB lookup is possible - // i.e. - // - the duration of the chart includes the required timeframe - // we SHOULD NOT check the dimensions - there might be alarms that refer non-existing dimensions (e.g. cpu steal) + if(RRDCALC_HAS_DB_LOOKUP(rc)) { + time_t needed = now + rc->before + rc->after; + + if(needed + update_every < first || needed - update_every > last) { + debug(D_HEALTH + , "Health not examining alarm '%s.%s' yet (not enough data yet - we need %lu but got %lu - %lu)." + , rc->chart ? rc->chart : "NOCHART", rc->name, (unsigned long) needed, (unsigned long) first + , (unsigned long) last); + return 0; + } + } return 1; } void *health_main(void *ptr) { - (void)ptr; + struct netdata_static_thread *static_thread = (struct netdata_static_thread *)ptr; info("HEALTH thread created with task id %d", gettid()); @@ -2326,28 +2839,32 @@ void *health_main(void *ptr) { debug(D_HEALTH, "Health monitoring iteration no %u started", loop); int oldstate, runnable = 0; - time_t now = time(NULL); + time_t now = now_realtime_sec(); time_t next_run = now + min_run_every; RRDCALC *rc; - if (unlikely(pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldstate) != 0)) + if(unlikely(pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldstate) != 0)) error("Cannot set pthread cancel state to DISABLE."); rrdhost_rdlock(&localhost); // the first loop is to lookup values from the db - for (rc = localhost.alarms; rc; rc = rc->next) { - if (unlikely(!rrdcalc_isrunnable(rc, now, &next_run))) + for(rc = localhost.alarms; rc; rc = rc->next) { + if(unlikely(!rrdcalc_isrunnable(rc, now, &next_run))) { + if(unlikely(rc->rrdcalc_flags & RRDCALC_FLAG_RUNNABLE)) + rc->rrdcalc_flags &= ~RRDCALC_FLAG_RUNNABLE; continue; + } runnable++; rc->old_value = rc->value; + rc->rrdcalc_flags |= RRDCALC_FLAG_RUNNABLE; // 1. if there is database lookup, do it // 2. if there is calculation expression, run it if (unlikely(RRDCALC_HAS_DB_LOOKUP(rc))) { - time_t old_db_timestamp = rc->db_before; + /* time_t old_db_timestamp = rc->db_before; */ int value_is_null = 0; int ret = rrd2value(rc->rrdset, wb, &rc->value, @@ -2368,6 +2885,7 @@ void *health_main(void *ptr) { else if (unlikely(rc->rrdcalc_flags & RRDCALC_FLAG_DB_ERROR)) rc->rrdcalc_flags &= ~RRDCALC_FLAG_DB_ERROR; + /* - RRDCALC_FLAG_DB_STALE not currently used if (unlikely(old_db_timestamp == rc->db_before)) { // database is stale @@ -2380,6 +2898,7 @@ void *health_main(void *ptr) { } else if (unlikely(rc->rrdcalc_flags & RRDCALC_FLAG_DB_STALE)) rc->rrdcalc_flags &= ~RRDCALC_FLAG_DB_STALE; + */ if (unlikely(value_is_null)) { // collected value is null @@ -2408,23 +2927,24 @@ void *health_main(void *ptr) { rc->value = NAN; - debug(D_HEALTH, "Health alarm '%s.%s': failed to evaluate calculation with error: %s", - rc->chart?rc->chart:"NOCHART", rc->name, buffer_tostring(rc->calculation->error_msg)); + debug(D_HEALTH, "Health alarm '%s.%s': expression '%s' failed: %s", + rc->chart?rc->chart:"NOCHART", rc->name, rc->calculation->parsed_as, buffer_tostring(rc->calculation->error_msg)); if (unlikely(!(rc->rrdcalc_flags & RRDCALC_FLAG_CALC_ERROR))) { rc->rrdcalc_flags |= RRDCALC_FLAG_CALC_ERROR; - error("Health alarm '%s.%s': failed to evaluate calculation with error: %s", - rc->chart?rc->chart:"NOCHART", rc->name, buffer_tostring(rc->calculation->error_msg)); + error("Health alarm '%s.%s': expression '%s' failed: %s", + rc->chart?rc->chart:"NOCHART", rc->name, rc->calculation->parsed_as, buffer_tostring(rc->calculation->error_msg)); } } else { if (unlikely(rc->rrdcalc_flags & RRDCALC_FLAG_CALC_ERROR)) rc->rrdcalc_flags &= ~RRDCALC_FLAG_CALC_ERROR; - debug(D_HEALTH, "Health alarm '%s.%s': calculation expression gave value " + debug(D_HEALTH, "Health alarm '%s.%s': expression '%s' gave value " CALCULATED_NUMBER_FORMAT ": %s (source: %s)", rc->chart?rc->chart:"NOCHART", rc->name, + rc->calculation->parsed_as, rc->calculation->result, buffer_tostring(rc->calculation->error_msg), rc->source @@ -2436,11 +2956,11 @@ void *health_main(void *ptr) { } rrdhost_unlock(&localhost); - if (unlikely(runnable && !netdata_exit)) { + if(unlikely(runnable && !netdata_exit)) { rrdhost_rdlock(&localhost); - for (rc = localhost.alarms; rc; rc = rc->next) { - if (unlikely(!rrdcalc_isrunnable(rc, now, &next_run))) + for(rc = localhost.alarms; rc; rc = rc->next) { + if(unlikely(!(rc->rrdcalc_flags & RRDCALC_FLAG_RUNNABLE))) continue; int warning_status = RRDCALC_STATUS_UNDEFINED; @@ -2591,11 +3111,11 @@ void *health_main(void *ptr) { if(unlikely(netdata_exit)) break; - now = time(NULL); + now = now_realtime_sec(); if(now < next_run) { debug(D_HEALTH, "Health monitoring iteration no %u done. Next iteration in %d secs", loop, (int) (next_run - now)); - sleep_usec(1000000 * (unsigned long long) (next_run - now)); + sleep_usec(USEC_PER_SEC * (usec_t) (next_run - now)); } else { debug(D_HEALTH, "Health monitoring iteration no %u done. Next iteration now", loop); @@ -2605,6 +3125,8 @@ void *health_main(void *ptr) { buffer_free(wb); info("HEALTH thread exiting"); + + static_thread->enabled = 0; pthread_exit(NULL); return NULL; } diff --git a/src/health.h b/src/health.h index 9d5834fca..79831d4fc 100644 --- a/src/health.h +++ b/src/health.h @@ -5,11 +5,13 @@ extern int health_enabled; extern int rrdvar_compare(void *a, void *b); -#define RRDVAR_TYPE_CALCULATED 1 -#define RRDVAR_TYPE_TIME_T 2 -#define RRDVAR_TYPE_COLLECTED 3 -#define RRDVAR_TYPE_TOTAL 4 -#define RRDVAR_TYPE_INT 5 +#define RRDVAR_TYPE_CALCULATED 1 +#define RRDVAR_TYPE_TIME_T 2 +#define RRDVAR_TYPE_COLLECTED 3 +#define RRDVAR_TYPE_TOTAL 4 +#define RRDVAR_TYPE_INT 5 +#define RRDVAR_TYPE_CALCULATED_ALLOCATED 6 + // the variables as stored in the variables indexes // there are 3 indexes: @@ -34,8 +36,8 @@ typedef struct rrdvar { // This means, there will be no speed penalty for using // these variables typedef struct rrdsetvar { - char *fullid; // chart type.chart id.variable - char *fullname; // chart type.chart name.variable + char *key_fullid; // chart type.chart id.variable + char *key_fullname; // chart type.chart name.variable char *variable; // variable int type; @@ -43,11 +45,11 @@ typedef struct rrdsetvar { uint32_t options; - RRDVAR *local; - RRDVAR *family; - RRDVAR *host; - RRDVAR *family_name; - RRDVAR *host_name; + RRDVAR *var_local; + RRDVAR *var_family; + RRDVAR *var_host; + RRDVAR *var_family_name; + RRDVAR *var_host_name; struct rrdset *rrdset; @@ -64,28 +66,32 @@ typedef struct rrddimvar { char *prefix; char *suffix; - char *id; // dimension id - char *name; // dimension name - char *fullidid; // chart type.chart id.dimension id - char *fullidname; // chart type.chart id.dimension name - char *fullnameid; // chart type.chart name.dimension id - char *fullnamename; // chart type.chart name.dimension name + char *key_id; // dimension id + char *key_name; // dimension name + char *key_contextid; // context + dimension id + char *key_contextname; // context + dimension name + char *key_fullidid; // chart type.chart id + dimension id + char *key_fullidname; // chart type.chart id + dimension name + char *key_fullnameid; // chart type.chart name + dimension id + char *key_fullnamename; // chart type.chart name + dimension name int type; void *value; uint32_t options; - RRDVAR *local_id; - RRDVAR *local_name; + RRDVAR *var_local_id; + RRDVAR *var_local_name; - RRDVAR *family_id; - RRDVAR *family_name; + RRDVAR *var_family_id; + RRDVAR *var_family_name; + RRDVAR *var_family_contextid; + RRDVAR *var_family_contextname; - RRDVAR *host_fullidid; - RRDVAR *host_fullidname; - RRDVAR *host_fullnameid; - RRDVAR *host_fullnamename; + RRDVAR *var_host_chartidid; + RRDVAR *var_host_chartidname; + RRDVAR *var_host_chartnameid; + RRDVAR *var_host_chartnamename; struct rrddim *rrddim; @@ -115,10 +121,11 @@ typedef struct rrddimvar { #define RRDCALC_FLAG_DB_ERROR 0x00000001 #define RRDCALC_FLAG_DB_NAN 0x00000002 -#define RRDCALC_FLAG_DB_STALE 0x00000004 +/* #define RRDCALC_FLAG_DB_STALE 0x00000004 */ #define RRDCALC_FLAG_CALC_ERROR 0x00000008 #define RRDCALC_FLAG_WARN_ERROR 0x00000010 #define RRDCALC_FLAG_CRIT_ERROR 0x00000020 +#define RRDCALC_FLAG_RUNNABLE 0x00000040 typedef struct rrdcalc { uint32_t id; // the unique id of this alarm @@ -225,6 +232,9 @@ typedef struct rrdcalctemplate { char *context; uint32_t hash_context; + char *family_match; + SIMPLE_PATTERN *family_pattern; + char *source; // the source of this alarm char *units; // the units of the alarm char *info; // a short description of the alarm @@ -264,10 +274,11 @@ typedef struct rrdcalctemplate { #define RRDCALCTEMPLATE_HAS_CALCULATION(rt) ((rt)->after) -#define HEALTH_ENTRY_NOTIFICATIONS_PROCESSED 0x00000001 -#define HEALTH_ENTRY_NOTIFICATIONS_UPDATED 0x00000002 -#define HEALTH_ENTRY_NOTIFICATIONS_EXEC_RUN 0x00000004 -#define HEALTH_ENTRY_NOTIFICATIONS_EXEC_FAILED 0x00000008 +#define HEALTH_ENTRY_FLAG_PROCESSED 0x00000001 +#define HEALTH_ENTRY_FLAG_UPDATED 0x00000002 +#define HEALTH_ENTRY_FLAG_EXEC_RUN 0x00000004 +#define HEALTH_ENTRY_FLAG_EXEC_FAILED 0x00000008 +#define HEALTH_ENTRY_FLAG_SAVED 0x10000000 typedef struct alarm_entry { uint32_t unique_id; @@ -300,7 +311,7 @@ typedef struct alarm_entry { int old_status; int new_status; - uint32_t notifications; + uint32_t flags; int delay; time_t delay_up_to_timestamp; @@ -344,4 +355,12 @@ extern int health_variable_lookup(const char *variable, uint32_t hash, RRDCALC * extern void health_alarms2json(RRDHOST *host, BUFFER *wb, int all); extern void health_alarm_log2json(RRDHOST *host, BUFFER *wb, uint32_t after); +void health_api_v1_chart_variables2json(RRDSET *st, BUFFER *buf); + +extern RRDVAR *rrdvar_custom_host_variable_create(RRDHOST *host, const char *name); +extern void rrdvar_custom_host_variable_destroy(RRDHOST *host, const char *name); +extern void rrdvar_custom_host_variable_set(RRDVAR *rv, calculated_number value); + +extern const char *rrdcalc_status2string(int status); + #endif //NETDATA_HEALTH_H diff --git a/src/inlined.h b/src/inlined.h new file mode 100644 index 000000000..e776f830e --- /dev/null +++ b/src/inlined.h @@ -0,0 +1,127 @@ +#ifndef NETDATA_INLINED_H +#define NETDATA_INLINED_H + +#include "common.h" + +// for faster execution, allow the compiler to inline +// these functions that are called thousands of times per second + +static inline uint32_t simple_hash(const char *name) { + unsigned char *s = (unsigned char *) name; + uint32_t hval = 0x811c9dc5; + while (*s) { + hval *= 16777619; + hval ^= (uint32_t) *s++; + } + return hval; +} + +static inline uint32_t simple_uhash(const char *name) { + unsigned char *s = (unsigned char *) name; + uint32_t hval = 0x811c9dc5, c; + while ((c = *s++)) { + if (unlikely(c >= 'A' && c <= 'Z')) c += 'a' - 'A'; + hval *= 16777619; + hval ^= c; + } + return hval; +} + +static inline int simple_hash_strcmp(const char *name, const char *b, uint32_t *hash) { + unsigned char *s = (unsigned char *) name; + uint32_t hval = 0x811c9dc5; + int ret = 0; + while (*s) { + if(!ret) ret = *s - *b++; + hval *= 16777619; + hval ^= (uint32_t) *s++; + } + *hash = hval; + return ret; +} + +static inline int str2i(const char *s) { + int n = 0; + char c, negative = (*s == '-'); + + for(c = (negative)?*(++s):*s; c >= '0' && c <= '9' ; c = *(++s)) { + n *= 10; + n += c - '0'; + } + + if(unlikely(negative)) + return -n; + + return n; +} + +static inline long str2l(const char *s) { + long n = 0; + char c, negative = (*s == '-'); + + for(c = (negative)?*(++s):*s; c >= '0' && c <= '9' ; c = *(++s)) { + n *= 10; + n += c - '0'; + } + + if(unlikely(negative)) + return -n; + + return n; +} + +static inline unsigned long str2ul(const char *s) { + unsigned long n = 0; + char c; + for(c = *s; c >= '0' && c <= '9' ; c = *(++s)) { + n *= 10; + n += c - '0'; + } + return n; +} + +static inline unsigned long long str2ull(const char *s) { + unsigned long long n = 0; + char c; + for(c = *s; c >= '0' && c <= '9' ; c = *(++s)) { + n *= 10; + n += c - '0'; + } + return n; +} + +#ifdef NETDATA_STRCMP_OVERRIDE +#ifdef strcmp +#undef strcmp +#endif +#define strcmp(a, b) strsame(a, b) +static inline int strsame(const char *a, const char *b) { + if(unlikely(a == b)) return 0; + while(*a && *a == *b) { a++; b++; } + return *a - *b; +} +#endif // NETDATA_STRSAME + +static inline int read_single_number_file(const char *filename, unsigned long long *result) { + char buffer[30 + 1]; + + int fd = open(filename, O_RDONLY, 0666); + if(unlikely(fd == -1)) { + *result = 0; + return 1; + } + + ssize_t r = read(fd, buffer, 30); + if(unlikely(r == -1)) { + *result = 0; + close(fd); + return 2; + } + + close(fd); + buffer[30] = '\0'; + *result = str2ull(buffer); + return 0; +} + +#endif //NETDATA_INLINED_H diff --git a/src/ipc.c b/src/ipc.c new file mode 100644 index 000000000..a5ab342d3 --- /dev/null +++ b/src/ipc.c @@ -0,0 +1,238 @@ +#include "common.h" + +#include <sys/sem.h> +#include <sys/msg.h> +#include <sys/shm.h> + + +#ifndef SEMVMX +#define SEMVMX 32767 /* <= 32767 semaphore maximum value */ +#endif + +/* Some versions of libc only define IPC_INFO when __USE_GNU is defined. */ +#ifndef IPC_INFO +#define IPC_INFO 3 +#endif + +struct ipc_limits { + uint64_t shmmni; /* max number of segments */ + uint64_t shmmax; /* max segment size */ + uint64_t shmall; /* max total shared memory */ + uint64_t shmmin; /* min segment size */ + + int semmni; /* max number of arrays */ + int semmsl; /* max semaphores per array */ + int semmns; /* max semaphores system wide */ + int semopm; /* max ops per semop call */ + unsigned int semvmx; /* semaphore max value (constant) */ + + int msgmni; /* max queues system wide */ + size_t msgmax; /* max size of message */ + int msgmnb; /* default max size of queue */ +}; + +struct ipc_status { + int semusz; /* current number of arrays */ + int semaem; /* current semaphores system wide */ +}; + +/* + * The last arg of semctl is a union semun, but where is it defined? X/OPEN + * tells us to define it ourselves, but until recently Linux include files + * would also define it. + */ +#ifndef HAVE_UNION_SEMUN +/* according to X/OPEN we have to define it ourselves */ +union semun { + int val; + struct semid_ds *buf; + unsigned short int *array; + struct seminfo *__buf; +}; +#endif + +static inline int ipc_sem_get_limits(struct ipc_limits *lim) { + static procfile *ff = NULL; + static int error_shown = 0; + static char filename[FILENAME_MAX + 1] = ""; + + if(unlikely(!filename[0])) + snprintfz(filename, FILENAME_MAX, "%s/proc/sys/kernel/sem", global_host_prefix); + + if(unlikely(!ff)) { + ff = procfile_open(filename, NULL, PROCFILE_FLAG_DEFAULT); + if(unlikely(!ff)) { + if(unlikely(!error_shown)) { + error("IPC: Cannot open file '%s'.", filename); + error_shown = 1; + } + goto ipc; + } + } + + ff = procfile_readall(ff); + if(unlikely(!ff)) { + if(unlikely(!error_shown)) { + error("IPC: Cannot read file '%s'.", filename); + error_shown = 1; + } + goto ipc; + } + + if(procfile_lines(ff) >= 1 && procfile_linewords(ff, 0) >= 4) { + lim->semvmx = SEMVMX; + lim->semmsl = str2i(procfile_lineword(ff, 0, 0)); + lim->semmns = str2i(procfile_lineword(ff, 0, 1)); + lim->semopm = str2i(procfile_lineword(ff, 0, 2)); + lim->semmni = str2i(procfile_lineword(ff, 0, 3)); + return 0; + } + else { + if(unlikely(!error_shown)) { + error("IPC: Invalid content in file '%s'.", filename); + error_shown = 1; + } + goto ipc; + } + +ipc: + // cannot do it from the file + // query IPC + { + struct seminfo seminfo = {.semmni = 0}; + union semun arg = {.array = (ushort *) &seminfo}; + + if(unlikely(semctl(0, 0, IPC_INFO, arg) < 0)) { + error("IPC: Failed to read '%s' and request IPC_INFO with semctl().", filename); + goto error; + } + + lim->semvmx = SEMVMX; + lim->semmni = seminfo.semmni; + lim->semmsl = seminfo.semmsl; + lim->semmns = seminfo.semmns; + lim->semopm = seminfo.semopm; + return 0; + } + +error: + lim->semvmx = 0; + lim->semmni = 0; + lim->semmsl = 0; + lim->semmns = 0; + lim->semopm = 0; + return -1; +} + +/* +printf ("------ Semaphore Limits --------\n"); +printf ("max number of arrays = %d\n", limits.semmni); +printf ("max semaphores per array = %d\n", limits.semmsl); +printf ("max semaphores system wide = %d\n", limits.semmns); +printf ("max ops per semop call = %d\n", limits.semopm); +printf ("semaphore max value = %u\n", limits.semvmx); + +printf ("------ Semaphore Status --------\n"); +printf ("used arrays = %d\n", status.semusz); +printf ("allocated semaphores = %d\n", status.semaem); +*/ + +static inline int ipc_sem_get_status(struct ipc_status *st) { + struct seminfo seminfo; + union semun arg; + + arg.array = (ushort *) (void *) &seminfo; + + if(unlikely(semctl (0, 0, SEM_INFO, arg) < 0)) { + /* kernel not configured for semaphores */ + static int error_shown = 0; + if(unlikely(!error_shown)) { + error("IPC: kernel is not configured for semaphores"); + error_shown = 1; + } + st->semusz = 0; + st->semaem = 0; + return -1; + } + + st->semusz = seminfo.semusz; + st->semaem = seminfo.semaem; + return 0; +} + +int do_ipc(int update_every, usec_t dt) { + (void)dt; + + static int initialized = 0, read_limits_next = 0; + static struct ipc_limits limits; + static struct ipc_status status; + static RRDVAR *arrays_max = NULL, *semaphores_max = NULL; + static RRDSET *semaphores = NULL, *arrays = NULL; + + if(unlikely(!initialized)) { + initialized = 1; + + // make sure it works + if(ipc_sem_get_limits(&limits) == -1) { + error("unable to fetch semaphore limits"); + return 1; + } + + // make sure it works + if(ipc_sem_get_status(&status) == -1) { + error("unable to fetch semaphore statistics"); + return 1; + } + + arrays_max = rrdvar_custom_host_variable_create(&localhost, "ipc.semaphores.arrays.max"); + semaphores_max = rrdvar_custom_host_variable_create(&localhost, "ipc.semaphores.max"); + + if(arrays_max) rrdvar_custom_host_variable_set(arrays_max, limits.semmni); + if(semaphores_max) rrdvar_custom_host_variable_set(semaphores_max, limits.semmns); + + // create the charts + semaphores = rrdset_find("system.ipc_semaphores"); + if(!semaphores) { + semaphores = rrdset_create("system", "ipc_semaphores", NULL, "ipc semaphores", NULL, "IPC Semaphores", "semaphores", 1000, rrd_update_every, RRDSET_TYPE_AREA); + rrddim_add(semaphores, "semaphores", NULL, 1, 1, RRDDIM_ABSOLUTE); + } + + arrays = rrdset_find("system.ipc_semaphore_arrays"); + if(!arrays) { + arrays = rrdset_create("system", "ipc_semaphore_arrays", NULL, "ipc semaphores", NULL, "IPC Semaphore Arrays", "arrays", 1000, rrd_update_every, RRDSET_TYPE_AREA); + rrddim_add(arrays, "arrays", NULL, 1, 1, RRDDIM_ABSOLUTE); + } + } + + if(unlikely(read_limits_next < 0)) { + if(unlikely(ipc_sem_get_limits(&limits) == -1)) { + error("Unable to fetch semaphore limits."); + } + else { + if(arrays_max) rrdvar_custom_host_variable_set(arrays_max, limits.semmni); + if(semaphores_max) rrdvar_custom_host_variable_set(semaphores_max, limits.semmns); + + arrays->red = limits.semmni; + semaphores->red = limits.semmns; + + read_limits_next = 60 / update_every; + } + } + else + read_limits_next--; + + if(unlikely(ipc_sem_get_status(&status) == -1)) { + error("Unable to get semaphore statistics"); + return 0; + } + + if(semaphores->counter_done) rrdset_next(semaphores); + rrddim_set(semaphores, "semaphores", status.semaem); + rrdset_done(semaphores); + + if(arrays->counter_done) rrdset_next(arrays); + rrddim_set(arrays, "arrays", status.semusz); + rrdset_done(arrays); + + return 0; +} diff --git a/src/ipc.h b/src/ipc.h new file mode 100644 index 000000000..04f9df5cd --- /dev/null +++ b/src/ipc.h @@ -0,0 +1,7 @@ +#ifndef NETDATA_PLUGIN_IPC_H +#define NETDATA_PLUGIN_IPC_H 1 + +extern int do_ipc(int update_every, usec_t dt); + +#endif /* NETDATA_PLUGIN_IPC_H */ + @@ -24,7 +24,7 @@ void syslog_init(void) { } int open_log_file(int fd, FILE **fp, const char *filename, int *enabled_syslog) { - int f, t; + int f; if(!filename || !*filename || !strcmp(filename, "none")) filename = "/dev/null"; @@ -64,7 +64,7 @@ int open_log_file(int fd, FILE **fp, const char *filename, int *enabled_syslog) if(fd != f && fd != -1) { // it automatically closes - t = dup2(f, fd); + int t = dup2(f, fd); if (t == -1) { error("Cannot dup2() new fd %d to old fd %d for '%s'", f, fd, filename); close(f); @@ -125,9 +125,13 @@ int error_log_limit(int reset) { // prevent all logs if the errors per period is 0 if(error_log_errors_per_period == 0) +#ifdef NETDATA_INTERNAL_CHECKS + return 0; +#else return 1; +#endif - time_t now = time(NULL); + time_t now = now_monotonic_sec(); if(!start) start = now; if(reset) { @@ -185,7 +189,11 @@ int error_log_limit(int reset) { prevented++; // prevent logging this error +#ifdef NETDATA_INTERNAL_CHECKS + return 0; +#else return 1; +#endif } return 0; @@ -200,15 +208,15 @@ int error_log_limit(int reset) { void log_date(FILE *out) { - char outstr[24]; + char outstr[26]; time_t t; struct tm *tmp, tmbuf; - t = time(NULL); + t = now_realtime_sec(); tmp = localtime_r(&t, &tmbuf); if (tmp == NULL) return; - if (unlikely(strftime(outstr, sizeof(outstr), "%y-%m-%d %H:%M:%S", tmp) == 0)) return; + if (unlikely(strftime(outstr, sizeof(outstr), "%Y-%m-%d %H:%M:%S", tmp) == 0)) return; fprintf(out, "%s: ", outstr); } @@ -222,7 +230,7 @@ void debug_int( const char *file, const char *function, const unsigned long line log_date(stdout); va_start( args, fmt ); - printf("DEBUG (%04lu@%-10.10s:%-15.15s): %s: ", line, file, function, program_name); + printf("%s: DEBUG (%04lu@%-10.10s:%-15.15s): ", program_name, line, file, function); vprintf(fmt, args); va_end( args ); putchar('\n'); @@ -249,8 +257,8 @@ void info_int( const char *file, const char *function, const unsigned long line, log_date(stderr); va_start( args, fmt ); - if(debug_flags) fprintf(stderr, "INFO (%04lu@%-10.10s:%-15.15s): %s: ", line, file, function, program_name); - else fprintf(stderr, "INFO: %s: ", program_name); + if(debug_flags) fprintf(stderr, "%s: INFO: (%04lu@%-10.10s:%-15.15s):", program_name, line, file, function); + else fprintf(stderr, "%s: INFO: ", program_name); vfprintf( stderr, fmt, args ); va_end( args ); @@ -298,8 +306,8 @@ void error_int( const char *prefix, const char *file, const char *function, cons log_date(stderr); va_start( args, fmt ); - if(debug_flags) fprintf(stderr, "%s (%04lu@%-10.10s:%-15.15s): %s: ", prefix, line, file, function, program_name); - else fprintf(stderr, "%s: %s: ", prefix, program_name); + if(debug_flags) fprintf(stderr, "%s: %s: (%04lu@%-10.10s:%-15.15s): ", program_name, prefix, line, file, function); + else fprintf(stderr, "%s: %s: ", program_name, prefix); vfprintf( stderr, fmt, args ); va_end( args ); @@ -325,8 +333,8 @@ void fatal_int( const char *file, const char *function, const unsigned long line log_date(stderr); va_start( args, fmt ); - if(debug_flags) fprintf(stderr, "FATAL (%04lu@%-10.10s:%-15.15s): %s: ", line, file, function, program_name); - else fprintf(stderr, "FATAL: %s: ", program_name); + if(debug_flags) fprintf(stderr, "%s: FATAL: (%04lu@%-10.10s:%-15.15s): ", program_name, line, file, function); + else fprintf(stderr, "%s: FATAL: ", program_name); vfprintf( stderr, fmt, args ); va_end( args ); @@ -25,6 +25,8 @@ #define D_REGISTRY 0x00200000 #define D_VARIABLES 0x00400000 #define D_HEALTH 0x00800000 +#define D_CONNECT_TO 0x01000000 +#define D_SYSTEM 0x80000000 //#define DEBUG (D_WEB_CLIENT_ACCESS|D_LISTENER|D_RRD_STATS) //#define DEBUG 0xffffffff @@ -62,10 +64,10 @@ extern void reopen_all_log_files(); #define fatal(args...) fatal_int(__FILE__, __FUNCTION__, __LINE__, ##args) extern void log_date(FILE *out); -extern void debug_int( const char *file, const char *function, const unsigned long line, const char *fmt, ... ) __attribute__ (( format (printf, 4, 5))); -extern void info_int( const char *file, const char *function, const unsigned long line, const char *fmt, ... ) __attribute__ (( format (printf, 4, 5))); -extern void error_int( const char *prefix, const char *file, const char *function, const unsigned long line, const char *fmt, ... ) __attribute__ (( format (printf, 5, 6))); -extern void fatal_int( const char *file, const char *function, const unsigned long line, const char *fmt, ... ) __attribute__ ((noreturn, format (printf, 4, 5))); -extern void log_access( const char *fmt, ... ) __attribute__ (( format (printf, 1, 2))); +extern void debug_int( const char *file, const char *function, const unsigned long line, const char *fmt, ... ) PRINTFLIKE(4, 5); +extern void info_int( const char *file, const char *function, const unsigned long line, const char *fmt, ... ) PRINTFLIKE(4, 5); +extern void error_int( const char *prefix, const char *file, const char *function, const unsigned long line, const char *fmt, ... ) PRINTFLIKE(5, 6); +extern void fatal_int( const char *file, const char *function, const unsigned long line, const char *fmt, ... ) NORETURN PRINTFLIKE(4, 5); +extern void log_access( const char *fmt, ... ) PRINTFLIKE(1, 2); #endif /* NETDATA_LOG_H */ diff --git a/src/macos_fw.c b/src/macos_fw.c new file mode 100644 index 000000000..a62aa7a7e --- /dev/null +++ b/src/macos_fw.c @@ -0,0 +1,487 @@ +#include "common.h" +#include <CoreFoundation/CoreFoundation.h> +#include <IOKit/IOKitLib.h> +#include <IOKit/storage/IOBlockStorageDriver.h> +#include <IOKit/IOBSD.h> +// NEEDED BY do_space, do_inodes +#include <sys/mount.h> +// NEEDED BY: struct ifaddrs, getifaddrs() +#include <net/if.h> +#include <ifaddrs.h> + +// NEEDED BY: do_bandwidth +#define IFA_DATA(s) (((struct if_data *)ifa->ifa_data)->ifi_ ## s) + +#define MAXDRIVENAME 31 + +#define KILO_FACTOR 1024 +#define MEGA_FACTOR 1048576 // 1024 * 1024 +#define GIGA_FACTOR 1073741824 // 1024 * 1024 * 1024 + +int do_macos_iokit(int update_every, usec_t dt) { + (void)dt; + + static int do_io = -1, do_space = -1, do_inodes = -1, do_bandwidth = -1; + + if (unlikely(do_io == -1)) { + do_io = config_get_boolean("plugin:macos:iokit", "disk i/o", 1); + do_space = config_get_boolean("plugin:macos:sysctl", "space usage for all disks", 1); + do_inodes = config_get_boolean("plugin:macos:sysctl", "inodes usage for all disks", 1); + do_bandwidth = config_get_boolean("plugin:macos:sysctl", "bandwidth", 1); + } + + RRDSET *st; + + mach_port_t master_port; + io_registry_entry_t drive, drive_media; + io_iterator_t drive_list; + CFDictionaryRef properties, statistics; + CFStringRef name; + CFNumberRef number; + kern_return_t status; + collected_number total_disk_reads = 0; + collected_number total_disk_writes = 0; + struct diskstat { + char name[MAXDRIVENAME]; + collected_number bytes_read; + collected_number bytes_write; + collected_number reads; + collected_number writes; + collected_number time_read; + collected_number time_write; + collected_number latency_read; + collected_number latency_write; + } diskstat; + struct cur_diskstat { + collected_number duration_read_ns; + collected_number duration_write_ns; + collected_number busy_time_ns; + } cur_diskstat; + struct prev_diskstat { + collected_number bytes_read; + collected_number bytes_write; + collected_number operations_read; + collected_number operations_write; + collected_number duration_read_ns; + collected_number duration_write_ns; + collected_number busy_time_ns; + } prev_diskstat; + + // NEEDED BY: do_space, do_inodes + struct statfs *mntbuf; + int mntsize, i; + char mntonname[MNAMELEN + 1]; + char title[4096 + 1]; + + // NEEDED BY: do_bandwidth + struct ifaddrs *ifa, *ifap; + + /* Get ports and services for drive statistics. */ + if (unlikely(IOMasterPort(bootstrap_port, &master_port))) { + error("MACOS: IOMasterPort() failed"); + do_io = 0; + error("DISABLED: system.io"); + /* Get the list of all drive objects. */ + } else if (unlikely(IOServiceGetMatchingServices(master_port, IOServiceMatching("IOBlockStorageDriver"), &drive_list))) { + error("MACOS: IOServiceGetMatchingServices() failed"); + do_io = 0; + error("DISABLED: system.io"); + } else { + while ((drive = IOIteratorNext(drive_list)) != 0) { + properties = 0; + statistics = 0; + number = 0; + bzero(&diskstat, sizeof(diskstat)); + + /* Get drive media object. */ + status = IORegistryEntryGetChildEntry(drive, kIOServicePlane, &drive_media); + if (unlikely(status != KERN_SUCCESS)) { + IOObjectRelease(drive); + continue; + } + + /* Get drive media properties. */ + if (likely(!IORegistryEntryCreateCFProperties(drive_media, (CFMutableDictionaryRef *)&properties, kCFAllocatorDefault, 0))) { + /* Get disk name. */ + if (likely(name = (CFStringRef)CFDictionaryGetValue(properties, CFSTR(kIOBSDNameKey)))) { + CFStringGetCString(name, diskstat.name, MAXDRIVENAME, kCFStringEncodingUTF8); + } + } + + /* Release. */ + CFRelease(properties); + IOObjectRelease(drive_media); + + /* Obtain the properties for this drive object. */ + if (unlikely(IORegistryEntryCreateCFProperties(drive, (CFMutableDictionaryRef *)&properties, kCFAllocatorDefault, 0))) { + error("MACOS: IORegistryEntryCreateCFProperties() failed"); + do_io = 0; + error("DISABLED: system.io"); + break; + } else if (likely(properties)) { + /* Obtain the statistics from the drive properties. */ + if (likely(statistics = (CFDictionaryRef)CFDictionaryGetValue(properties, CFSTR(kIOBlockStorageDriverStatisticsKey)))) { + + // -------------------------------------------------------------------- + + /* Get bytes read. */ + if (likely(number = (CFNumberRef)CFDictionaryGetValue(statistics, CFSTR(kIOBlockStorageDriverStatisticsBytesReadKey)))) { + CFNumberGetValue(number, kCFNumberSInt64Type, &diskstat.bytes_read); + total_disk_reads += diskstat.bytes_read; + } + + /* Get bytes written. */ + if (likely(number = (CFNumberRef)CFDictionaryGetValue(statistics, CFSTR(kIOBlockStorageDriverStatisticsBytesWrittenKey)))) { + CFNumberGetValue(number, kCFNumberSInt64Type, &diskstat.bytes_write); + total_disk_writes += diskstat.bytes_write; + } + + st = rrdset_find_bytype("disk", diskstat.name); + if (unlikely(!st)) { + st = rrdset_create("disk", diskstat.name, NULL, diskstat.name, "disk.io", "Disk I/O Bandwidth", "kilobytes/s", 2000, update_every, RRDSET_TYPE_AREA); + + rrddim_add(st, "reads", NULL, 1, 1024, RRDDIM_INCREMENTAL); + rrddim_add(st, "writes", NULL, -1, 1024, RRDDIM_INCREMENTAL); + } + else rrdset_next(st); + + prev_diskstat.bytes_read = rrddim_set(st, "reads", diskstat.bytes_read); + prev_diskstat.bytes_write = rrddim_set(st, "writes", diskstat.bytes_write); + rrdset_done(st); + + // -------------------------------------------------------------------- + + /* Get number of reads. */ + if (likely(number = (CFNumberRef)CFDictionaryGetValue(statistics, CFSTR(kIOBlockStorageDriverStatisticsReadsKey)))) { + CFNumberGetValue(number, kCFNumberSInt64Type, &diskstat.reads); + } + + /* Get number of writes. */ + if (likely(number = (CFNumberRef)CFDictionaryGetValue(statistics, CFSTR(kIOBlockStorageDriverStatisticsWritesKey)))) { + CFNumberGetValue(number, kCFNumberSInt64Type, &diskstat.writes); + } + + st = rrdset_find_bytype("disk_ops", diskstat.name); + if (unlikely(!st)) { + st = rrdset_create("disk_ops", diskstat.name, NULL, diskstat.name, "disk.ops", "Disk Completed I/O Operations", "operations/s", 2001, update_every, RRDSET_TYPE_LINE); + st->isdetail = 1; + + rrddim_add(st, "reads", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "writes", NULL, -1, 1, RRDDIM_INCREMENTAL); + } + else rrdset_next(st); + + prev_diskstat.operations_read = rrddim_set(st, "reads", diskstat.reads); + prev_diskstat.operations_write = rrddim_set(st, "writes", diskstat.writes); + rrdset_done(st); + + // -------------------------------------------------------------------- + + /* Get reads time. */ + if (likely(number = (CFNumberRef)CFDictionaryGetValue(statistics, CFSTR(kIOBlockStorageDriverStatisticsTotalReadTimeKey)))) { + CFNumberGetValue(number, kCFNumberSInt64Type, &diskstat.time_read); + } + + /* Get writes time. */ + if (likely(number = (CFNumberRef)CFDictionaryGetValue(statistics, CFSTR(kIOBlockStorageDriverStatisticsTotalWriteTimeKey)))) { + CFNumberGetValue(number, kCFNumberSInt64Type, &diskstat.time_write); + } + + st = rrdset_find_bytype("disk_util", diskstat.name); + if (unlikely(!st)) { + st = rrdset_create("disk_util", diskstat.name, NULL, diskstat.name, "disk.util", "Disk Utilization Time", "% of time working", 2004, update_every, RRDSET_TYPE_AREA); + st->isdetail = 1; + + rrddim_add(st, "utilization", NULL, 1, 10000000, RRDDIM_INCREMENTAL); + } + else rrdset_next(st); + + cur_diskstat.busy_time_ns = (diskstat.time_read + diskstat.time_write); + prev_diskstat.busy_time_ns = rrddim_set(st, "utilization", cur_diskstat.busy_time_ns); + rrdset_done(st); + + // -------------------------------------------------------------------- + + /* Get reads latency. */ + if (likely(number = (CFNumberRef)CFDictionaryGetValue(statistics, CFSTR(kIOBlockStorageDriverStatisticsLatentReadTimeKey)))) { + CFNumberGetValue(number, kCFNumberSInt64Type, &diskstat.latency_read); + } + + /* Get writes latency. */ + if (likely(number = (CFNumberRef)CFDictionaryGetValue(statistics, CFSTR(kIOBlockStorageDriverStatisticsLatentWriteTimeKey)))) { + CFNumberGetValue(number, kCFNumberSInt64Type, &diskstat.latency_write); + } + + st = rrdset_find_bytype("disk_iotime", diskstat.name); + if (unlikely(!st)) { + st = rrdset_create("disk_iotime", diskstat.name, NULL, diskstat.name, "disk.iotime", "Disk Total I/O Time", "milliseconds/s", 2022, update_every, RRDSET_TYPE_LINE); + st->isdetail = 1; + + rrddim_add(st, "reads", NULL, 1, 1000000, RRDDIM_INCREMENTAL); + rrddim_add(st, "writes", NULL, -1, 1000000, RRDDIM_INCREMENTAL); + } + else rrdset_next(st); + + cur_diskstat.duration_read_ns = diskstat.time_read + diskstat.latency_read; + cur_diskstat.duration_write_ns = diskstat.time_write + diskstat.latency_write; + prev_diskstat.duration_read_ns = rrddim_set(st, "reads", cur_diskstat.duration_read_ns); + prev_diskstat.duration_write_ns = rrddim_set(st, "writes", cur_diskstat.duration_write_ns); + rrdset_done(st); + + // -------------------------------------------------------------------- + // calculate differential charts + // only if this is not the first time we run + + if (likely(dt)) { + + // -------------------------------------------------------------------- + + st = rrdset_find_bytype("disk_await", diskstat.name); + if (unlikely(!st)) { + st = rrdset_create("disk_await", diskstat.name, NULL, diskstat.name, "disk.await", "Average Completed I/O Operation Time", "ms per operation", 2005, update_every, RRDSET_TYPE_LINE); + st->isdetail = 1; + + rrddim_add(st, "reads", NULL, 1, 1000000, RRDDIM_ABSOLUTE); + rrddim_add(st, "writes", NULL, -1, 1000000, RRDDIM_ABSOLUTE); + } + else rrdset_next(st); + + rrddim_set(st, "reads", (diskstat.reads - prev_diskstat.operations_read) ? + (cur_diskstat.duration_read_ns - prev_diskstat.duration_read_ns) / (diskstat.reads - prev_diskstat.operations_read) : 0); + rrddim_set(st, "writes", (diskstat.writes - prev_diskstat.operations_write) ? + (cur_diskstat.duration_write_ns - prev_diskstat.duration_write_ns) / (diskstat.writes - prev_diskstat.operations_write) : 0); + rrdset_done(st); + + // -------------------------------------------------------------------- + + st = rrdset_find_bytype("disk_avgsz", diskstat.name); + if (unlikely(!st)) { + st = rrdset_create("disk_avgsz", diskstat.name, NULL, diskstat.name, "disk.avgsz", "Average Completed I/O Operation Bandwidth", "kilobytes per operation", 2006, update_every, RRDSET_TYPE_AREA); + st->isdetail = 1; + + rrddim_add(st, "reads", NULL, 1, 1024, RRDDIM_ABSOLUTE); + rrddim_add(st, "writes", NULL, -1, 1024, RRDDIM_ABSOLUTE); + } + else rrdset_next(st); + + rrddim_set(st, "reads", (diskstat.reads - prev_diskstat.operations_read) ? + (diskstat.bytes_read - prev_diskstat.bytes_read) / (diskstat.reads - prev_diskstat.operations_read) : 0); + rrddim_set(st, "writes", (diskstat.writes - prev_diskstat.operations_write) ? + (diskstat.bytes_write - prev_diskstat.bytes_write) / (diskstat.writes - prev_diskstat.operations_write) : 0); + rrdset_done(st); + + // -------------------------------------------------------------------- + + st = rrdset_find_bytype("disk_svctm", diskstat.name); + if (unlikely(!st)) { + st = rrdset_create("disk_svctm", diskstat.name, NULL, diskstat.name, "disk.svctm", "Average Service Time", "ms per operation", 2007, update_every, RRDSET_TYPE_LINE); + st->isdetail = 1; + + rrddim_add(st, "svctm", NULL, 1, 1000000, RRDDIM_ABSOLUTE); + } + else rrdset_next(st); + + rrddim_set(st, "svctm", ((diskstat.reads - prev_diskstat.operations_read) + (diskstat.writes - prev_diskstat.operations_write)) ? + (cur_diskstat.busy_time_ns - prev_diskstat.busy_time_ns) / ((diskstat.reads - prev_diskstat.operations_read) + (diskstat.writes - prev_diskstat.operations_write)) : 0); + rrdset_done(st); + } + } + + /* Release. */ + CFRelease(properties); + } + + /* Release. */ + IOObjectRelease(drive); + } + IOIteratorReset(drive_list); + + /* Release. */ + IOObjectRelease(drive_list); + } + + if (likely(do_io)) { + st = rrdset_find_bytype("system", "io"); + if (unlikely(!st)) { + st = rrdset_create("system", "io", NULL, "disk", NULL, "Disk I/O", "kilobytes/s", 150, update_every, RRDSET_TYPE_AREA); + rrddim_add(st, "in", NULL, 1, 1024, RRDDIM_INCREMENTAL); + rrddim_add(st, "out", NULL, -1, 1024, RRDDIM_INCREMENTAL); + } + else rrdset_next(st); + + rrddim_set(st, "in", total_disk_reads); + rrddim_set(st, "out", total_disk_writes); + rrdset_done(st); + } + + // Can be merged with FreeBSD plugin + // -------------------------------------------------------------------------- + + if (likely(do_space || do_inodes)) { + // there is no mount info in sysctl MIBs + if (unlikely(!(mntsize = getmntinfo(&mntbuf, MNT_NOWAIT)))) { + error("MACOS: getmntinfo() failed"); + do_space = 0; + error("DISABLED: disk_space.X"); + do_inodes = 0; + error("DISABLED: disk_inodes.X"); + } else { + for (i = 0; i < mntsize; i++) { + if (mntbuf[i].f_flags == MNT_RDONLY || + mntbuf[i].f_blocks == 0 || + // taken from gnulib/mountlist.c and shortened to FreeBSD related fstypes + strcmp(mntbuf[i].f_fstypename, "autofs") == 0 || + strcmp(mntbuf[i].f_fstypename, "procfs") == 0 || + strcmp(mntbuf[i].f_fstypename, "subfs") == 0 || + strcmp(mntbuf[i].f_fstypename, "devfs") == 0 || + strcmp(mntbuf[i].f_fstypename, "none") == 0) + continue; + + // -------------------------------------------------------------------------- + + if (likely(do_space)) { + st = rrdset_find_bytype("disk_space", mntbuf[i].f_mntonname); + if (unlikely(!st)) { + snprintfz(title, 4096, "Disk Space Usage for %s [%s]", mntbuf[i].f_mntonname, mntbuf[i].f_mntfromname); + st = rrdset_create("disk_space", mntbuf[i].f_mntonname, NULL, mntbuf[i].f_mntonname, "disk.space", title, "GB", 2023, + update_every, + RRDSET_TYPE_STACKED); + + rrddim_add(st, "avail", NULL, mntbuf[i].f_bsize, GIGA_FACTOR, RRDDIM_ABSOLUTE); + rrddim_add(st, "used", NULL, mntbuf[i].f_bsize, GIGA_FACTOR, RRDDIM_ABSOLUTE); + rrddim_add(st, "reserved_for_root", "reserved for root", mntbuf[i].f_bsize, GIGA_FACTOR, + RRDDIM_ABSOLUTE); + } else + rrdset_next(st); + + rrddim_set(st, "avail", (collected_number) mntbuf[i].f_bavail); + rrddim_set(st, "used", (collected_number) (mntbuf[i].f_blocks - mntbuf[i].f_bfree)); + rrddim_set(st, "reserved_for_root", (collected_number) (mntbuf[i].f_bfree - mntbuf[i].f_bavail)); + rrdset_done(st); + } + + // -------------------------------------------------------------------------- + + if (likely(do_inodes)) { + st = rrdset_find_bytype("disk_inodes", mntbuf[i].f_mntonname); + if (unlikely(!st)) { + snprintfz(title, 4096, "Disk Files (inodes) Usage for %s [%s]", mntbuf[i].f_mntonname, mntbuf[i].f_mntfromname); + st = rrdset_create("disk_inodes", mntbuf[i].f_mntonname, NULL, mntbuf[i].f_mntonname, "disk.inodes", title, "Inodes", 2024, + update_every, RRDSET_TYPE_STACKED); + + rrddim_add(st, "avail", NULL, 1, 1, RRDDIM_ABSOLUTE); + rrddim_add(st, "used", NULL, 1, 1, RRDDIM_ABSOLUTE); + rrddim_add(st, "reserved_for_root", "reserved for root", 1, 1, RRDDIM_ABSOLUTE); + } else + rrdset_next(st); + + rrddim_set(st, "avail", (collected_number) mntbuf[i].f_ffree); + rrddim_set(st, "used", (collected_number) (mntbuf[i].f_files - mntbuf[i].f_ffree)); + rrdset_done(st); + } + } + } + } + + // Can be merged with FreeBSD plugin + // -------------------------------------------------------------------- + + if (likely(do_bandwidth)) { + if (unlikely(getifaddrs(&ifap))) { + error("MACOS: getifaddrs()"); + do_bandwidth = 0; + error("DISABLED: system.ipv4"); + } else { + for (ifa = ifap; ifa; ifa = ifa->ifa_next) { + if (ifa->ifa_addr->sa_family != AF_LINK) + continue; + + // -------------------------------------------------------------------- + + st = rrdset_find_bytype("net", ifa->ifa_name); + if (unlikely(!st)) { + st = rrdset_create("net", ifa->ifa_name, NULL, ifa->ifa_name, "net.net", "Bandwidth", "kilobits/s", 7000, update_every, RRDSET_TYPE_AREA); + + rrddim_add(st, "received", NULL, 8, 1024, RRDDIM_INCREMENTAL); + rrddim_add(st, "sent", NULL, -8, 1024, RRDDIM_INCREMENTAL); + } + else rrdset_next(st); + + rrddim_set(st, "received", IFA_DATA(ibytes)); + rrddim_set(st, "sent", IFA_DATA(obytes)); + rrdset_done(st); + + // -------------------------------------------------------------------- + + st = rrdset_find_bytype("net_packets", ifa->ifa_name); + if (unlikely(!st)) { + st = rrdset_create("net_packets", ifa->ifa_name, NULL, ifa->ifa_name, "net.packets", "Packets", "packets/s", 7001, update_every, RRDSET_TYPE_LINE); + st->isdetail = 1; + + rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "multicast_received", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "multicast_sent", NULL, -1, 1, RRDDIM_INCREMENTAL); + } + else rrdset_next(st); + + rrddim_set(st, "received", IFA_DATA(ipackets)); + rrddim_set(st, "sent", IFA_DATA(opackets)); + rrddim_set(st, "multicast_received", IFA_DATA(imcasts)); + rrddim_set(st, "multicast_sent", IFA_DATA(omcasts)); + rrdset_done(st); + + // -------------------------------------------------------------------- + + st = rrdset_find_bytype("net_errors", ifa->ifa_name); + if (unlikely(!st)) { + st = rrdset_create("net_errors", ifa->ifa_name, NULL, ifa->ifa_name, "net.errors", "Interface Errors", "errors/s", 7002, update_every, RRDSET_TYPE_LINE); + st->isdetail = 1; + + rrddim_add(st, "inbound", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "outbound", NULL, -1, 1, RRDDIM_INCREMENTAL); + } + else rrdset_next(st); + + rrddim_set(st, "inbound", IFA_DATA(ierrors)); + rrddim_set(st, "outbound", IFA_DATA(oerrors)); + rrdset_done(st); + + // -------------------------------------------------------------------- + + st = rrdset_find_bytype("net_drops", ifa->ifa_name); + if (unlikely(!st)) { + st = rrdset_create("net_drops", ifa->ifa_name, NULL, ifa->ifa_name, "net.drops", "Interface Drops", "drops/s", 7003, update_every, RRDSET_TYPE_LINE); + st->isdetail = 1; + + rrddim_add(st, "inbound", NULL, 1, 1, RRDDIM_INCREMENTAL); + } + else rrdset_next(st); + + rrddim_set(st, "inbound", IFA_DATA(iqdrops)); + rrdset_done(st); + + // -------------------------------------------------------------------- + + st = rrdset_find_bytype("net_events", ifa->ifa_name); + if (unlikely(!st)) { + st = rrdset_create("net_events", ifa->ifa_name, NULL, ifa->ifa_name, "net.events", "Network Interface Events", "events/s", 7006, update_every, RRDSET_TYPE_LINE); + st->isdetail = 1; + + rrddim_add(st, "frames", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "collisions", NULL, -1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "carrier", NULL, -1, 1, RRDDIM_INCREMENTAL); + } + else rrdset_next(st); + + rrddim_set(st, "collisions", IFA_DATA(collisions)); + rrdset_done(st); + } + + freeifaddrs(ifap); + } + } + + + return 0; +} diff --git a/src/macos_mach_smi.c b/src/macos_mach_smi.c new file mode 100644 index 000000000..d86a03220 --- /dev/null +++ b/src/macos_mach_smi.c @@ -0,0 +1,167 @@ +#include "common.h" +#include <mach/mach.h> + +int do_macos_mach_smi(int update_every, usec_t dt) { + (void)dt; + + static int do_cpu = -1, do_ram = - 1, do_swapio = -1, do_pgfaults = -1; + + if (unlikely(do_cpu == -1)) { + do_cpu = config_get_boolean("plugin:macos:mach_smi", "cpu utilization", 1); + do_ram = config_get_boolean("plugin:macos:mach_smi", "system ram", 1); + do_swapio = config_get_boolean("plugin:macos:mach_smi", "swap i/o", 1); + do_pgfaults = config_get_boolean("plugin:macos:mach_smi", "memory page faults", 1); + } + + RRDSET *st; + + kern_return_t kr; + mach_msg_type_number_t count; + host_t host; + vm_size_t system_pagesize; + + + // NEEDED BY: do_cpu + natural_t cp_time[CPU_STATE_MAX]; + + // NEEDED BY: do_ram, do_swapio, do_pgfaults + vm_statistics64_data_t vm_statistics; + + host = mach_host_self(); + kr = host_page_size(host, &system_pagesize); + if (unlikely(kr != KERN_SUCCESS)) + return -1; + + // -------------------------------------------------------------------- + + if (likely(do_cpu)) { + if (unlikely(HOST_CPU_LOAD_INFO_COUNT != 4)) { + error("MACOS: There are %d CPU states (4 was expected)", HOST_CPU_LOAD_INFO_COUNT); + do_cpu = 0; + error("DISABLED: system.cpu"); + } else { + count = HOST_CPU_LOAD_INFO_COUNT; + kr = host_statistics(host, HOST_CPU_LOAD_INFO, (host_info_t)cp_time, &count); + if (unlikely(kr != KERN_SUCCESS)) { + error("MACOS: host_statistics() failed: %s", mach_error_string(kr)); + do_cpu = 0; + error("DISABLED: system.cpu"); + } else { + + st = rrdset_find_bytype("system", "cpu"); + if (unlikely(!st)) { + st = rrdset_create("system", "cpu", NULL, "cpu", "system.cpu", "Total CPU utilization", "percentage", 100, update_every, RRDSET_TYPE_STACKED); + + rrddim_add(st, "user", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL); + rrddim_add(st, "nice", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL); + rrddim_add(st, "system", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL); + rrddim_add(st, "idle", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL); + rrddim_hide(st, "idle"); + } + else rrdset_next(st); + + rrddim_set(st, "user", cp_time[CPU_STATE_USER]); + rrddim_set(st, "nice", cp_time[CPU_STATE_NICE]); + rrddim_set(st, "system", cp_time[CPU_STATE_SYSTEM]); + rrddim_set(st, "idle", cp_time[CPU_STATE_IDLE]); + rrdset_done(st); + } + } + } + + // -------------------------------------------------------------------- + + if (likely(do_ram || do_swapio || do_pgfaults)) { + count = sizeof(vm_statistics64_data_t); + kr = host_statistics64(host, HOST_VM_INFO64, (host_info64_t)&vm_statistics, &count); + if (unlikely(kr != KERN_SUCCESS)) { + error("MACOS: host_statistics64() failed: %s", mach_error_string(kr)); + do_ram = 0; + error("DISABLED: system.ram"); + do_swapio = 0; + error("DISABLED: system.swapio"); + do_pgfaults = 0; + error("DISABLED: mem.pgfaults"); + } else { + if (likely(do_ram)) { + st = rrdset_find("system.ram"); + if (unlikely(!st)) { + st = rrdset_create("system", "ram", NULL, "ram", NULL, "System RAM", "MB", 200, update_every, RRDSET_TYPE_STACKED); + + rrddim_add(st, "active", NULL, system_pagesize, 1048576, RRDDIM_ABSOLUTE); + rrddim_add(st, "wired", NULL, system_pagesize, 1048576, RRDDIM_ABSOLUTE); + rrddim_add(st, "throttled", NULL, system_pagesize, 1048576, RRDDIM_ABSOLUTE); + rrddim_add(st, "compressor", NULL, system_pagesize, 1048576, RRDDIM_ABSOLUTE); + rrddim_add(st, "inactive", NULL, system_pagesize, 1048576, RRDDIM_ABSOLUTE); + rrddim_add(st, "purgeable", NULL, system_pagesize, 1048576, RRDDIM_ABSOLUTE); + rrddim_add(st, "speculative", NULL, system_pagesize, 1048576, RRDDIM_ABSOLUTE); + rrddim_add(st, "free", NULL, system_pagesize, 1048576, RRDDIM_ABSOLUTE); + } + else rrdset_next(st); + + rrddim_set(st, "active", vm_statistics.active_count); + rrddim_set(st, "wired", vm_statistics.wire_count); + rrddim_set(st, "throttled", vm_statistics.throttled_count); + rrddim_set(st, "compressor", vm_statistics.compressor_page_count); + rrddim_set(st, "inactive", vm_statistics.inactive_count); + rrddim_set(st, "purgeable", vm_statistics.purgeable_count); + rrddim_set(st, "speculative", vm_statistics.speculative_count); + rrddim_set(st, "free", (vm_statistics.free_count - vm_statistics.speculative_count)); + rrdset_done(st); + } + + // -------------------------------------------------------------------- + + if (likely(do_swapio)) { + st = rrdset_find("system.swapio"); + if (unlikely(!st)) { + st = rrdset_create("system", "swapio", NULL, "swap", NULL, "Swap I/O", "kilobytes/s", 250, update_every, RRDSET_TYPE_AREA); + + rrddim_add(st, "in", NULL, system_pagesize, 1024, RRDDIM_INCREMENTAL); + rrddim_add(st, "out", NULL, -system_pagesize, 1024, RRDDIM_INCREMENTAL); + } + else rrdset_next(st); + + rrddim_set(st, "in", vm_statistics.swapins); + rrddim_set(st, "out", vm_statistics.swapouts); + rrdset_done(st); + } + + // -------------------------------------------------------------------- + + if (likely(do_pgfaults)) { + st = rrdset_find("mem.pgfaults"); + if (unlikely(!st)) { + st = rrdset_create("mem", "pgfaults", NULL, "system", NULL, "Memory Page Faults", "page faults/s", 500, update_every, RRDSET_TYPE_LINE); + st->isdetail = 1; + + rrddim_add(st, "memory", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "cow", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "pagein", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "pageout", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "compress", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "decompress", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "zero_fill", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "reactivate", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "purge", NULL, 1, 1, RRDDIM_INCREMENTAL); + } + else rrdset_next(st); + + rrddim_set(st, "memory", vm_statistics.faults); + rrddim_set(st, "cow", vm_statistics.cow_faults); + rrddim_set(st, "pagein", vm_statistics.pageins); + rrddim_set(st, "pageout", vm_statistics.pageouts); + rrddim_set(st, "compress", vm_statistics.compressions); + rrddim_set(st, "decompress", vm_statistics.decompressions); + rrddim_set(st, "zero_fill", vm_statistics.zero_fill_count); + rrddim_set(st, "reactivate", vm_statistics.reactivations); + rrddim_set(st, "purge", vm_statistics.purges); + rrdset_done(st); + } + } + } + + // -------------------------------------------------------------------- + + return 0; +} diff --git a/src/macos_sysctl.c b/src/macos_sysctl.c new file mode 100644 index 000000000..955b70757 --- /dev/null +++ b/src/macos_sysctl.c @@ -0,0 +1,1107 @@ +#include "common.h" +#include <sys/sysctl.h> +// NEEDED BY: do_bandwidth +#include <net/route.h> +// NEEDED BY do_tcp... +#include <sys/socketvar.h> +#include <netinet/tcp_var.h> +#include <netinet/tcp_fsm.h> +// NEEDED BY do_udp..., do_ip... +#include <netinet/ip_var.h> +// NEEDED BY do_udp... +#include <netinet/udp.h> +#include <netinet/udp_var.h> +// NEEDED BY do_icmp... +#include <netinet/ip.h> +#include <netinet/ip_icmp.h> +#include <netinet/icmp_var.h> +// NEEDED BY do_icmp6... +#include <netinet/icmp6.h> +// NEEDED BY do_uptime +#include <time.h> + +// MacOS calculates load averages once every 5 seconds +#define MIN_LOADAVG_UPDATE_EVERY 5 + +int do_macos_sysctl(int update_every, usec_t dt) { + (void)dt; + + static int do_loadavg = -1, do_swap = -1, do_bandwidth = -1, + do_tcp_packets = -1, do_tcp_errors = -1, do_tcp_handshake = -1, do_ecn = -1, + do_tcpext_syscookies = -1, do_tcpext_ofo = -1, do_tcpext_connaborts = -1, + do_udp_packets = -1, do_udp_errors = -1, do_icmp_packets = -1, do_icmpmsg = -1, + do_ip_packets = -1, do_ip_fragsout = -1, do_ip_fragsin = -1, do_ip_errors = -1, + do_ip6_packets = -1, do_ip6_fragsout = -1, do_ip6_fragsin = -1, do_ip6_errors = -1, + do_icmp6 = -1, do_icmp6_redir = -1, do_icmp6_errors = -1, do_icmp6_echos = -1, + do_icmp6_router = -1, do_icmp6_neighbor = -1, do_icmp6_types = -1, do_uptime = -1; + + + if (unlikely(do_loadavg == -1)) { + do_loadavg = config_get_boolean("plugin:macos:sysctl", "enable load average", 1); + do_swap = config_get_boolean("plugin:macos:sysctl", "system swap", 1); + do_bandwidth = config_get_boolean("plugin:macos:sysctl", "bandwidth", 1); + do_tcp_packets = config_get_boolean("plugin:macos:sysctl", "ipv4 TCP packets", 1); + do_tcp_errors = config_get_boolean("plugin:macos:sysctl", "ipv4 TCP errors", 1); + do_tcp_handshake = config_get_boolean("plugin:macos:sysctl", "ipv4 TCP handshake issues", 1); + do_ecn = config_get_boolean_ondemand("plugin:macos:sysctl", "ECN packets", CONFIG_ONDEMAND_ONDEMAND); + do_tcpext_syscookies = config_get_boolean_ondemand("plugin:macos:sysctl", "TCP SYN cookies", CONFIG_ONDEMAND_ONDEMAND); + do_tcpext_ofo = config_get_boolean_ondemand("plugin:macos:sysctl", "TCP out-of-order queue", CONFIG_ONDEMAND_ONDEMAND); + do_tcpext_connaborts = config_get_boolean_ondemand("plugin:macos:sysctl", "TCP connection aborts", CONFIG_ONDEMAND_ONDEMAND); + do_udp_packets = config_get_boolean("plugin:macos:sysctl", "ipv4 UDP packets", 1); + do_udp_errors = config_get_boolean("plugin:macos:sysctl", "ipv4 UDP errors", 1); + do_icmp_packets = config_get_boolean("plugin:macos:sysctl", "ipv4 ICMP packets", 1); + do_icmpmsg = config_get_boolean("plugin:macos:sysctl", "ipv4 ICMP messages", 1); + do_ip_packets = config_get_boolean("plugin:macos:sysctl", "ipv4 packets", 1); + do_ip_fragsout = config_get_boolean("plugin:macos:sysctl", "ipv4 fragments sent", 1); + do_ip_fragsin = config_get_boolean("plugin:macos:sysctl", "ipv4 fragments assembly", 1); + do_ip_errors = config_get_boolean("plugin:macos:sysctl", "ipv4 errors", 1); + do_ip6_packets = config_get_boolean_ondemand("plugin:macos:sysctl", "ipv6 packets", CONFIG_ONDEMAND_ONDEMAND); + do_ip6_fragsout = config_get_boolean_ondemand("plugin:macos:sysctl", "ipv6 fragments sent", CONFIG_ONDEMAND_ONDEMAND); + do_ip6_fragsin = config_get_boolean_ondemand("plugin:macos:sysctl", "ipv6 fragments assembly", CONFIG_ONDEMAND_ONDEMAND); + do_ip6_errors = config_get_boolean_ondemand("plugin:macos:sysctl", "ipv6 errors", CONFIG_ONDEMAND_ONDEMAND); + do_icmp6 = config_get_boolean_ondemand("plugin:macos:sysctl", "icmp", CONFIG_ONDEMAND_ONDEMAND); + do_icmp6_redir = config_get_boolean_ondemand("plugin:macos:sysctl", "icmp redirects", CONFIG_ONDEMAND_ONDEMAND); + do_icmp6_errors = config_get_boolean_ondemand("plugin:macos:sysctl", "icmp errors", CONFIG_ONDEMAND_ONDEMAND); + do_icmp6_echos = config_get_boolean_ondemand("plugin:macos:sysctl", "icmp echos", CONFIG_ONDEMAND_ONDEMAND); + do_icmp6_router = config_get_boolean_ondemand("plugin:macos:sysctl", "icmp router", CONFIG_ONDEMAND_ONDEMAND); + do_icmp6_neighbor = config_get_boolean_ondemand("plugin:macos:sysctl", "icmp neighbor", CONFIG_ONDEMAND_ONDEMAND); + do_icmp6_types = config_get_boolean_ondemand("plugin:macos:sysctl", "icmp types", CONFIG_ONDEMAND_ONDEMAND); + do_uptime = config_get_boolean("plugin:macos:sysctl", "system uptime", 1); + } + + RRDSET *st; + + int system_pagesize = getpagesize(); // wouldn't it be better to get value directly from hw.pagesize? + int i, n; + int common_error = 0; + size_t size; + + // NEEDED BY: do_loadavg + static usec_t last_loadavg_usec = 0; + struct loadavg sysload; + + // NEEDED BY: do_swap + struct xsw_usage swap_usage; + + // NEEDED BY: do_bandwidth + int mib[6]; + static char *ifstatdata = NULL; + char *lim, *next; + struct if_msghdr *ifm; + struct iftot { + u_long ift_ibytes; + u_long ift_obytes; + } iftot = {0, 0}; + + // NEEDED BY: do_tcp... + struct tcpstat tcpstat; + uint64_t tcps_states[TCP_NSTATES]; + + // NEEDED BY: do_udp... + struct udpstat udpstat; + + // NEEDED BY: do_icmp... + struct icmpstat icmpstat; + struct icmp_total { + u_long msgs_in; + u_long msgs_out; + } icmp_total = {0, 0}; + + // NEEDED BY: do_ip... + struct ipstat ipstat; + + // NEEDED BY: do_ip6... + /* + * Dirty workaround for /usr/include/netinet6/ip6_var.h absence. + * Struct ip6stat was copied from bsd/netinet6/ip6_var.h from xnu sources. + */ +#define IP6S_SRCRULE_COUNT 16 +#include <netinet6/scope6_var.h> + struct ip6stat { + u_quad_t ip6s_total; /* total packets received */ + u_quad_t ip6s_tooshort; /* packet too short */ + u_quad_t ip6s_toosmall; /* not enough data */ + u_quad_t ip6s_fragments; /* fragments received */ + u_quad_t ip6s_fragdropped; /* frags dropped(dups, out of space) */ + u_quad_t ip6s_fragtimeout; /* fragments timed out */ + u_quad_t ip6s_fragoverflow; /* fragments that exceeded limit */ + u_quad_t ip6s_forward; /* packets forwarded */ + u_quad_t ip6s_cantforward; /* packets rcvd for unreachable dest */ + u_quad_t ip6s_redirectsent; /* packets forwarded on same net */ + u_quad_t ip6s_delivered; /* datagrams delivered to upper level */ + u_quad_t ip6s_localout; /* total ip packets generated here */ + u_quad_t ip6s_odropped; /* lost packets due to nobufs, etc. */ + u_quad_t ip6s_reassembled; /* total packets reassembled ok */ + u_quad_t ip6s_atmfrag_rcvd; /* atomic fragments received */ + u_quad_t ip6s_fragmented; /* datagrams successfully fragmented */ + u_quad_t ip6s_ofragments; /* output fragments created */ + u_quad_t ip6s_cantfrag; /* don't fragment flag was set, etc. */ + u_quad_t ip6s_badoptions; /* error in option processing */ + u_quad_t ip6s_noroute; /* packets discarded due to no route */ + u_quad_t ip6s_badvers; /* ip6 version != 6 */ + u_quad_t ip6s_rawout; /* total raw ip packets generated */ + u_quad_t ip6s_badscope; /* scope error */ + u_quad_t ip6s_notmember; /* don't join this multicast group */ + u_quad_t ip6s_nxthist[256]; /* next header history */ + u_quad_t ip6s_m1; /* one mbuf */ + u_quad_t ip6s_m2m[32]; /* two or more mbuf */ + u_quad_t ip6s_mext1; /* one ext mbuf */ + u_quad_t ip6s_mext2m; /* two or more ext mbuf */ + u_quad_t ip6s_exthdrtoolong; /* ext hdr are not continuous */ + u_quad_t ip6s_nogif; /* no match gif found */ + u_quad_t ip6s_toomanyhdr; /* discarded due to too many headers */ + + /* + * statistics for improvement of the source address selection + * algorithm: + */ + /* number of times that address selection fails */ + u_quad_t ip6s_sources_none; + /* number of times that an address on the outgoing I/F is chosen */ + u_quad_t ip6s_sources_sameif[SCOPE6_ID_MAX]; + /* number of times that an address on a non-outgoing I/F is chosen */ + u_quad_t ip6s_sources_otherif[SCOPE6_ID_MAX]; + /* + * number of times that an address that has the same scope + * from the destination is chosen. + */ + u_quad_t ip6s_sources_samescope[SCOPE6_ID_MAX]; + /* + * number of times that an address that has a different scope + * from the destination is chosen. + */ + u_quad_t ip6s_sources_otherscope[SCOPE6_ID_MAX]; + /* number of times that a deprecated address is chosen */ + u_quad_t ip6s_sources_deprecated[SCOPE6_ID_MAX]; + + u_quad_t ip6s_forward_cachehit; + u_quad_t ip6s_forward_cachemiss; + + /* number of times that each rule of source selection is applied. */ + u_quad_t ip6s_sources_rule[IP6S_SRCRULE_COUNT]; + + /* number of times we ignored address on expensive secondary interfaces */ + u_quad_t ip6s_sources_skip_expensive_secondary_if; + + /* pkt dropped, no mbufs for control data */ + u_quad_t ip6s_pktdropcntrl; + + /* total packets trimmed/adjusted */ + u_quad_t ip6s_adj; + /* hwcksum info discarded during adjustment */ + u_quad_t ip6s_adj_hwcsum_clr; + + /* duplicate address detection collisions */ + u_quad_t ip6s_dad_collide; + + /* DAD NS looped back */ + u_quad_t ip6s_dad_loopcount; + } ip6stat; + + // NEEDED BY: do_icmp6... + struct icmp6stat icmp6stat; + struct icmp6_total { + u_long msgs_in; + u_long msgs_out; + } icmp6_total = {0, 0}; + + // NEEDED BY: do_uptime + struct timespec boot_time, cur_time; + + // -------------------------------------------------------------------- + + if (last_loadavg_usec <= dt) { + if (likely(do_loadavg)) { + if (unlikely(GETSYSCTL("vm.loadavg", sysload))) { + do_loadavg = 0; + error("DISABLED: system.load"); + } else { + + st = rrdset_find_bytype("system", "load"); + if (unlikely(!st)) { + st = rrdset_create("system", "load", NULL, "load", NULL, "System Load Average", "load", 100, (update_every < MIN_LOADAVG_UPDATE_EVERY) ? MIN_LOADAVG_UPDATE_EVERY : update_every, RRDSET_TYPE_LINE); + rrddim_add(st, "load1", NULL, 1, 1000, RRDDIM_ABSOLUTE); + rrddim_add(st, "load5", NULL, 1, 1000, RRDDIM_ABSOLUTE); + rrddim_add(st, "load15", NULL, 1, 1000, RRDDIM_ABSOLUTE); + } + else rrdset_next(st); + + rrddim_set(st, "load1", (collected_number) ((double)sysload.ldavg[0] / sysload.fscale * 1000)); + rrddim_set(st, "load5", (collected_number) ((double)sysload.ldavg[1] / sysload.fscale * 1000)); + rrddim_set(st, "load15", (collected_number) ((double)sysload.ldavg[2] / sysload.fscale * 1000)); + rrdset_done(st); + } + } + + last_loadavg_usec = st->update_every * USEC_PER_SEC; + } + else last_loadavg_usec -= dt; + + // -------------------------------------------------------------------- + + if (likely(do_swap)) { + if (unlikely(GETSYSCTL("vm.swapusage", swap_usage))) { + do_swap = 0; + error("DISABLED: system.swap"); + } else { + st = rrdset_find("system.swap"); + if (unlikely(!st)) { + st = rrdset_create("system", "swap", NULL, "swap", NULL, "System Swap", "MB", 201, update_every, RRDSET_TYPE_STACKED); + st->isdetail = 1; + + rrddim_add(st, "free", NULL, 1, 1048576, RRDDIM_ABSOLUTE); + rrddim_add(st, "used", NULL, 1, 1048576, RRDDIM_ABSOLUTE); + } + else rrdset_next(st); + + rrddim_set(st, "free", swap_usage.xsu_avail); + rrddim_set(st, "used", swap_usage.xsu_used); + rrdset_done(st); + } + } + + // -------------------------------------------------------------------- + + if (likely(do_bandwidth)) { + mib[0] = CTL_NET; + mib[1] = PF_ROUTE; + mib[2] = 0; + mib[3] = AF_INET; + mib[4] = NET_RT_IFLIST2; + mib[5] = 0; + if (unlikely(sysctl(mib, 6, NULL, &size, NULL, 0))) { + error("MACOS: sysctl(%s...) failed: %s", "net interfaces", strerror(errno)); + do_bandwidth = 0; + error("DISABLED: system.ipv4"); + } else { + ifstatdata = reallocz(ifstatdata, size); + if (unlikely(sysctl(mib, 6, ifstatdata, &size, NULL, 0) < 0)) { + error("MACOS: sysctl(%s...) failed: %s", "net interfaces", strerror(errno)); + do_bandwidth = 0; + error("DISABLED: system.ipv4"); + } else { + lim = ifstatdata + size; + iftot.ift_ibytes = iftot.ift_obytes = 0; + for (next = ifstatdata; next < lim; ) { + ifm = (struct if_msghdr *)next; + next += ifm->ifm_msglen; + + if (ifm->ifm_type == RTM_IFINFO2) { + struct if_msghdr2 *if2m = (struct if_msghdr2 *)ifm; + + iftot.ift_ibytes += if2m->ifm_data.ifi_ibytes; + iftot.ift_obytes += if2m->ifm_data.ifi_obytes; + } + } + st = rrdset_find("system.ipv4"); + if (unlikely(!st)) { + st = rrdset_create("system", "ipv4", NULL, "network", NULL, "IPv4 Bandwidth", "kilobits/s", 500, update_every, RRDSET_TYPE_AREA); + + rrddim_add(st, "InOctets", "received", 8, 1024, RRDDIM_INCREMENTAL); + rrddim_add(st, "OutOctets", "sent", -8, 1024, RRDDIM_INCREMENTAL); + } + else rrdset_next(st); + + rrddim_set(st, "InOctets", iftot.ift_ibytes); + rrddim_set(st, "OutOctets", iftot.ift_obytes); + rrdset_done(st); + } + } + } + + // -------------------------------------------------------------------- + + // see http://net-snmp.sourceforge.net/docs/mibs/tcp.html + if (likely(do_tcp_packets || do_tcp_errors || do_tcp_handshake || do_tcpext_connaborts || do_tcpext_ofo || do_tcpext_syscookies || do_ecn)) { + if (unlikely(GETSYSCTL("net.inet.tcp.stats", tcpstat))){ + do_tcp_packets = 0; + error("DISABLED: ipv4.tcppackets"); + do_tcp_errors = 0; + error("DISABLED: ipv4.tcperrors"); + do_tcp_handshake = 0; + error("DISABLED: ipv4.tcphandshake"); + do_tcpext_connaborts = 0; + error("DISABLED: ipv4.tcpconnaborts"); + do_tcpext_ofo = 0; + error("DISABLED: ipv4.tcpofo"); + do_tcpext_syscookies = 0; + error("DISABLED: ipv4.tcpsyncookies"); + do_ecn = 0; + error("DISABLED: ipv4.ecnpkts"); + } else { + if (likely(do_tcp_packets)) { + st = rrdset_find("ipv4.tcppackets"); + if (unlikely(!st)) { + st = rrdset_create("ipv4", "tcppackets", NULL, "tcp", NULL, "IPv4 TCP Packets", + "packets/s", + 2600, update_every, RRDSET_TYPE_LINE); + + rrddim_add(st, "InSegs", "received", 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "OutSegs", "sent", -1, 1, RRDDIM_INCREMENTAL); + } else + rrdset_next(st); + + rrddim_set(st, "InSegs", tcpstat.tcps_rcvtotal); + rrddim_set(st, "OutSegs", tcpstat.tcps_sndtotal); + rrdset_done(st); + } + + // -------------------------------------------------------------------- + + if (likely(do_tcp_errors)) { + st = rrdset_find("ipv4.tcperrors"); + if (unlikely(!st)) { + st = rrdset_create("ipv4", "tcperrors", NULL, "tcp", NULL, "IPv4 TCP Errors", + "packets/s", + 2700, update_every, RRDSET_TYPE_LINE); + st->isdetail = 1; + + rrddim_add(st, "InErrs", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "InCsumErrors", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "RetransSegs", NULL, -1, 1, RRDDIM_INCREMENTAL); + } else + rrdset_next(st); + + rrddim_set(st, "InErrs", tcpstat.tcps_rcvbadoff + tcpstat.tcps_rcvshort); + rrddim_set(st, "InCsumErrors", tcpstat.tcps_rcvbadsum); + rrddim_set(st, "RetransSegs", tcpstat.tcps_sndrexmitpack); + rrdset_done(st); + } + + // -------------------------------------------------------------------- + + if (likely(do_tcp_handshake)) { + st = rrdset_find("ipv4.tcphandshake"); + if (unlikely(!st)) { + st = rrdset_create("ipv4", "tcphandshake", NULL, "tcp", NULL, + "IPv4 TCP Handshake Issues", + "events/s", 2900, update_every, RRDSET_TYPE_LINE); + st->isdetail = 1; + + rrddim_add(st, "EstabResets", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "ActiveOpens", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "PassiveOpens", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "AttemptFails", NULL, 1, 1, RRDDIM_INCREMENTAL); + } else + rrdset_next(st); + + rrddim_set(st, "EstabResets", tcpstat.tcps_drops); + rrddim_set(st, "ActiveOpens", tcpstat.tcps_connattempt); + rrddim_set(st, "PassiveOpens", tcpstat.tcps_accepts); + rrddim_set(st, "AttemptFails", tcpstat.tcps_conndrops); + rrdset_done(st); + } + + // -------------------------------------------------------------------- + + if (do_tcpext_connaborts == CONFIG_ONDEMAND_YES || (do_tcpext_connaborts == CONFIG_ONDEMAND_ONDEMAND && (tcpstat.tcps_rcvpackafterwin || tcpstat.tcps_rcvafterclose || tcpstat.tcps_rcvmemdrop || tcpstat.tcps_persistdrop))) { + do_tcpext_connaborts = CONFIG_ONDEMAND_YES; + st = rrdset_find("ipv4.tcpconnaborts"); + if (unlikely(!st)) { + st = rrdset_create("ipv4", "tcpconnaborts", NULL, "tcp", NULL, "TCP Connection Aborts", "connections/s", 3010, update_every, RRDSET_TYPE_LINE); + + rrddim_add(st, "TCPAbortOnData", "baddata", 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "TCPAbortOnClose", "userclosed", 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "TCPAbortOnMemory", "nomemory", 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "TCPAbortOnTimeout", "timeout", 1, 1, RRDDIM_INCREMENTAL); + } + else rrdset_next(st); + + rrddim_set(st, "TCPAbortOnData", tcpstat.tcps_rcvpackafterwin); + rrddim_set(st, "TCPAbortOnClose", tcpstat.tcps_rcvafterclose); + rrddim_set(st, "TCPAbortOnMemory", tcpstat.tcps_rcvmemdrop); + rrddim_set(st, "TCPAbortOnTimeout", tcpstat.tcps_persistdrop); + rrdset_done(st); + } + + // -------------------------------------------------------------------- + + if (do_tcpext_ofo == CONFIG_ONDEMAND_YES || (do_tcpext_ofo == CONFIG_ONDEMAND_ONDEMAND && tcpstat.tcps_rcvoopack)) { + do_tcpext_ofo = CONFIG_ONDEMAND_YES; + st = rrdset_find("ipv4.tcpofo"); + if (unlikely(!st)) { + st = rrdset_create("ipv4", "tcpofo", NULL, "tcp", NULL, "TCP Out-Of-Order Queue", "packets/s", 3050, update_every, RRDSET_TYPE_LINE); + + rrddim_add(st, "TCPOFOQueue", "inqueue", 1, 1, RRDDIM_INCREMENTAL); + } + else rrdset_next(st); + + rrddim_set(st, "TCPOFOQueue", tcpstat.tcps_rcvoopack); + rrdset_done(st); + } + + // -------------------------------------------------------------------- + + if (do_tcpext_syscookies == CONFIG_ONDEMAND_YES || (do_tcpext_syscookies == CONFIG_ONDEMAND_ONDEMAND && (tcpstat.tcps_sc_sendcookie || tcpstat.tcps_sc_recvcookie || tcpstat.tcps_sc_zonefail))) { + do_tcpext_syscookies = CONFIG_ONDEMAND_YES; + + st = rrdset_find("ipv4.tcpsyncookies"); + if (unlikely(!st)) { + st = rrdset_create("ipv4", "tcpsyncookies", NULL, "tcp", NULL, "TCP SYN Cookies", "packets/s", 3100, update_every, RRDSET_TYPE_LINE); + + rrddim_add(st, "SyncookiesRecv", "received", 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "SyncookiesSent", "sent", -1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "SyncookiesFailed", "failed", -1, 1, RRDDIM_INCREMENTAL); + } + else rrdset_next(st); + + rrddim_set(st, "SyncookiesRecv", tcpstat.tcps_sc_recvcookie); + rrddim_set(st, "SyncookiesSent", tcpstat.tcps_sc_sendcookie); + rrddim_set(st, "SyncookiesFailed", tcpstat.tcps_sc_zonefail); + rrdset_done(st); + } + + // -------------------------------------------------------------------- + + if (do_ecn == CONFIG_ONDEMAND_YES || (do_ecn == CONFIG_ONDEMAND_ONDEMAND && (tcpstat.tcps_ecn_recv_ce || tcpstat.tcps_ecn_not_supported))) { + do_ecn = CONFIG_ONDEMAND_YES; + st = rrdset_find("ipv4.ecnpkts"); + if (unlikely(!st)) { + st = rrdset_create("ipv4", "ecnpkts", NULL, "ecn", NULL, "IPv4 ECN Statistics", "packets/s", 8700, update_every, RRDSET_TYPE_LINE); + st->isdetail = 1; + + rrddim_add(st, "InCEPkts", "CEP", 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "InNoECTPkts", "NoECTP", -1, 1, RRDDIM_INCREMENTAL); + } + else rrdset_next(st); + + rrddim_set(st, "InCEPkts", tcpstat.tcps_ecn_recv_ce); + rrddim_set(st, "InNoECTPkts", tcpstat.tcps_ecn_not_supported); + rrdset_done(st); + } + + } + } + + // -------------------------------------------------------------------- + + // see http://net-snmp.sourceforge.net/docs/mibs/udp.html + if (likely(do_udp_packets || do_udp_errors)) { + if (unlikely(GETSYSCTL("net.inet.udp.stats", udpstat))) { + do_udp_packets = 0; + error("DISABLED: ipv4.udppackets"); + do_udp_errors = 0; + error("DISABLED: ipv4.udperrors"); + } else { + if (likely(do_udp_packets)) { + st = rrdset_find("ipv4.udppackets"); + if (unlikely(!st)) { + st = rrdset_create("ipv4", "udppackets", NULL, "udp", NULL, "IPv4 UDP Packets", + "packets/s", 2601, update_every, RRDSET_TYPE_LINE); + + rrddim_add(st, "InDatagrams", "received", 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "OutDatagrams", "sent", -1, 1, RRDDIM_INCREMENTAL); + } else + rrdset_next(st); + + rrddim_set(st, "InDatagrams", udpstat.udps_ipackets); + rrddim_set(st, "OutDatagrams", udpstat.udps_opackets); + rrdset_done(st); + } + + // -------------------------------------------------------------------- + + if (likely(do_udp_errors)) { + st = rrdset_find("ipv4.udperrors"); + if (unlikely(!st)) { + st = rrdset_create("ipv4", "udperrors", NULL, "udp", NULL, "IPv4 UDP Errors", "events/s", + 2701, update_every, RRDSET_TYPE_LINE); + st->isdetail = 1; + + rrddim_add(st, "RcvbufErrors", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "InErrors", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "NoPorts", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "InCsumErrors", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "IgnoredMulti", NULL, 1, 1, RRDDIM_INCREMENTAL); + } else + rrdset_next(st); + + rrddim_set(st, "InErrors", udpstat.udps_hdrops + udpstat.udps_badlen); + rrddim_set(st, "NoPorts", udpstat.udps_noport); + rrddim_set(st, "RcvbufErrors", udpstat.udps_fullsock); + rrddim_set(st, "InCsumErrors", udpstat.udps_badsum + udpstat.udps_nosum); + rrddim_set(st, "IgnoredMulti", udpstat.udps_filtermcast); + rrdset_done(st); + } + } + } + + // -------------------------------------------------------------------- + + if (likely(do_icmp_packets || do_icmpmsg)) { + if (unlikely(GETSYSCTL("net.inet.icmp.stats", icmpstat))) { + do_icmp_packets = 0; + error("DISABLED: ipv4.icmp"); + error("DISABLED: ipv4.icmp_errors"); + do_icmpmsg = 0; + error("DISABLED: ipv4.icmpmsg"); + } else { + for (i = 0; i <= ICMP_MAXTYPE; i++) { + icmp_total.msgs_in += icmpstat.icps_inhist[i]; + icmp_total.msgs_out += icmpstat.icps_outhist[i]; + } + icmp_total.msgs_in += icmpstat.icps_badcode + icmpstat.icps_badlen + icmpstat.icps_checksum + icmpstat.icps_tooshort; + + // -------------------------------------------------------------------- + + if (likely(do_icmp_packets)) { + st = rrdset_find("ipv4.icmp"); + if (unlikely(!st)) { + st = rrdset_create("ipv4", "icmp", NULL, "icmp", NULL, "IPv4 ICMP Packets", "packets/s", + 2602, + update_every, RRDSET_TYPE_LINE); + + rrddim_add(st, "InMsgs", "received", 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "OutMsgs", "sent", -1, 1, RRDDIM_INCREMENTAL); + } else + rrdset_next(st); + + rrddim_set(st, "InMsgs", icmp_total.msgs_in); + rrddim_set(st, "OutMsgs", icmp_total.msgs_out); + + rrdset_done(st); + + // -------------------------------------------------------------------- + + st = rrdset_find("ipv4.icmp_errors"); + if (unlikely(!st)) { + st = rrdset_create("ipv4", "icmp_errors", NULL, "icmp", NULL, "IPv4 ICMP Errors", + "packets/s", + 2603, update_every, RRDSET_TYPE_LINE); + + rrddim_add(st, "InErrors", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "OutErrors", NULL, -1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "InCsumErrors", NULL, 1, 1, RRDDIM_INCREMENTAL); + } else + rrdset_next(st); + + rrddim_set(st, "InErrors", icmpstat.icps_badcode + icmpstat.icps_badlen + icmpstat.icps_checksum + icmpstat.icps_tooshort); + rrddim_set(st, "OutErrors", icmpstat.icps_error); + rrddim_set(st, "InCsumErrors", icmpstat.icps_checksum); + + rrdset_done(st); + } + + // -------------------------------------------------------------------- + + if (likely(do_icmpmsg)) { + st = rrdset_find("ipv4.icmpmsg"); + if (unlikely(!st)) { + st = rrdset_create("ipv4", "icmpmsg", NULL, "icmp", NULL, "IPv4 ICMP Messsages", + "packets/s", 2604, update_every, RRDSET_TYPE_LINE); + + rrddim_add(st, "InEchoReps", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "OutEchoReps", NULL, -1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "InEchos", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "OutEchos", NULL, -1, 1, RRDDIM_INCREMENTAL); + } else + rrdset_next(st); + + rrddim_set(st, "InEchoReps", icmpstat.icps_inhist[ICMP_ECHOREPLY]); + rrddim_set(st, "OutEchoReps", icmpstat.icps_outhist[ICMP_ECHOREPLY]); + rrddim_set(st, "InEchos", icmpstat.icps_inhist[ICMP_ECHO]); + rrddim_set(st, "OutEchos", icmpstat.icps_outhist[ICMP_ECHO]); + + rrdset_done(st); + } + } + } + + // -------------------------------------------------------------------- + + // see also http://net-snmp.sourceforge.net/docs/mibs/ip.html + if (likely(do_ip_packets || do_ip_fragsout || do_ip_fragsin || do_ip_errors)) { + if (unlikely(GETSYSCTL("net.inet.ip.stats", ipstat))) { + do_ip_packets = 0; + error("DISABLED: ipv4.packets"); + do_ip_fragsout = 0; + error("DISABLED: ipv4.fragsout"); + do_ip_fragsin = 0; + error("DISABLED: ipv4.fragsin"); + do_ip_errors = 0; + error("DISABLED: ipv4.errors"); + } else { + if (likely(do_ip_packets)) { + st = rrdset_find("ipv4.packets"); + if (unlikely(!st)) { + st = rrdset_create("ipv4", "packets", NULL, "packets", NULL, "IPv4 Packets", "packets/s", + 3000, update_every, RRDSET_TYPE_LINE); + + rrddim_add(st, "InReceives", "received", 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "OutRequests", "sent", -1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "ForwDatagrams", "forwarded", 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "InDelivers", "delivered", 1, 1, RRDDIM_INCREMENTAL); + } else + rrdset_next(st); + + rrddim_set(st, "OutRequests", ipstat.ips_localout); + rrddim_set(st, "InReceives", ipstat.ips_total); + rrddim_set(st, "ForwDatagrams", ipstat.ips_forward); + rrddim_set(st, "InDelivers", ipstat.ips_delivered); + rrdset_done(st); + } + + // -------------------------------------------------------------------- + + if (likely(do_ip_fragsout)) { + st = rrdset_find("ipv4.fragsout"); + if (unlikely(!st)) { + st = rrdset_create("ipv4", "fragsout", NULL, "fragments", NULL, "IPv4 Fragments Sent", + "packets/s", 3010, update_every, RRDSET_TYPE_LINE); + st->isdetail = 1; + + rrddim_add(st, "FragOKs", "ok", 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "FragFails", "failed", -1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "FragCreates", "created", 1, 1, RRDDIM_INCREMENTAL); + } else + rrdset_next(st); + + rrddim_set(st, "FragOKs", ipstat.ips_fragmented); + rrddim_set(st, "FragFails", ipstat.ips_cantfrag); + rrddim_set(st, "FragCreates", ipstat.ips_ofragments); + rrdset_done(st); + } + + // -------------------------------------------------------------------- + + if (likely(do_ip_fragsin)) { + st = rrdset_find("ipv4.fragsin"); + if (unlikely(!st)) { + st = rrdset_create("ipv4", "fragsin", NULL, "fragments", NULL, + "IPv4 Fragments Reassembly", + "packets/s", 3011, update_every, RRDSET_TYPE_LINE); + st->isdetail = 1; + + rrddim_add(st, "ReasmOKs", "ok", 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "ReasmFails", "failed", -1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "ReasmReqds", "all", 1, 1, RRDDIM_INCREMENTAL); + } else + rrdset_next(st); + + rrddim_set(st, "ReasmOKs", ipstat.ips_fragments); + rrddim_set(st, "ReasmFails", ipstat.ips_fragdropped); + rrddim_set(st, "ReasmReqds", ipstat.ips_reassembled); + rrdset_done(st); + } + + // -------------------------------------------------------------------- + + if (likely(do_ip_errors)) { + st = rrdset_find("ipv4.errors"); + if (unlikely(!st)) { + st = rrdset_create("ipv4", "errors", NULL, "errors", NULL, "IPv4 Errors", "packets/s", + 3002, + update_every, RRDSET_TYPE_LINE); + st->isdetail = 1; + + rrddim_add(st, "InDiscards", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "OutDiscards", NULL, -1, 1, RRDDIM_INCREMENTAL); + + rrddim_add(st, "InHdrErrors", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "OutNoRoutes", NULL, -1, 1, RRDDIM_INCREMENTAL); + + rrddim_add(st, "InAddrErrors", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "InUnknownProtos", NULL, 1, 1, RRDDIM_INCREMENTAL); + } else + rrdset_next(st); + + rrddim_set(st, "InDiscards", ipstat.ips_badsum + ipstat.ips_tooshort + ipstat.ips_toosmall + ipstat.ips_toolong); + rrddim_set(st, "OutDiscards", ipstat.ips_odropped); + rrddim_set(st, "InHdrErrors", ipstat.ips_badhlen + ipstat.ips_badlen + ipstat.ips_badoptions + ipstat.ips_badvers); + rrddim_set(st, "InAddrErrors", ipstat.ips_badaddr); + rrddim_set(st, "InUnknownProtos", ipstat.ips_noproto); + rrddim_set(st, "OutNoRoutes", ipstat.ips_noroute); + rrdset_done(st); + } + } + } + + // -------------------------------------------------------------------- + + if (likely(do_ip6_packets || do_ip6_fragsout || do_ip6_fragsin || do_ip6_errors)) { + if (unlikely(GETSYSCTL("net.inet6.ip6.stats", ip6stat))) { + do_ip6_packets = 0; + error("DISABLED: ipv6.packets"); + do_ip6_fragsout = 0; + error("DISABLED: ipv6.fragsout"); + do_ip6_fragsin = 0; + error("DISABLED: ipv6.fragsin"); + do_ip6_errors = 0; + error("DISABLED: ipv6.errors"); + } else { + if (do_ip6_packets == CONFIG_ONDEMAND_YES || (do_ip6_packets == CONFIG_ONDEMAND_ONDEMAND && + (ip6stat.ip6s_localout || ip6stat.ip6s_total || + ip6stat.ip6s_forward || ip6stat.ip6s_delivered))) { + do_ip6_packets = CONFIG_ONDEMAND_YES; + st = rrdset_find("ipv6.packets"); + if (unlikely(!st)) { + st = rrdset_create("ipv6", "packets", NULL, "packets", NULL, "IPv6 Packets", "packets/s", 3000, + update_every, RRDSET_TYPE_LINE); + + rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "forwarded", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "delivers", NULL, -1, 1, RRDDIM_INCREMENTAL); + } else + rrdset_next(st); + + rrddim_set(st, "sent", ip6stat.ip6s_localout); + rrddim_set(st, "received", ip6stat.ip6s_total); + rrddim_set(st, "forwarded", ip6stat.ip6s_forward); + rrddim_set(st, "delivers", ip6stat.ip6s_delivered); + rrdset_done(st); + } + + // -------------------------------------------------------------------- + + if (do_ip6_fragsout == CONFIG_ONDEMAND_YES || (do_ip6_fragsout == CONFIG_ONDEMAND_ONDEMAND && + (ip6stat.ip6s_fragmented || ip6stat.ip6s_cantfrag || + ip6stat.ip6s_ofragments))) { + do_ip6_fragsout = CONFIG_ONDEMAND_YES; + st = rrdset_find("ipv6.fragsout"); + if (unlikely(!st)) { + st = rrdset_create("ipv6", "fragsout", NULL, "fragments", NULL, "IPv6 Fragments Sent", + "packets/s", 3010, update_every, RRDSET_TYPE_LINE); + st->isdetail = 1; + + rrddim_add(st, "ok", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "failed", NULL, -1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "all", NULL, 1, 1, RRDDIM_INCREMENTAL); + } else + rrdset_next(st); + + rrddim_set(st, "ok", ip6stat.ip6s_fragmented); + rrddim_set(st, "failed", ip6stat.ip6s_cantfrag); + rrddim_set(st, "all", ip6stat.ip6s_ofragments); + rrdset_done(st); + } + + // -------------------------------------------------------------------- + + if (do_ip6_fragsin == CONFIG_ONDEMAND_YES || (do_ip6_fragsin == CONFIG_ONDEMAND_ONDEMAND && + (ip6stat.ip6s_reassembled || ip6stat.ip6s_fragdropped || + ip6stat.ip6s_fragtimeout || ip6stat.ip6s_fragments))) { + do_ip6_fragsin = CONFIG_ONDEMAND_YES; + st = rrdset_find("ipv6.fragsin"); + if (unlikely(!st)) { + st = rrdset_create("ipv6", "fragsin", NULL, "fragments", NULL, "IPv6 Fragments Reassembly", + "packets/s", 3011, update_every, RRDSET_TYPE_LINE); + st->isdetail = 1; + + rrddim_add(st, "ok", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "failed", NULL, -1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "timeout", NULL, -1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "all", NULL, 1, 1, RRDDIM_INCREMENTAL); + } else + rrdset_next(st); + + rrddim_set(st, "ok", ip6stat.ip6s_reassembled); + rrddim_set(st, "failed", ip6stat.ip6s_fragdropped); + rrddim_set(st, "timeout", ip6stat.ip6s_fragtimeout); + rrddim_set(st, "all", ip6stat.ip6s_fragments); + rrdset_done(st); + } + + // -------------------------------------------------------------------- + + if (do_ip6_errors == CONFIG_ONDEMAND_YES || (do_ip6_errors == CONFIG_ONDEMAND_ONDEMAND && ( + ip6stat.ip6s_toosmall || + ip6stat.ip6s_odropped || + ip6stat.ip6s_badoptions || + ip6stat.ip6s_badvers || + ip6stat.ip6s_exthdrtoolong || + ip6stat.ip6s_sources_none || + ip6stat.ip6s_tooshort || + ip6stat.ip6s_cantforward || + ip6stat.ip6s_noroute))) { + do_ip6_errors = CONFIG_ONDEMAND_YES; + st = rrdset_find("ipv6.errors"); + if (unlikely(!st)) { + st = rrdset_create("ipv6", "errors", NULL, "errors", NULL, "IPv6 Errors", "packets/s", 3002, + update_every, RRDSET_TYPE_LINE); + st->isdetail = 1; + + rrddim_add(st, "InDiscards", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "OutDiscards", NULL, -1, 1, RRDDIM_INCREMENTAL); + + rrddim_add(st, "InHdrErrors", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "InAddrErrors", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "InTruncatedPkts", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "InNoRoutes", NULL, 1, 1, RRDDIM_INCREMENTAL); + + rrddim_add(st, "OutNoRoutes", NULL, -1, 1, RRDDIM_INCREMENTAL); + } else + rrdset_next(st); + + rrddim_set(st, "InDiscards", ip6stat.ip6s_toosmall); + rrddim_set(st, "OutDiscards", ip6stat.ip6s_odropped); + + rrddim_set(st, "InHdrErrors", + ip6stat.ip6s_badoptions + ip6stat.ip6s_badvers + ip6stat.ip6s_exthdrtoolong); + rrddim_set(st, "InAddrErrors", ip6stat.ip6s_sources_none); + rrddim_set(st, "InTruncatedPkts", ip6stat.ip6s_tooshort); + rrddim_set(st, "InNoRoutes", ip6stat.ip6s_cantforward); + + rrddim_set(st, "OutNoRoutes", ip6stat.ip6s_noroute); + rrdset_done(st); + } + } + } + + // -------------------------------------------------------------------- + + if (likely(do_icmp6 || do_icmp6_redir || do_icmp6_errors || do_icmp6_echos || do_icmp6_router || do_icmp6_neighbor || do_icmp6_types)) { + if (unlikely(GETSYSCTL("net.inet6.icmp6.stats", icmp6stat))) { + do_icmp6 = 0; + error("DISABLED: ipv6.icmp"); + } else { + for (i = 0; i <= ICMP6_MAXTYPE; i++) { + icmp6_total.msgs_in += icmp6stat.icp6s_inhist[i]; + icmp6_total.msgs_out += icmp6stat.icp6s_outhist[i]; + } + icmp6_total.msgs_in += icmp6stat.icp6s_badcode + icmp6stat.icp6s_badlen + icmp6stat.icp6s_checksum + icmp6stat.icp6s_tooshort; + if (do_icmp6 == CONFIG_ONDEMAND_YES || (do_icmp6 == CONFIG_ONDEMAND_ONDEMAND && (icmp6_total.msgs_in || icmp6_total.msgs_out))) { + do_icmp6 = CONFIG_ONDEMAND_YES; + st = rrdset_find("ipv6.icmp"); + if (unlikely(!st)) { + st = rrdset_create("ipv6", "icmp", NULL, "icmp", NULL, "IPv6 ICMP Messages", + "messages/s", 10000, update_every, RRDSET_TYPE_LINE); + + rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL); + } else + rrdset_next(st); + + rrddim_set(st, "sent", icmp6_total.msgs_in); + rrddim_set(st, "received", icmp6_total.msgs_out); + rrdset_done(st); + } + + // -------------------------------------------------------------------- + + if (do_icmp6_redir == CONFIG_ONDEMAND_YES || (do_icmp6_redir == CONFIG_ONDEMAND_ONDEMAND && (icmp6stat.icp6s_inhist[ND_REDIRECT] || icmp6stat.icp6s_outhist[ND_REDIRECT]))) { + do_icmp6_redir = CONFIG_ONDEMAND_YES; + st = rrdset_find("ipv6.icmpredir"); + if (unlikely(!st)) { + st = rrdset_create("ipv6", "icmpredir", NULL, "icmp", NULL, "IPv6 ICMP Redirects", + "redirects/s", 10050, update_every, RRDSET_TYPE_LINE); + + rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL); + } else + rrdset_next(st); + + rrddim_set(st, "sent", icmp6stat.icp6s_inhist[ND_REDIRECT]); + rrddim_set(st, "received", icmp6stat.icp6s_outhist[ND_REDIRECT]); + rrdset_done(st); + } + + // -------------------------------------------------------------------- + + if (do_icmp6_errors == CONFIG_ONDEMAND_YES || (do_icmp6_errors == CONFIG_ONDEMAND_ONDEMAND && ( + icmp6stat.icp6s_badcode || + icmp6stat.icp6s_badlen || + icmp6stat.icp6s_checksum || + icmp6stat.icp6s_tooshort || + icmp6stat.icp6s_error || + icmp6stat.icp6s_inhist[ICMP6_DST_UNREACH] || + icmp6stat.icp6s_inhist[ICMP6_TIME_EXCEEDED] || + icmp6stat.icp6s_inhist[ICMP6_PARAM_PROB] || + icmp6stat.icp6s_outhist[ICMP6_DST_UNREACH] || + icmp6stat.icp6s_outhist[ICMP6_TIME_EXCEEDED] || + icmp6stat.icp6s_outhist[ICMP6_PARAM_PROB]))) { + do_icmp6_errors = CONFIG_ONDEMAND_YES; + st = rrdset_find("ipv6.icmperrors"); + if (unlikely(!st)) { + st = rrdset_create("ipv6", "icmperrors", NULL, "icmp", NULL, "IPv6 ICMP Errors", "errors/s", 10100, update_every, RRDSET_TYPE_LINE); + + rrddim_add(st, "InErrors", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "OutErrors", NULL, -1, 1, RRDDIM_INCREMENTAL); + + rrddim_add(st, "InCsumErrors", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "InDestUnreachs", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "InPktTooBigs", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "InTimeExcds", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "InParmProblems", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "OutDestUnreachs", NULL, -1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "OutTimeExcds", NULL, -1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "OutParmProblems", NULL, -1, 1, RRDDIM_INCREMENTAL); + } else + rrdset_next(st); + + rrddim_set(st, "InErrors", icmp6stat.icp6s_badcode + icmp6stat.icp6s_badlen + icmp6stat.icp6s_checksum + icmp6stat.icp6s_tooshort); + rrddim_set(st, "OutErrors", icmp6stat.icp6s_error); + rrddim_set(st, "InCsumErrors", icmp6stat.icp6s_checksum); + rrddim_set(st, "InDestUnreachs", icmp6stat.icp6s_inhist[ICMP6_DST_UNREACH]); + rrddim_set(st, "InPktTooBigs", icmp6stat.icp6s_badlen); + rrddim_set(st, "InTimeExcds", icmp6stat.icp6s_inhist[ICMP6_TIME_EXCEEDED]); + rrddim_set(st, "InParmProblems", icmp6stat.icp6s_inhist[ICMP6_PARAM_PROB]); + rrddim_set(st, "OutDestUnreachs", icmp6stat.icp6s_outhist[ICMP6_DST_UNREACH]); + rrddim_set(st, "OutTimeExcds", icmp6stat.icp6s_outhist[ICMP6_TIME_EXCEEDED]); + rrddim_set(st, "OutParmProblems", icmp6stat.icp6s_outhist[ICMP6_PARAM_PROB]); + rrdset_done(st); + } + + // -------------------------------------------------------------------- + + if (do_icmp6_echos == CONFIG_ONDEMAND_YES || (do_icmp6_echos == CONFIG_ONDEMAND_ONDEMAND && ( + icmp6stat.icp6s_inhist[ICMP6_ECHO_REQUEST] || + icmp6stat.icp6s_outhist[ICMP6_ECHO_REQUEST] || + icmp6stat.icp6s_inhist[ICMP6_ECHO_REPLY] || + icmp6stat.icp6s_outhist[ICMP6_ECHO_REPLY]))) { + do_icmp6_echos = CONFIG_ONDEMAND_YES; + st = rrdset_find("ipv6.icmpechos"); + if (unlikely(!st)) { + st = rrdset_create("ipv6", "icmpechos", NULL, "icmp", NULL, "IPv6 ICMP Echo", "messages/s", 10200, update_every, RRDSET_TYPE_LINE); + + rrddim_add(st, "InEchos", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "OutEchos", NULL, -1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "InEchoReplies", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "OutEchoReplies", NULL, -1, 1, RRDDIM_INCREMENTAL); + } else + rrdset_next(st); + + rrddim_set(st, "InEchos", icmp6stat.icp6s_inhist[ICMP6_ECHO_REQUEST]); + rrddim_set(st, "OutEchos", icmp6stat.icp6s_outhist[ICMP6_ECHO_REQUEST]); + rrddim_set(st, "InEchoReplies", icmp6stat.icp6s_inhist[ICMP6_ECHO_REPLY]); + rrddim_set(st, "OutEchoReplies", icmp6stat.icp6s_outhist[ICMP6_ECHO_REPLY]); + rrdset_done(st); + } + + // -------------------------------------------------------------------- + + if (do_icmp6_router == CONFIG_ONDEMAND_YES || (do_icmp6_router == CONFIG_ONDEMAND_ONDEMAND && ( + icmp6stat.icp6s_inhist[ND_ROUTER_SOLICIT] || + icmp6stat.icp6s_outhist[ND_ROUTER_SOLICIT] || + icmp6stat.icp6s_inhist[ND_ROUTER_ADVERT] || + icmp6stat.icp6s_outhist[ND_ROUTER_ADVERT]))) { + do_icmp6_router = CONFIG_ONDEMAND_YES; + st = rrdset_find("ipv6.icmprouter"); + if (unlikely(!st)) { + st = rrdset_create("ipv6", "icmprouter", NULL, "icmp", NULL, "IPv6 Router Messages", "messages/s", 10400, update_every, RRDSET_TYPE_LINE); + + rrddim_add(st, "InSolicits", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "OutSolicits", NULL, -1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "InAdvertisements", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "OutAdvertisements", NULL, -1, 1, RRDDIM_INCREMENTAL); + } else + rrdset_next(st); + + rrddim_set(st, "InSolicits", icmp6stat.icp6s_inhist[ND_ROUTER_SOLICIT]); + rrddim_set(st, "OutSolicits", icmp6stat.icp6s_outhist[ND_ROUTER_SOLICIT]); + rrddim_set(st, "InAdvertisements", icmp6stat.icp6s_inhist[ND_ROUTER_ADVERT]); + rrddim_set(st, "OutAdvertisements", icmp6stat.icp6s_outhist[ND_ROUTER_ADVERT]); + rrdset_done(st); + } + + // -------------------------------------------------------------------- + + if (do_icmp6_neighbor == CONFIG_ONDEMAND_YES || (do_icmp6_neighbor == CONFIG_ONDEMAND_ONDEMAND && ( + icmp6stat.icp6s_inhist[ND_NEIGHBOR_SOLICIT] || + icmp6stat.icp6s_outhist[ND_NEIGHBOR_SOLICIT] || + icmp6stat.icp6s_inhist[ND_NEIGHBOR_ADVERT] || + icmp6stat.icp6s_outhist[ND_NEIGHBOR_ADVERT]))) { + do_icmp6_neighbor = CONFIG_ONDEMAND_YES; + st = rrdset_find("ipv6.icmpneighbor"); + if (unlikely(!st)) { + st = rrdset_create("ipv6", "icmpneighbor", NULL, "icmp", NULL, "IPv6 Neighbor Messages", "messages/s", 10500, update_every, RRDSET_TYPE_LINE); + + rrddim_add(st, "InSolicits", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "OutSolicits", NULL, -1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "InAdvertisements", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "OutAdvertisements", NULL, -1, 1, RRDDIM_INCREMENTAL); + } else + rrdset_next(st); + + rrddim_set(st, "InSolicits", icmp6stat.icp6s_inhist[ND_NEIGHBOR_SOLICIT]); + rrddim_set(st, "OutSolicits", icmp6stat.icp6s_outhist[ND_NEIGHBOR_SOLICIT]); + rrddim_set(st, "InAdvertisements", icmp6stat.icp6s_inhist[ND_NEIGHBOR_ADVERT]); + rrddim_set(st, "OutAdvertisements", icmp6stat.icp6s_outhist[ND_NEIGHBOR_ADVERT]); + rrdset_done(st); + } + + // -------------------------------------------------------------------- + + if (do_icmp6_types == CONFIG_ONDEMAND_YES || (do_icmp6_types == CONFIG_ONDEMAND_ONDEMAND && ( + icmp6stat.icp6s_inhist[1] || + icmp6stat.icp6s_inhist[128] || + icmp6stat.icp6s_inhist[129] || + icmp6stat.icp6s_inhist[136] || + icmp6stat.icp6s_outhist[1] || + icmp6stat.icp6s_outhist[128] || + icmp6stat.icp6s_outhist[129] || + icmp6stat.icp6s_outhist[133] || + icmp6stat.icp6s_outhist[135] || + icmp6stat.icp6s_outhist[136]))) { + do_icmp6_types = CONFIG_ONDEMAND_YES; + st = rrdset_find("ipv6.icmptypes"); + if (unlikely(!st)) { + st = rrdset_create("ipv6", "icmptypes", NULL, "icmp", NULL, "IPv6 ICMP Types", + "messages/s", 10700, update_every, RRDSET_TYPE_LINE); + + rrddim_add(st, "InType1", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "InType128", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "InType129", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "InType136", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "OutType1", NULL, -1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "OutType128", NULL, -1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "OutType129", NULL, -1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "OutType133", NULL, -1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "OutType135", NULL, -1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "OutType143", NULL, -1, 1, RRDDIM_INCREMENTAL); + } else + rrdset_next(st); + + rrddim_set(st, "InType1", icmp6stat.icp6s_inhist[1]); + rrddim_set(st, "InType128", icmp6stat.icp6s_inhist[128]); + rrddim_set(st, "InType129", icmp6stat.icp6s_inhist[129]); + rrddim_set(st, "InType136", icmp6stat.icp6s_inhist[136]); + rrddim_set(st, "OutType1", icmp6stat.icp6s_outhist[1]); + rrddim_set(st, "OutType128", icmp6stat.icp6s_outhist[128]); + rrddim_set(st, "OutType129", icmp6stat.icp6s_outhist[129]); + rrddim_set(st, "OutType133", icmp6stat.icp6s_outhist[133]); + rrddim_set(st, "OutType135", icmp6stat.icp6s_outhist[135]); + rrddim_set(st, "OutType143", icmp6stat.icp6s_outhist[143]); + rrdset_done(st); + } + } + } + + // -------------------------------------------------------------------- + + if (likely(do_uptime)) { + if (unlikely(GETSYSCTL("kern.boottime", boot_time))) { + do_uptime = 0; + error("DISABLED: system.uptime"); + } else { + clock_gettime(CLOCK_REALTIME, &cur_time); + st = rrdset_find("system.uptime"); + + if(unlikely(!st)) { + st = rrdset_create("system", "uptime", NULL, "uptime", NULL, "System Uptime", "seconds", 1000, update_every, RRDSET_TYPE_LINE); + rrddim_add(st, "uptime", NULL, 1, 1, RRDDIM_ABSOLUTE); + } + else rrdset_next(st); + + rrddim_set(st, "uptime", cur_time.tv_sec - boot_time.tv_sec); + rrdset_done(st); + } + } + + return 0; +} + +int getsysctl(const char *name, void *ptr, size_t len) +{ + size_t nlen = len; + + if (unlikely(sysctlbyname(name, ptr, &nlen, NULL, 0) == -1)) { + error("MACOS: sysctl(%s...) failed: %s", name, strerror(errno)); + return 1; + } + if (unlikely(nlen != len)) { + error("MACOS: sysctl(%s...) expected %lu, got %lu", name, (unsigned long)len, (unsigned long)nlen); + return 1; + } + return 0; +} diff --git a/src/main.c b/src/main.c index 3e6aa5046..ca134fcb0 100644 --- a/src/main.c +++ b/src/main.c @@ -7,36 +7,30 @@ void netdata_cleanup_and_exit(int ret) { error_log_limit_unlimited(); - info("Called: netdata_cleanup_and_exit()"); -#ifdef NETDATA_INTERNAL_CHECKS - rrdset_free_all(); -#else + debug(D_EXIT, "Called: netdata_cleanup_and_exit()"); + + // save the database rrdset_save_all(); -#endif - // kill_childs(); + // unlink the pid if(pidfile[0]) { if(unlink(pidfile) != 0) error("Cannot unlink pidfile '%s'.", pidfile); } - info("NetData exiting. Bye bye..."); - exit(ret); -} - -struct netdata_static_thread { - char *name; - - char *config_section; - char *config_name; +#ifdef NETDATA_INTERNAL_CHECKS + // kill all childs + //kill_childs(); - int enabled; + // free database + rrdset_free_all(); +#endif - pthread_t *thread; + info("netdata exiting. Bye bye..."); + exit(ret); +} - void (*init_routine) (void); - void *(*start_routine) (void *); -} static_threads[] = { +struct netdata_static_thread static_threads[] = { #ifdef INTERNAL_PLUGIN_NFACCT // nfacct requires root access // so, we build it as an external plugin with setuid to root @@ -45,9 +39,17 @@ struct netdata_static_thread { {"tc", "plugins", "tc", 1, NULL, NULL, tc_main}, {"idlejitter", "plugins", "idlejitter", 1, NULL, NULL, cpuidlejitter_main}, +#if defined(__FreeBSD__) + {"freebsd", "plugins", "freebsd", 1, NULL, NULL, freebsd_main}, +#elif defined(__APPLE__) + {"macos", "plugins", "macos", 1, NULL, NULL, macos_main}, +#else {"proc", "plugins", "proc", 1, NULL, NULL, proc_main}, + {"diskspace", "plugins", "diskspace", 1, NULL, NULL, proc_diskspace_main}, +#endif /* __FreeBSD__, __APPLE__*/ {"cgroups", "plugins", "cgroups", 1, NULL, NULL, cgroups_main}, {"check", "plugins", "checks", 0, NULL, NULL, checks_main}, + {"backends", NULL, NULL, 1, NULL, NULL, backends_main}, {"health", NULL, NULL, 1, NULL, NULL, health_main}, {"plugins.d", NULL, NULL, 1, NULL, NULL, pluginsd_main}, {"web", NULL, NULL, 1, NULL, NULL, socket_listen_main_multi_threaded}, @@ -149,67 +151,80 @@ int killpid(pid_t pid, int sig) void kill_childs() { + error_log_limit_unlimited(); + siginfo_t info; struct web_client *w; for(w = web_clients; w ; w = w->next) { - debug(D_EXIT, "Stopping web client %s", w->client_ip); + info("Stopping web client %s", w->client_ip); pthread_cancel(w->thread); - pthread_join(w->thread, NULL); + // it is detached + // pthread_join(w->thread, NULL); + + w->obsolete = 1; } int i; for (i = 0; static_threads[i].name != NULL ; i++) { - if(static_threads[i].thread) { - debug(D_EXIT, "Stopping %s thread", static_threads[i].name); + if(static_threads[i].enabled) { + info("Stopping %s thread", static_threads[i].name); pthread_cancel(*static_threads[i].thread); - pthread_join(*static_threads[i].thread, NULL); - static_threads[i].thread = NULL; + // it is detached + // pthread_join(*static_threads[i].thread, NULL); + + static_threads[i].enabled = 0; } } if(tc_child_pid) { - info("Killing tc-qos-helper procees"); + info("Killing tc-qos-helper process %d", tc_child_pid); if(killpid(tc_child_pid, SIGTERM) != -1) waitid(P_PID, (id_t) tc_child_pid, &info, WEXITED); + + tc_child_pid = 0; } - tc_child_pid = 0; struct plugind *cd; for(cd = pluginsd_root ; cd ; cd = cd->next) { - debug(D_EXIT, "Stopping %s plugin thread", cd->id); - pthread_cancel(cd->thread); - pthread_join(cd->thread, NULL); - - if(cd->pid && !cd->obsolete) { - debug(D_EXIT, "killing %s plugin process", cd->id); - if(killpid(cd->pid, SIGTERM) != -1) - waitid(P_PID, (id_t) cd->pid, &info, WEXITED); + if(cd->enabled && !cd->obsolete) { + info("Stopping %s plugin thread", cd->id); + pthread_cancel(cd->thread); + + if(cd->pid) { + info("killing %s plugin child process pid %d", cd->id, cd->pid); + if(killpid(cd->pid, SIGTERM) != -1) + waitid(P_PID, (id_t) cd->pid, &info, WEXITED); + + cd->pid = 0; + } + + cd->obsolete = 1; } } // if, for any reason there is any child exited // catch it here + info("Cleaning up an other children"); waitid(P_PID, 0, &info, WEXITED|WNOHANG); - debug(D_EXIT, "All threads/childs stopped."); + info("All threads/childs stopped."); } struct option_def options[] = { - // opt description arg name default value - {'c', "Load alternate configuration file", "config_file", CONFIG_DIR "/" CONFIG_FILENAME}, - {'D', "Disable fork into background", NULL, NULL}, - {'h', "Display help message", NULL, NULL}, - {'P', "File to save a pid while running", "FILE", NULL}, - {'i', "The IP address to listen to.", "address", "All addresses"}, - {'k', "Check daemon configuration.", NULL, NULL}, - {'p', "Port to listen. Can be from 1 to 65535.", "port_number", "19999"}, - {'s', "Path to access host /proc and /sys when running in a container.", "PATH", NULL}, - {'t', "The frequency in seconds, for data collection. \ -Same as 'update every' config file option.", "seconds", "1"}, - {'u', "System username to run as.", "username", "netdata"}, - {'v', "Version of the program", NULL, NULL}, - {'W', "vendor options.", "stacksize=N|unittest|debug_flags=N", NULL}, + // opt description arg name default value + { 'c', "Configuration file to load.", "filename", CONFIG_DIR "/" CONFIG_FILENAME}, + { 'D', "Do not fork. Run in the foreground.", NULL, "run in the background"}, + { 'h', "Display this help message.", NULL, NULL}, + { 'P', "File to save a pid while running.", "filename", "do not save pid to a file"}, + { 'i', "The IP address to listen to.", "IP", "all IP addresses IPv4 and IPv6"}, + { 'k', "Check health configuration and exit.", NULL, NULL}, + { 'p', "API/Web port to use.", "port", "19999"}, + { 's', "Prefix for /proc and /sys (for containers).", "path", "no prefix"}, + { 't', "The internal clock of netdata.", "seconds", "1"}, + { 'u', "Run as user.", "username", "netdata"}, + { 'v', "Print netdata version and exit.", NULL, NULL}, + { 'W', "See Advanced options below.", "options", NULL}, }; void help(int exitcode) { @@ -231,20 +246,63 @@ void help(int exitcode) { } } - fprintf(stream, "SYNOPSIS: netdata [options]\n"); + if(max_len_arg > 30) max_len_arg = 30; + if(max_len_arg < 20) max_len_arg = 20; + + fprintf(stream, "%s", "\n" + " ^\n" + " |.-. .-. .-. .-. . netdata \n" + " | '-' '-' '-' '-' real-time performance monitoring, done right! \n" + " +----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+--->\n" + "\n" + " Copyright (C) 2016-2017, Costa Tsaousis <costa@tsaousis.gr>\n" + " Released under GNU Public License v3 or later.\n" + " All rights reserved.\n" + "\n" + " Home Page : https://my-netdata.io\n" + " Source Code: https://github.com/firehol/netdata\n" + " Wiki / Docs: https://github.com/firehol/netdata/wiki\n" + " Support : https://github.com/firehol/netdata/issues\n" + " License : https://github.com/firehol/netdata/blob/master/LICENSE.md\n" + "\n" + " Twitter : https://twitter.com/linuxnetdata\n" + " Facebook : https://www.facebook.com/linuxnetdata/\n" + "\n" + " netdata is a https://firehol.org project.\n" + "\n" + "\n" + ); + + fprintf(stream, " SYNOPSIS: netdata [options]\n"); fprintf(stream, "\n"); - fprintf(stream, "Options:\n"); + fprintf(stream, " Options:\n\n"); // Output options description. for( i = 0; i < num_opts; i++ ) { fprintf(stream, " -%c %-*s %s", options[i].val, max_len_arg, options[i].arg_name ? options[i].arg_name : "", options[i].description); if(options[i].default_value) { - fprintf(stream, " Default: %s\n", options[i].default_value); + fprintf(stream, "\n %c %-*s Default: %s\n", ' ', max_len_arg, "", options[i].default_value); } else { fprintf(stream, "\n"); } + fprintf(stream, "\n"); } + fprintf(stream, "\n Advanced options:\n\n" + " -W stacksize=N Set the stacksize (in bytes).\n\n" + " -W debug_flags=N Set runtime tracing to debug.log.\n\n" + " -W unittest Run internal unittests and exit.\n\n" + " -W simple-pattern pattern string\n" + " Check if string matches pattern and exit.\n\n" + ); + + fprintf(stream, "\n Signals netdata handles:\n\n" + " - HUP Close and reopen log files.\n" + " - USR1 Save internal DB to disk.\n" + " - USR2 Reload health configuration.\n" + "\n" + ); + fflush(stream); exit(exitcode); } @@ -329,6 +387,9 @@ int main(int argc, char **argv) string_i++; } } + // terminate optstring + optstring[string_i] ='\0'; + optstring[(num_opts *2)] ='\0'; int opt; while( (opt = getopt(argc, argv, optstring)) != -1 ) { @@ -386,10 +447,54 @@ int main(int argc, char **argv) if(unit_test_storage()) exit(1); fprintf(stderr, "\n\nALL TESTS PASSED\n\n"); exit(0); - } else if(strncmp(optarg, stacksize_string, strlen(stacksize_string)) == 0) { + } + else if(strcmp(optarg, "simple-pattern") == 0) { + if(optind + 2 > argc) { + fprintf(stderr, "%s", "\nUSAGE: -W simple-pattern 'pattern' 'string'\n\n" + " Checks if 'pattern' matches the given 'string'.\n" + " - 'pattern' can be one or more space separated words.\n" + " - each 'word' can contain one or more asterisks.\n" + " - words starting with '!' give negative matches.\n" + " - words are processed left to right\n" + "\n" + "Examples:\n" + "\n" + " > match all veth interfaces, except veth0:\n" + "\n" + " -W simple-pattern '!veth0 veth*' 'veth12'\n" + "\n" + "\n" + " > match all *.ext files directly in /path/:\n" + " (this will not match *.ext files in a subdir of /path/)\n" + "\n" + " -W simple-pattern '!/path/*/*.ext /path/*.ext' '/path/test.ext'\n" + "\n" + ); + exit(1); + } + + const char *heystack = argv[optind]; + const char *needle = argv[optind + 1]; + + SIMPLE_PATTERN *p = simple_pattern_create(heystack + , SIMPLE_PATTERN_EXACT); + int ret = simple_pattern_matches(p, needle); + simple_pattern_free(p); + + if(ret) { + fprintf(stdout, "RESULT: MATCHED - pattern '%s' matches '%s'\n", heystack, needle); + exit(0); + } + else { + fprintf(stdout, "RESULT: NOT MATCHED - pattern '%s' does not match '%s'\n", heystack, needle); + exit(1); + } + } + else if(strncmp(optarg, stacksize_string, strlen(stacksize_string)) == 0) { optarg += strlen(stacksize_string); config_set("global", "pthread stack size", optarg); - } else if(strncmp(optarg, debug_flags_string, strlen(debug_flags_string)) == 0) { + } + else if(strncmp(optarg, debug_flags_string, strlen(debug_flags_string)) == 0) { optarg += strlen(debug_flags_string); config_set("global", "debug flags", optarg); debug_flags = strtoull(optarg, NULL, 0); @@ -460,8 +565,11 @@ int main(int argc, char **argv) if(debug_flags != 0) { struct rlimit rl = { RLIM_INFINITY, RLIM_INFINITY }; if(setrlimit(RLIMIT_CORE, &rl) != 0) - info("Cannot request unlimited core dumps for debugging... Proceeding anyway..."); + error("Cannot request unlimited core dumps for debugging... Proceeding anyway..."); + +#if !(defined(__FreeBSD__) || defined(__APPLE__)) prctl(PR_SET_DUMPABLE, 1, 0, 0, 0); +#endif /* __FreeBSD__ || __APPLE__*/ } // -------------------------------------------------------------------- @@ -520,7 +628,7 @@ int main(int argc, char **argv) rrd_default_history_entries = (int) config_get_number("global", "history", RRD_DEFAULT_HISTORY_ENTRIES); if(rrd_default_history_entries < 5 || rrd_default_history_entries > RRD_HISTORY_ENTRIES_MAX) { - info("Invalid save lines %d given. Defaulting to %d.", rrd_default_history_entries, RRD_DEFAULT_HISTORY_ENTRIES); + error("Invalid history entries %d given. Defaulting to %d.", rrd_default_history_entries, RRD_DEFAULT_HISTORY_ENTRIES); rrd_default_history_entries = RRD_DEFAULT_HISTORY_ENTRIES; } else { @@ -531,7 +639,7 @@ int main(int argc, char **argv) rrd_update_every = (int) config_get_number("global", "update every", UPDATE_EVERY); if(rrd_update_every < 1 || rrd_update_every > 600) { - info("Invalid update timer %d given. Defaulting to %d.", rrd_update_every, UPDATE_EVERY_MAX); + error("Invalid data collection frequency (update every) %d given. Defaulting to %d.", rrd_update_every, UPDATE_EVERY_MAX); rrd_update_every = UPDATE_EVERY; } else debug(D_OPTIONS, "update timer set to %d.", rrd_update_every); @@ -635,17 +743,18 @@ int main(int argc, char **argv) if(debug_flags != 0) { struct rlimit rl = { RLIM_INFINITY, RLIM_INFINITY }; if(setrlimit(RLIMIT_CORE, &rl) != 0) - info("Cannot request unlimited core dumps for debugging... Proceeding anyway..."); + error("Cannot request unlimited core dumps for debugging... Proceeding anyway..."); +#if !(defined(__FreeBSD__) || defined(__APPLE__)) prctl(PR_SET_DUMPABLE, 1, 0, 0, 0); +#endif /* __FreeBSD__ || __APPLE__*/ } #endif /* NETDATA_INTERNAL_CHECKS */ // fork, switch user, create pid file, set process priority if(become_daemon(dont_fork, user) == -1) - fatal("Cannot demonize myself."); - - info("NetData started on pid %d", getpid()); + fatal("Cannot daemonize myself."); + info("netdata started on pid %d.", getpid()); // ------------------------------------------------------------------------ // get default pthread stack size @@ -655,7 +764,7 @@ int main(int argc, char **argv) if(i != 0) fatal("pthread_attr_setstacksize() to %zu bytes, failed with code %d.", wanted_stacksize, i); else - info("Successfully set pthread stacksize to %zu bytes", wanted_stacksize); + debug(D_SYSTEM, "Successfully set pthread stacksize to %zu bytes", wanted_stacksize); } // ------------------------------------------------------------------------ @@ -692,17 +801,19 @@ int main(int argc, char **argv) if(st->enabled) { st->thread = mallocz(sizeof(pthread_t)); - info("Starting thread %s.", st->name); + debug(D_SYSTEM, "Starting thread %s.", st->name); - if(pthread_create(st->thread, &attr, st->start_routine, NULL)) + if(pthread_create(st->thread, &attr, st->start_routine, st)) error("failed to create new thread for %s.", st->name); else if(pthread_detach(*st->thread)) error("Cannot request detach of newly created %s thread.", st->name); } - else info("Not starting thread %s.", st->name); + else debug(D_SYSTEM, "Not starting thread %s.", st->name); } + info("netdata initialization completed. Enjoy real-time performance monitoring!"); + // ------------------------------------------------------------------------ // block signals while initializing threads. sigset_t sigset; @@ -716,7 +827,7 @@ int main(int argc, char **argv) while(1) { pause(); if(netdata_exit) { - info("Exit main loop of netdata."); + debug(D_EXIT, "Exit main loop of netdata."); netdata_cleanup_and_exit(0); exit(0); } diff --git a/src/main.h b/src/main.h index 646827fbd..49afaef12 100644 --- a/src/main.h +++ b/src/main.h @@ -24,8 +24,22 @@ struct option_def { */ extern struct option_def options[]; +struct netdata_static_thread { + char *name; + + char *config_section; + char *config_name; + + volatile int enabled; + + pthread_t *thread; + + void (*init_routine) (void); + void *(*start_routine) (void *); +}; + extern void kill_childs(void); extern int killpid(pid_t pid, int signal); -extern void netdata_cleanup_and_exit(int ret) __attribute__ ((noreturn)); +extern void netdata_cleanup_and_exit(int ret) NORETURN; #endif /* NETDATA_MAIN_H */ diff --git a/src/plugin_checks.c b/src/plugin_checks.c index 007d6565f..fcc542e68 100644 --- a/src/plugin_checks.c +++ b/src/plugin_checks.c @@ -1,8 +1,7 @@ #include "common.h" -void *checks_main(void *ptr) -{ - if(ptr) { ; } +void *checks_main(void *ptr) { + struct netdata_static_thread *static_thread = (struct netdata_static_thread *)ptr; info("CHECKS thread created with task id %d", gettid()); @@ -12,7 +11,7 @@ void *checks_main(void *ptr) if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0) error("Cannot set pthread cancel state to ENABLE."); - unsigned long long usec = 0, susec = rrd_update_every * 1000000ULL, loop_usec = 0, total_susec = 0; + usec_t usec = 0, susec = rrd_update_every * USEC_PER_SEC, loop_usec = 0, total_susec = 0; struct timeval now, last, loop; RRDSET *check1, *check2, *check3, *apps_cpu = NULL; @@ -30,18 +29,18 @@ void *checks_main(void *ptr) rrddim_add(check3, "netdata", NULL, 1, 1, RRDDIM_ABSOLUTE); rrddim_add(check3, "apps.plugin", NULL, 1, 1, RRDDIM_ABSOLUTE); - gettimeofday(&last, NULL); + now_realtime_timeval(&last); while(1) { usleep(susec); // find the time to sleep in order to wait exactly update_every seconds - gettimeofday(&now, NULL); - loop_usec = usec_dt(&now, &last); + now_realtime_timeval(&now); + loop_usec = dt_usec(&now, &last); usec = loop_usec - susec; debug(D_PROCNETDEV_LOOP, "CHECK: last loop took %llu usec (worked for %llu, sleeped for %llu).", loop_usec, usec, susec); - if(usec < (rrd_update_every * 1000000ULL / 2ULL)) susec = (rrd_update_every * 1000000ULL) - usec; - else susec = rrd_update_every * 1000000ULL / 2ULL; + if(usec < (rrd_update_every * USEC_PER_SEC / 2ULL)) susec = (rrd_update_every * USEC_PER_SEC) - usec; + else susec = rrd_update_every * USEC_PER_SEC / 2ULL; // -------------------------------------------------------------------- // Calculate loop time @@ -71,13 +70,16 @@ void *checks_main(void *ptr) if(!apps_cpu) apps_cpu = rrdset_find("apps.cpu"); if(check3->counter_done) rrdset_next_usec(check3, loop_usec); - gettimeofday(&loop, NULL); - rrddim_set(check3, "caller", (long long) usec_dt(&loop, &check1->last_collected_time)); - rrddim_set(check3, "netdata", (long long) usec_dt(&loop, &check2->last_collected_time)); - if(apps_cpu) rrddim_set(check3, "apps.plugin", (long long) usec_dt(&loop, &apps_cpu->last_collected_time)); + now_realtime_timeval(&loop); + rrddim_set(check3, "caller", (long long) dt_usec(&loop, &check1->last_collected_time)); + rrddim_set(check3, "netdata", (long long) dt_usec(&loop, &check2->last_collected_time)); + if(apps_cpu) rrddim_set(check3, "apps.plugin", (long long) dt_usec(&loop, &apps_cpu->last_collected_time)); rrdset_done(check3); } + info("CHECKS thread exiting"); + + static_thread->enabled = 0; pthread_exit(NULL); return NULL; } diff --git a/src/plugin_freebsd.c b/src/plugin_freebsd.c new file mode 100644 index 000000000..bdc3599ea --- /dev/null +++ b/src/plugin_freebsd.c @@ -0,0 +1,79 @@ +#include "common.h" + +void *freebsd_main(void *ptr) { + struct netdata_static_thread *static_thread = (struct netdata_static_thread *)ptr; + + info("FREEBSD Plugin thread created with task id %d", gettid()); + + if(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL) != 0) + error("Cannot set pthread cancel type to DEFERRED."); + + if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0) + error("Cannot set pthread cancel state to ENABLE."); + + // disable (by default) various interface that are not needed + /* + config_get_boolean("plugin:proc:/proc/net/dev:lo", "enabled", 0); + config_get_boolean("plugin:proc:/proc/net/dev:fireqos_monitor", "enabled", 0); + */ + + // when ZERO, attempt to do it + int vdo_cpu_netdata = !config_get_boolean("plugin:freebsd", "netdata server resources", 1); + int vdo_freebsd_sysctl = !config_get_boolean("plugin:freebsd", "sysctl", 1); + + // keep track of the time each module was called + unsigned long long sutime_freebsd_sysctl = 0ULL; + + usec_t step = rrd_update_every * USEC_PER_SEC; + for(;;) { + usec_t now = now_realtime_usec(); + usec_t next = now - (now % step) + step; + + while(now < next) { + sleep_usec(next - now); + now = now_realtime_usec(); + } + + if(unlikely(netdata_exit)) break; + + // BEGIN -- the job to be done + + if(!vdo_freebsd_sysctl) { + debug(D_PROCNETDEV_LOOP, "FREEBSD: calling do_freebsd_sysctl()."); + now = now_realtime_usec(); + vdo_freebsd_sysctl = do_freebsd_sysctl(rrd_update_every, (sutime_freebsd_sysctl > 0)?now - sutime_freebsd_sysctl:0ULL); + sutime_freebsd_sysctl = now; + } + if(unlikely(netdata_exit)) break; + + // END -- the job is done + + // -------------------------------------------------------------------- + + if(!vdo_cpu_netdata) { + global_statistics_charts(); + registry_statistics(); + } + } + + info("FREEBSD thread exiting"); + + static_thread->enabled = 0; + pthread_exit(NULL); + return NULL; +} + +int getsysctl(const char *name, void *ptr, size_t len) +{ + size_t nlen = len; + + if (unlikely(sysctlbyname(name, ptr, &nlen, NULL, 0) == -1)) { + error("FREEBSD: sysctl(%s...) failed: %s", name, strerror(errno)); + return 1; + } + if (unlikely(nlen != len)) { + error("FREEBSD: sysctl(%s...) expected %lu, got %lu", name, (unsigned long)len, (unsigned long)nlen); + return 1; + } + return 0; +} diff --git a/src/plugin_freebsd.h b/src/plugin_freebsd.h new file mode 100644 index 000000000..e4767a091 --- /dev/null +++ b/src/plugin_freebsd.h @@ -0,0 +1,14 @@ +#ifndef NETDATA_PLUGIN_FREEBSD_H +#define NETDATA_PLUGIN_FREEBSD_H 1 + +#include <sys/sysctl.h> + +#define GETSYSCTL(name, var) getsysctl(name, &(var), sizeof(var)) + +void *freebsd_main(void *ptr); + +int getsysctl(const char *name, void *ptr, size_t len); + +extern int do_freebsd_sysctl(int update_every, usec_t dt); + +#endif /* NETDATA_PLUGIN_FREEBSD_H */ diff --git a/src/plugin_idlejitter.c b/src/plugin_idlejitter.c index 30c6d8708..7d4a4c189 100644 --- a/src/plugin_idlejitter.c +++ b/src/plugin_idlejitter.c @@ -2,11 +2,10 @@ #define CPU_IDLEJITTER_SLEEP_TIME_MS 20 -void *cpuidlejitter_main(void *ptr) -{ - if(ptr) { ; } +void *cpuidlejitter_main(void *ptr) { + struct netdata_static_thread *static_thread = (struct netdata_static_thread *)ptr; - info("CPU Idle Jitter thread created with task id %d", gettid()); + info("IDLEJITTER thread created with task id %d", gettid()); if(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL) != 0) error("Cannot set pthread cancel type to DEFERRED."); @@ -29,16 +28,16 @@ void *cpuidlejitter_main(void *ptr) struct timeval before, after; unsigned long long counter; for(counter = 0; 1 ;counter++) { - unsigned long long usec = 0, susec = 0; + usec_t usec = 0, susec = 0; - while(susec < (rrd_update_every * 1000000ULL)) { + while(susec < (rrd_update_every * USEC_PER_SEC)) { - gettimeofday(&before, NULL); - usleep(sleep_ms * 1000); - gettimeofday(&after, NULL); + now_realtime_timeval(&before); + sleep_usec(sleep_ms * 1000); + now_realtime_timeval(&after); // calculate the time it took for a full loop - usec = usec_dt(&after, &before); + usec = dt_usec(&after, &before); susec += usec; } usec -= (sleep_ms * 1000); @@ -48,6 +47,9 @@ void *cpuidlejitter_main(void *ptr) rrdset_done(st); } + info("IDLEJITTER thread exiting"); + + static_thread->enabled = 0; pthread_exit(NULL); return NULL; } diff --git a/src/plugin_macos.c b/src/plugin_macos.c new file mode 100644 index 000000000..3955c1414 --- /dev/null +++ b/src/plugin_macos.c @@ -0,0 +1,84 @@ +#include "common.h" + +void *macos_main(void *ptr) { + struct netdata_static_thread *static_thread = (struct netdata_static_thread *)ptr; + + info("MACOS Plugin thread created with task id %d", gettid()); + + if(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL) != 0) + error("Cannot set pthread cancel type to DEFERRED."); + + if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0) + error("Cannot set pthread cancel state to ENABLE."); + + // disable (by default) various interface that are not needed + /* + config_get_boolean("plugin:proc:/proc/net/dev:lo", "enabled", 0); + config_get_boolean("plugin:proc:/proc/net/dev:fireqos_monitor", "enabled", 0); + */ + + // when ZERO, attempt to do it + int vdo_cpu_netdata = !config_get_boolean("plugin:macos", "netdata server resources", 1); + int vdo_macos_sysctl = !config_get_boolean("plugin:macos", "sysctl", 1); + int vdo_macos_mach_smi = !config_get_boolean("plugin:macos", "mach system management interface", 1); + int vdo_macos_iokit = !config_get_boolean("plugin:macos", "iokit", 1); + + // keep track of the time each module was called + unsigned long long sutime_macos_sysctl = 0ULL; + unsigned long long sutime_macos_mach_smi = 0ULL; + unsigned long long sutime_macos_iokit = 0ULL; + + usec_t step = rrd_update_every * USEC_PER_SEC; + for(;;) { + usec_t now = now_realtime_usec(); + usec_t next = now - (now % step) + step; + + while(now < next) { + sleep_usec(next - now); + now = now_realtime_usec(); + } + + if(unlikely(netdata_exit)) break; + + // BEGIN -- the job to be done + + if(!vdo_macos_sysctl) { + debug(D_PROCNETDEV_LOOP, "MACOS: calling do_macos_sysctl()."); + now = now_realtime_usec(); + vdo_macos_sysctl = do_macos_sysctl(rrd_update_every, (sutime_macos_sysctl > 0)?now - sutime_macos_sysctl:0ULL); + sutime_macos_sysctl = now; + } + if(unlikely(netdata_exit)) break; + + if(!vdo_macos_mach_smi) { + debug(D_PROCNETDEV_LOOP, "MACOS: calling do_macos_mach_smi()."); + now = now_realtime_usec(); + vdo_macos_mach_smi = do_macos_mach_smi(rrd_update_every, (sutime_macos_mach_smi > 0)?now - sutime_macos_mach_smi:0ULL); + sutime_macos_mach_smi = now; + } + if(unlikely(netdata_exit)) break; + + if(!vdo_macos_iokit) { + debug(D_PROCNETDEV_LOOP, "MACOS: calling do_macos_iokit()."); + now = now_realtime_usec(); + vdo_macos_iokit = do_macos_iokit(rrd_update_every, (sutime_macos_iokit > 0)?now - sutime_macos_iokit:0ULL); + sutime_macos_iokit = now; + } + if(unlikely(netdata_exit)) break; + + // END -- the job is done + + // -------------------------------------------------------------------- + + if(!vdo_cpu_netdata) { + global_statistics_charts(); + registry_statistics(); + } + } + + info("MACOS thread exiting"); + + static_thread->enabled = 0; + pthread_exit(NULL); + return NULL; +} diff --git a/src/plugin_macos.h b/src/plugin_macos.h new file mode 100644 index 000000000..a21e5601d --- /dev/null +++ b/src/plugin_macos.h @@ -0,0 +1,14 @@ +#ifndef NETDATA_PLUGIN_MACOS_H +#define NETDATA_PLUGIN_MACOS_H 1 + +void *macos_main(void *ptr); + +#define GETSYSCTL(name, var) getsysctl(name, &(var), sizeof(var)) + +extern int getsysctl(const char *name, void *ptr, size_t len); + +extern int do_macos_sysctl(int update_every, usec_t dt); +extern int do_macos_mach_smi(int update_every, usec_t dt); +extern int do_macos_iokit(int update_every, usec_t dt); + +#endif /* NETDATA_PLUGIN_MACOS_H */ diff --git a/src/plugin_nfacct.c b/src/plugin_nfacct.c index 7843161d3..7aae33c0c 100644 --- a/src/plugin_nfacct.c +++ b/src/plugin_nfacct.c @@ -55,7 +55,7 @@ static int nfacct_callback(const struct nlmsghdr *nlh, void *data) { } void *nfacct_main(void *ptr) { - if(ptr) { ; } + struct netdata_static_thread *static_thread = (struct netdata_static_thread *)ptr; info("NFACCT thread created with task id %d", gettid()); @@ -70,30 +70,27 @@ void *nfacct_main(void *ptr) { struct nlmsghdr *nlh = NULL; unsigned int seq = 0, portid = 0; - seq = time(NULL) - 1; + seq = now_realtime_sec() - 1; nl = mnl_socket_open(NETLINK_NETFILTER); if(!nl) { error("nfacct.plugin: mnl_socket_open() failed"); - pthread_exit(NULL); - return NULL; + goto cleanup; } if(mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) { - mnl_socket_close(nl); error("nfacct.plugin: mnl_socket_bind() failed"); - pthread_exit(NULL); - return NULL; + goto cleanup; } portid = mnl_socket_get_portid(nl); // ------------------------------------------------------------------------ struct timeval last, now; - unsigned long long usec = 0, susec = 0; + usec_t usec = 0, susec = 0; RRDSET *st = NULL; - gettimeofday(&last, NULL); + now_realtime_timeval(&last); // ------------------------------------------------------------------------ @@ -104,16 +101,13 @@ void *nfacct_main(void *ptr) { nlh = nfacct_nlmsg_build_hdr(buf, NFNL_MSG_ACCT_GET, NLM_F_DUMP, seq); if(!nlh) { - mnl_socket_close(nl); error("nfacct.plugin: nfacct_nlmsg_build_hdr() failed"); - pthread_exit(NULL); - return NULL; + goto cleanup; } if(mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { error("nfacct.plugin: mnl_socket_send"); - pthread_exit(NULL); - return NULL; + goto cleanup; } if(nfacct_list) nfacct_list->len = 0; @@ -125,14 +119,13 @@ void *nfacct_main(void *ptr) { if (ret == -1) { error("nfacct.plugin: error communicating with kernel."); - pthread_exit(NULL); - return NULL; + goto cleanup; } // -------------------------------------------------------------------- - gettimeofday(&now, NULL); - usec = usec_dt(&now, &last) - susec; + now_realtime_timeval(&now); + usec = dt_usec(&now, &last) - susec; debug(D_NFACCT_LOOP, "nfacct.plugin: last loop took %llu usec (worked for %llu, sleeped for %llu).", usec + susec, usec, susec); if(usec < (rrd_update_every * 1000000ULL / 2ULL)) susec = (rrd_update_every * 1000000ULL) - usec; @@ -191,7 +184,12 @@ void *nfacct_main(void *ptr) { memmove(&last, &now, sizeof(struct timeval)); } - mnl_socket_close(nl); +cleanup: + info("NFACCT thread exiting"); + + if(nl) mnl_socket_close(nl); + + static_thread->enabled = 0; pthread_exit(NULL); return NULL; } diff --git a/src/plugin_proc.c b/src/plugin_proc.c index a50a22514..9b66b7c28 100644 --- a/src/plugin_proc.c +++ b/src/plugin_proc.c @@ -1,241 +1,146 @@ #include "common.h" -void *proc_main(void *ptr) -{ - (void)ptr; +static struct proc_module { + const char *name; + const char *dim; - info("PROC Plugin thread created with task id %d", gettid()); + int enabled; - if(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL) != 0) - error("Cannot set pthread cancel type to DEFERRED."); + int (*func)(int update_every, usec_t dt); + usec_t last_run_usec; + usec_t duration; - if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0) - error("Cannot set pthread cancel state to ENABLE."); + RRDDIM *rd; - // disable (by default) various interface that are not needed - config_get_boolean("plugin:proc:/proc/net/dev:lo", "enabled", 0); - config_get_boolean("plugin:proc:/proc/net/dev:fireqos_monitor", "enabled", 0); - - // when ZERO, attempt to do it - int vdo_proc_net_dev = !config_get_boolean("plugin:proc", "/proc/net/dev", 1); - int vdo_proc_diskstats = !config_get_boolean("plugin:proc", "/proc/diskstats", 1); - int vdo_proc_net_snmp = !config_get_boolean("plugin:proc", "/proc/net/snmp", 1); - int vdo_proc_net_snmp6 = !config_get_boolean("plugin:proc", "/proc/net/snmp6", 1); - int vdo_proc_net_netstat = !config_get_boolean("plugin:proc", "/proc/net/netstat", 1); - int vdo_proc_net_stat_conntrack = !config_get_boolean("plugin:proc", "/proc/net/stat/conntrack", 1); - int vdo_proc_net_ip_vs_stats = !config_get_boolean("plugin:proc", "/proc/net/ip_vs/stats", 1); - int vdo_proc_net_stat_synproxy = !config_get_boolean("plugin:proc", "/proc/net/stat/synproxy", 1); - int vdo_proc_stat = !config_get_boolean("plugin:proc", "/proc/stat", 1); - int vdo_proc_meminfo = !config_get_boolean("plugin:proc", "/proc/meminfo", 1); - int vdo_proc_vmstat = !config_get_boolean("plugin:proc", "/proc/vmstat", 1); - int vdo_proc_net_rpc_nfs = !config_get_boolean("plugin:proc", "/proc/net/rpc/nfs", 1); - int vdo_proc_net_rpc_nfsd = !config_get_boolean("plugin:proc", "/proc/net/rpc/nfsd", 1); - int vdo_proc_sys_kernel_random_entropy_avail = !config_get_boolean("plugin:proc", "/proc/sys/kernel/random/entropy_avail", 1); - int vdo_proc_interrupts = !config_get_boolean("plugin:proc", "/proc/interrupts", 1); - int vdo_proc_softirqs = !config_get_boolean("plugin:proc", "/proc/softirqs", 1); - int vdo_proc_net_softnet_stat = !config_get_boolean("plugin:proc", "/proc/net/softnet_stat", 1); - int vdo_proc_loadavg = !config_get_boolean("plugin:proc", "/proc/loadavg", 1); - int vdo_sys_kernel_mm_ksm = !config_get_boolean("plugin:proc", "/sys/kernel/mm/ksm", 1); - int vdo_cpu_netdata = !config_get_boolean("plugin:proc", "netdata server resources", 1); - - // keep track of the time each module was called - unsigned long long sutime_proc_net_dev = 0ULL; - unsigned long long sutime_proc_diskstats = 0ULL; - unsigned long long sutime_proc_net_snmp = 0ULL; - unsigned long long sutime_proc_net_snmp6 = 0ULL; - unsigned long long sutime_proc_net_netstat = 0ULL; - unsigned long long sutime_proc_net_stat_conntrack = 0ULL; - unsigned long long sutime_proc_net_ip_vs_stats = 0ULL; - unsigned long long sutime_proc_net_stat_synproxy = 0ULL; - unsigned long long sutime_proc_stat = 0ULL; - unsigned long long sutime_proc_meminfo = 0ULL; - unsigned long long sutime_proc_vmstat = 0ULL; - unsigned long long sutime_proc_net_rpc_nfs = 0ULL; - unsigned long long sutime_proc_net_rpc_nfsd = 0ULL; - unsigned long long sutime_proc_sys_kernel_random_entropy_avail = 0ULL; - unsigned long long sutime_proc_interrupts = 0ULL; - unsigned long long sutime_proc_softirqs = 0ULL; - unsigned long long sutime_proc_net_softnet_stat = 0ULL; - unsigned long long sutime_proc_loadavg = 0ULL; - unsigned long long sutime_sys_kernel_mm_ksm = 0ULL; - - // the next time we will run - aligned properly - unsigned long long sunext = (time(NULL) - (time(NULL) % rrd_update_every) + rrd_update_every) * 1000000ULL; - unsigned long long sunow; +} proc_modules[] = { - for(;;) { - if(unlikely(netdata_exit)) break; + // system metrics + { .name = "/proc/stat", .dim = "stat", .func = do_proc_stat }, + { .name = "/proc/uptime", .dim = "uptime", .func = do_proc_uptime }, + { .name = "/proc/loadavg", .dim = "loadavg", .func = do_proc_loadavg }, + { .name = "/proc/sys/kernel/random/entropy_avail", .dim = "entropy", .func = do_proc_sys_kernel_random_entropy_avail }, - // delay until it is our time to run - while((sunow = time_usec()) < sunext) - sleep_usec(sunext - sunow); + // CPU metrics + { .name = "/proc/interrupts", .dim = "interrupts", .func = do_proc_interrupts }, + { .name = "/proc/softirqs", .dim = "softirqs", .func = do_proc_softirqs }, - // find the next time we need to run - while(time_usec() > sunext) - sunext += rrd_update_every * 1000000ULL; + // memory metrics + { .name = "/proc/vmstat", .dim = "vmstat", .func = do_proc_vmstat }, + { .name = "/proc/meminfo", .dim = "meminfo", .func = do_proc_meminfo }, + { .name = "/sys/kernel/mm/ksm", .dim = "ksm", .func = do_sys_kernel_mm_ksm }, + { .name = "/sys/devices/system/edac/mc", .dim = "ecc", .func = do_proc_sys_devices_system_edac_mc }, + { .name = "/sys/devices/system/node", .dim = "numa", .func = do_proc_sys_devices_system_node }, - if(unlikely(netdata_exit)) break; + // network metrics + { .name = "/proc/net/dev", .dim = "netdev", .func = do_proc_net_dev }, + { .name = "/proc/net/netstat", .dim = "netstat", .func = do_proc_net_netstat }, + { .name = "/proc/net/snmp", .dim = "snmp", .func = do_proc_net_snmp }, + { .name = "/proc/net/snmp6", .dim = "snmp6", .func = do_proc_net_snmp6 }, + { .name = "/proc/net/softnet_stat", .dim = "softnet", .func = do_proc_net_softnet_stat }, + { .name = "/proc/net/ip_vs/stats", .dim = "ipvs", .func = do_proc_net_ip_vs_stats }, - // BEGIN -- the job to be done + // firewall metrics + { .name = "/proc/net/stat/conntrack", .dim = "conntrack", .func = do_proc_net_stat_conntrack }, + { .name = "/proc/net/stat/synproxy", .dim = "synproxy", .func = do_proc_net_stat_synproxy }, - if(!vdo_sys_kernel_mm_ksm) { - debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling do_sys_kernel_mm_ksm()."); + // disk metrics + { .name = "/proc/diskstats", .dim = "diskstats", .func = do_proc_diskstats }, - sunow = time_usec(); - vdo_sys_kernel_mm_ksm = do_sys_kernel_mm_ksm(rrd_update_every, (sutime_sys_kernel_mm_ksm > 0)?sunow - sutime_sys_kernel_mm_ksm:0ULL); - sutime_sys_kernel_mm_ksm = sunow; - } - if(unlikely(netdata_exit)) break; + // NFS metrics + { .name = "/proc/net/rpc/nfsd", .dim = "nfsd", .func = do_proc_net_rpc_nfsd }, + { .name = "/proc/net/rpc/nfs", .dim = "nfs", .func = do_proc_net_rpc_nfs }, - if(!vdo_proc_loadavg) { - debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling do_proc_loadavg()."); - sunow = time_usec(); - vdo_proc_loadavg = do_proc_loadavg(rrd_update_every, (sutime_proc_loadavg > 0)?sunow - sutime_proc_loadavg:0ULL); - sutime_proc_loadavg = sunow; - } - if(unlikely(netdata_exit)) break; + // IPC metrics + { .name = "ipc", .dim = "ipc", .func = do_ipc }, - if(!vdo_proc_interrupts) { - debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling do_proc_interrupts()."); - sunow = time_usec(); - vdo_proc_interrupts = do_proc_interrupts(rrd_update_every, (sutime_proc_interrupts > 0)?sunow - sutime_proc_interrupts:0ULL); - sutime_proc_interrupts = sunow; - } - if(unlikely(netdata_exit)) break; + // the terminator of this array + { .name = NULL, .dim = NULL, .func = NULL } +}; - if(!vdo_proc_softirqs) { - debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling do_proc_softirqs()."); - sunow = time_usec(); - vdo_proc_softirqs = do_proc_softirqs(rrd_update_every, (sutime_proc_softirqs > 0)?sunow - sutime_proc_softirqs:0ULL); - sutime_proc_softirqs = sunow; - } - if(unlikely(netdata_exit)) break; +void *proc_main(void *ptr) { + struct netdata_static_thread *static_thread = (struct netdata_static_thread *)ptr; - if(!vdo_proc_net_softnet_stat) { - debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling do_proc_net_softnet_stat()."); - sunow = time_usec(); - vdo_proc_net_softnet_stat = do_proc_net_softnet_stat(rrd_update_every, (sutime_proc_net_softnet_stat > 0)?sunow - sutime_proc_net_softnet_stat:0ULL); - sutime_proc_net_softnet_stat = sunow; - } - if(unlikely(netdata_exit)) break; + info("PROC Plugin thread created with task id %d", gettid()); - if(!vdo_proc_sys_kernel_random_entropy_avail) { - debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling do_proc_sys_kernel_random_entropy_avail()."); - sunow = time_usec(); - vdo_proc_sys_kernel_random_entropy_avail = do_proc_sys_kernel_random_entropy_avail(rrd_update_every, (sutime_proc_sys_kernel_random_entropy_avail > 0)?sunow - sutime_proc_sys_kernel_random_entropy_avail:0ULL); - sutime_proc_sys_kernel_random_entropy_avail = sunow; - } - if(unlikely(netdata_exit)) break; + if(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL) != 0) + error("Cannot set pthread cancel type to DEFERRED."); - if(!vdo_proc_net_dev) { - debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling do_proc_net_dev()."); - sunow = time_usec(); - vdo_proc_net_dev = do_proc_net_dev(rrd_update_every, (sutime_proc_net_dev > 0)?sunow - sutime_proc_net_dev:0ULL); - sutime_proc_net_dev = sunow; - } - if(unlikely(netdata_exit)) break; + if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0) + error("Cannot set pthread cancel state to ENABLE."); - if(!vdo_proc_diskstats) { - debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling do_proc_diskstats()."); - sunow = time_usec(); - vdo_proc_diskstats = do_proc_diskstats(rrd_update_every, (sutime_proc_diskstats > 0)?sunow - sutime_proc_diskstats:0ULL); - sutime_proc_diskstats = sunow; - } - if(unlikely(netdata_exit)) break; + int vdo_cpu_netdata = config_get_boolean("plugin:proc", "netdata server resources", 1); - if(!vdo_proc_net_snmp) { - debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling do_proc_net_snmp()."); - sunow = time_usec(); - vdo_proc_net_snmp = do_proc_net_snmp(rrd_update_every, (sutime_proc_net_snmp > 0)?sunow - sutime_proc_net_snmp:0ULL); - sutime_proc_net_snmp = sunow; - } - if(unlikely(netdata_exit)) break; + // check the enabled status for each module + int i; + for(i = 0 ; proc_modules[i].name ;i++) { + struct proc_module *pm = &proc_modules[i]; - if(!vdo_proc_net_snmp6) { - debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling do_proc_net_snmp6()."); - sunow = time_usec(); - vdo_proc_net_snmp6 = do_proc_net_snmp6(rrd_update_every, (sutime_proc_net_snmp6 > 0)?sunow - sutime_proc_net_snmp6:0ULL); - sutime_proc_net_snmp6 = sunow; - } - if(unlikely(netdata_exit)) break; + pm->enabled = config_get_boolean("plugin:proc", pm->name, 1); + pm->last_run_usec = 0ULL; + pm->duration = 0ULL; + pm->rd = NULL; + } - if(!vdo_proc_net_netstat) { - debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling do_proc_net_netstat()."); - sunow = time_usec(); - vdo_proc_net_netstat = do_proc_net_netstat(rrd_update_every, (sutime_proc_net_netstat > 0)?sunow - sutime_proc_net_netstat:0ULL); - sutime_proc_net_netstat = sunow; - } - if(unlikely(netdata_exit)) break; + usec_t step = rrd_update_every * USEC_PER_SEC; + for(;;) { + usec_t now = now_monotonic_usec(); + usec_t next = now - (now % step) + step; - if(!vdo_proc_net_stat_conntrack) { - debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling do_proc_net_stat_conntrack()."); - sunow = time_usec(); - vdo_proc_net_stat_conntrack = do_proc_net_stat_conntrack(rrd_update_every, (sutime_proc_net_stat_conntrack > 0)?sunow - sutime_proc_net_stat_conntrack:0ULL); - sutime_proc_net_stat_conntrack = sunow; + while(now < next) { + sleep_usec(next - now); + now = now_monotonic_usec(); } - if(unlikely(netdata_exit)) break; - if(!vdo_proc_net_ip_vs_stats) { - debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling vdo_proc_net_ip_vs_stats()."); - sunow = time_usec(); - vdo_proc_net_ip_vs_stats = do_proc_net_ip_vs_stats(rrd_update_every, (sutime_proc_net_ip_vs_stats > 0)?sunow - sutime_proc_net_ip_vs_stats:0ULL); - sutime_proc_net_ip_vs_stats = sunow; - } if(unlikely(netdata_exit)) break; - if(!vdo_proc_net_stat_synproxy) { - debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling vdo_proc_net_stat_synproxy()."); - sunow = time_usec(); - vdo_proc_net_stat_synproxy = do_proc_net_stat_synproxy(rrd_update_every, (sutime_proc_net_stat_synproxy > 0)?sunow - sutime_proc_net_stat_synproxy:0ULL); - sutime_proc_net_stat_synproxy = sunow; - } - if(unlikely(netdata_exit)) break; + // BEGIN -- the job to be done - if(!vdo_proc_stat) { - debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling do_proc_stat()."); - sunow = time_usec(); - vdo_proc_stat = do_proc_stat(rrd_update_every, (sutime_proc_stat > 0)?sunow - sutime_proc_stat:0ULL); - sutime_proc_stat = sunow; - } - if(unlikely(netdata_exit)) break; + for(i = 0 ; proc_modules[i].name ;i++) { + struct proc_module *pm = &proc_modules[i]; + if(unlikely(!pm->enabled)) continue; - if(!vdo_proc_meminfo) { - debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling vdo_proc_meminfo()."); - sunow = time_usec(); - vdo_proc_meminfo = do_proc_meminfo(rrd_update_every, (sutime_proc_meminfo > 0)?sunow - sutime_proc_meminfo:0ULL); - sutime_proc_meminfo = sunow; - } - if(unlikely(netdata_exit)) break; + debug(D_PROCNETDEV_LOOP, "PROC calling %s.", pm->name); - if(!vdo_proc_vmstat) { - debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling vdo_proc_vmstat()."); - sunow = time_usec(); - vdo_proc_vmstat = do_proc_vmstat(rrd_update_every, (sutime_proc_vmstat > 0)?sunow - sutime_proc_vmstat:0ULL); - sutime_proc_vmstat = sunow; - } - if(unlikely(netdata_exit)) break; + pm->enabled = !pm->func(rrd_update_every, (pm->last_run_usec > 0)?now - pm->last_run_usec:0ULL); + pm->last_run_usec = now; - if(!vdo_proc_net_rpc_nfsd) { - debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling do_proc_net_rpc_nfsd()."); - sunow = time_usec(); - vdo_proc_net_rpc_nfsd = do_proc_net_rpc_nfsd(rrd_update_every, (sutime_proc_net_rpc_nfsd > 0)?sunow - sutime_proc_net_rpc_nfsd:0ULL); - sutime_proc_net_rpc_nfsd = sunow; - } - if(unlikely(netdata_exit)) break; + now = now_monotonic_usec(); + pm->duration = now - pm->last_run_usec; - if(!vdo_proc_net_rpc_nfs) { - debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling do_proc_net_rpc_nfs()."); - sunow = time_usec(); - vdo_proc_net_rpc_nfs = do_proc_net_rpc_nfs(rrd_update_every, (sutime_proc_net_rpc_nfs > 0)?sunow - sutime_proc_net_rpc_nfs:0ULL); - sutime_proc_net_rpc_nfs = sunow; + if(unlikely(netdata_exit)) break; } - if(unlikely(netdata_exit)) break; // END -- the job is done // -------------------------------------------------------------------- - if(!vdo_cpu_netdata) { + if(vdo_cpu_netdata) { + static RRDSET *st = NULL; + + if(unlikely(!st)) { + st = rrdset_find_bytype("netdata", "plugin_proc_modules"); + + if(!st) { + st = rrdset_create("netdata", "plugin_proc_modules", NULL, "proc", NULL, "NetData Proc Plugin Modules Durations", "milliseconds/run", 132001, rrd_update_every, RRDSET_TYPE_STACKED); + + for(i = 0 ; proc_modules[i].name ;i++) { + struct proc_module *pm = &proc_modules[i]; + if(unlikely(!pm->enabled)) continue; + + pm->rd = rrddim_add(st, pm->dim, NULL, 1, 1000, RRDDIM_ABSOLUTE); + } + } + } + else rrdset_next(st); + + for(i = 0 ; proc_modules[i].name ;i++) { + struct proc_module *pm = &proc_modules[i]; + if(unlikely(!pm->enabled)) continue; + + rrddim_set_by_pointer(st, pm->rd, pm->duration); + } + rrdset_done(st); + global_statistics_charts(); registry_statistics(); } @@ -243,6 +148,41 @@ void *proc_main(void *ptr) info("PROC thread exiting"); + static_thread->enabled = 0; pthread_exit(NULL); return NULL; } + +int get_numa_node_count(void) +{ + static int numa_node_count = -1; + + if (numa_node_count != -1) + return numa_node_count; + + numa_node_count = 0; + + char name[FILENAME_MAX + 1]; + snprintfz(name, FILENAME_MAX, "%s%s", global_host_prefix, "/sys/devices/system/node"); + char *dirname = config_get("plugin:proc:/sys/devices/system/node", "directory to monitor", name); + + DIR *dir = opendir(dirname); + if(dir) { + struct dirent *de = NULL; + while((de = readdir(dir))) { + if(de->d_type != DT_DIR) + continue; + + if(strncmp(de->d_name, "node", 4) != 0) + continue; + + if(!isdigit(de->d_name[4])) + continue; + + numa_node_count++; + } + closedir(dir); + } + + return numa_node_count; +} diff --git a/src/plugin_proc.h b/src/plugin_proc.h index f72a9970f..5dee7853c 100644 --- a/src/plugin_proc.h +++ b/src/plugin_proc.h @@ -3,24 +3,29 @@ void *proc_main(void *ptr); -extern int do_proc_net_dev(int update_every, unsigned long long dt); -extern int do_proc_diskstats(int update_every, unsigned long long dt); -extern int do_proc_net_snmp(int update_every, unsigned long long dt); -extern int do_proc_net_snmp6(int update_every, unsigned long long dt); -extern int do_proc_net_netstat(int update_every, unsigned long long dt); -extern int do_proc_net_stat_conntrack(int update_every, unsigned long long dt); -extern int do_proc_net_ip_vs_stats(int update_every, unsigned long long dt); -extern int do_proc_stat(int update_every, unsigned long long dt); -extern int do_proc_meminfo(int update_every, unsigned long long dt); -extern int do_proc_vmstat(int update_every, unsigned long long dt); -extern int do_proc_net_rpc_nfs(int update_every, unsigned long long dt); -extern int do_proc_net_rpc_nfsd(int update_every, unsigned long long dt); -extern int do_proc_sys_kernel_random_entropy_avail(int update_every, unsigned long long dt); -extern int do_proc_interrupts(int update_every, unsigned long long dt); -extern int do_proc_softirqs(int update_every, unsigned long long dt); -extern int do_sys_kernel_mm_ksm(int update_every, unsigned long long dt); -extern int do_proc_loadavg(int update_every, unsigned long long dt); -extern int do_proc_net_stat_synproxy(int update_every, unsigned long long dt); -extern int do_proc_net_softnet_stat(int update_every, unsigned long long dt); +extern int do_proc_net_dev(int update_every, usec_t dt); +extern int do_proc_diskstats(int update_every, usec_t dt); +extern int do_proc_net_snmp(int update_every, usec_t dt); +extern int do_proc_net_snmp6(int update_every, usec_t dt); +extern int do_proc_net_netstat(int update_every, usec_t dt); +extern int do_proc_net_stat_conntrack(int update_every, usec_t dt); +extern int do_proc_net_ip_vs_stats(int update_every, usec_t dt); +extern int do_proc_stat(int update_every, usec_t dt); +extern int do_proc_meminfo(int update_every, usec_t dt); +extern int do_proc_vmstat(int update_every, usec_t dt); +extern int do_proc_net_rpc_nfs(int update_every, usec_t dt); +extern int do_proc_net_rpc_nfsd(int update_every, usec_t dt); +extern int do_proc_sys_kernel_random_entropy_avail(int update_every, usec_t dt); +extern int do_proc_interrupts(int update_every, usec_t dt); +extern int do_proc_softirqs(int update_every, usec_t dt); +extern int do_sys_kernel_mm_ksm(int update_every, usec_t dt); +extern int do_proc_loadavg(int update_every, usec_t dt); +extern int do_proc_net_stat_synproxy(int update_every, usec_t dt); +extern int do_proc_net_softnet_stat(int update_every, usec_t dt); +extern int do_proc_uptime(int update_every, usec_t dt); +extern int do_proc_sys_devices_system_edac_mc(int update_every, usec_t dt); +extern int do_proc_sys_devices_system_node(int update_every, usec_t dt); + +extern int get_numa_node_count(void); #endif /* NETDATA_PLUGIN_PROC_H */ diff --git a/src/plugin_proc_diskspace.c b/src/plugin_proc_diskspace.c new file mode 100644 index 000000000..43e6dd7c5 --- /dev/null +++ b/src/plugin_proc_diskspace.c @@ -0,0 +1,324 @@ +#include "common.h" + +#define DELAULT_EXLUDED_PATHS "/proc/* /sys/* /var/run/user/* /run/user/*" + +static struct mountinfo *disk_mountinfo_root = NULL; +static int check_for_new_mountpoints_every = 15; + +static inline void mountinfo_reload(int force) { + static time_t last_loaded = 0; + time_t now = now_realtime_sec(); + + if(force || now - last_loaded >= check_for_new_mountpoints_every) { + // mountinfo_free() can be called with NULL disk_mountinfo_root + mountinfo_free(disk_mountinfo_root); + + // re-read mountinfo in case something changed + disk_mountinfo_root = mountinfo_read(1); + + last_loaded = now; + } +} + +// Data to be stored in DICTIONARY mount_points used by do_disk_space_stats(). +// This DICTIONARY is used to lookup the settings of the mount point on each iteration. +struct mount_point_metadata { + int do_space; + int do_inodes; + + size_t collected; // the number of times this has been collected + + RRDSET *st_space; + RRDDIM *rd_space_used; + RRDDIM *rd_space_avail; + RRDDIM *rd_space_reserved; + + RRDSET *st_inodes; + RRDDIM *rd_inodes_used; + RRDDIM *rd_inodes_avail; + RRDDIM *rd_inodes_reserved; +}; + +static inline void do_disk_space_stats(struct mountinfo *mi, int update_every) { + const char *family = mi->mount_point; + const char *disk = mi->persistent_id; + + static DICTIONARY *mount_points = NULL; + static SIMPLE_PATTERN *excluded_mountpoints = NULL; + int do_space, do_inodes; + + if(unlikely(!mount_points)) { + const char *s; + SIMPLE_PREFIX_MODE mode = SIMPLE_PATTERN_EXACT; + + if(config_exists("plugin:proc:/proc/diskstats", "exclude space metrics on paths") && !config_exists("plugin:proc:diskspace", "exclude space metrics on paths")) { + // the config exists in the old section + s = config_get("plugin:proc:/proc/diskstats", "exclude space metrics on paths", DELAULT_EXLUDED_PATHS); + mode = SIMPLE_PATTERN_PREFIX; + } + else + s = config_get("plugin:proc:diskspace", "exclude space metrics on paths", DELAULT_EXLUDED_PATHS); + + mount_points = dictionary_create(DICTIONARY_FLAG_SINGLE_THREADED); + excluded_mountpoints = simple_pattern_create(s, mode); + } + + struct mount_point_metadata *m = dictionary_get(mount_points, mi->mount_point); + if(unlikely(!m)) { + char var_name[4096 + 1]; + snprintfz(var_name, 4096, "plugin:proc:diskspace:%s", mi->mount_point); + + int def_space = config_get_boolean_ondemand("plugin:proc:diskspace", "space usage for all disks", CONFIG_ONDEMAND_ONDEMAND); + int def_inodes = config_get_boolean_ondemand("plugin:proc:diskspace", "inodes usage for all disks", CONFIG_ONDEMAND_ONDEMAND); + + if(unlikely(simple_pattern_matches(excluded_mountpoints, mi->mount_point))) { + def_space = CONFIG_ONDEMAND_NO; + def_inodes = CONFIG_ONDEMAND_NO; + } + + do_space = config_get_boolean_ondemand(var_name, "space usage", def_space); + do_inodes = config_get_boolean_ondemand(var_name, "inodes usage", def_inodes); + + struct mount_point_metadata mp = { + .do_space = do_space, + .do_inodes = do_inodes, + + .collected = 0, + + .st_space = NULL, + .rd_space_avail = NULL, + .rd_space_used = NULL, + .rd_space_reserved = NULL, + + .st_inodes = NULL, + .rd_inodes_avail = NULL, + .rd_inodes_used = NULL, + .rd_inodes_reserved = NULL + }; + + m = dictionary_set(mount_points, mi->mount_point, &mp, sizeof(struct mount_point_metadata)); + } + else { + do_space = m->do_space; + do_inodes = m->do_inodes; + } + + if(unlikely(do_space == CONFIG_ONDEMAND_NO && do_inodes == CONFIG_ONDEMAND_NO)) + return; + + if(unlikely(mi->flags & MOUNTINFO_READONLY && !m->collected)) + return; + + struct statvfs buff_statvfs; + if (statvfs(mi->mount_point, &buff_statvfs) < 0) { + error("Failed statvfs() for '%s' (disk '%s')", mi->mount_point, disk); + return; + } + + // logic found at get_fs_usage() in coreutils + unsigned long bsize = (buff_statvfs.f_frsize) ? buff_statvfs.f_frsize : buff_statvfs.f_bsize; + + fsblkcnt_t bavail = buff_statvfs.f_bavail; + fsblkcnt_t btotal = buff_statvfs.f_blocks; + fsblkcnt_t bavail_root = buff_statvfs.f_bfree; + fsblkcnt_t breserved_root = bavail_root - bavail; + fsblkcnt_t bused; + if(likely(btotal >= bavail_root)) + bused = btotal - bavail_root; + else + bused = bavail_root - btotal; + +#ifdef NETDATA_INTERNAL_CHECKS + if(unlikely(btotal != bavail + breserved_root + bused)) + error("Disk block statistics for '%s' (disk '%s') do not sum up: total = %llu, available = %llu, reserved = %llu, used = %llu", mi->mount_point, disk, (unsigned long long)btotal, (unsigned long long)bavail, (unsigned long long)breserved_root, (unsigned long long)bused); +#endif + + // -------------------------------------------------------------------------- + + fsfilcnt_t favail = buff_statvfs.f_favail; + fsfilcnt_t ftotal = buff_statvfs.f_files; + fsfilcnt_t favail_root = buff_statvfs.f_ffree; + fsfilcnt_t freserved_root = favail_root - favail; + fsfilcnt_t fused = ftotal - favail_root; + +#ifdef NETDATA_INTERNAL_CHECKS + if(unlikely(btotal != bavail + breserved_root + bused)) + error("Disk inode statistics for '%s' (disk '%s') do not sum up: total = %llu, available = %llu, reserved = %llu, used = %llu", mi->mount_point, disk, (unsigned long long)ftotal, (unsigned long long)favail, (unsigned long long)freserved_root, (unsigned long long)fused); +#endif + + // -------------------------------------------------------------------------- + + int rendered = 0; + + if(do_space == CONFIG_ONDEMAND_YES || (do_space == CONFIG_ONDEMAND_ONDEMAND && (bavail || breserved_root || bused))) { + if(unlikely(!m->st_space)) { + m->do_space = CONFIG_ONDEMAND_YES; + m->st_space = rrdset_find_bytype("disk_space", disk); + if(unlikely(!m->st_space)) { + char title[4096 + 1]; + snprintfz(title, 4096, "Disk Space Usage for %s [%s]", family, mi->mount_source); + m->st_space = rrdset_create("disk_space", disk, NULL, family, "disk.space", title, "GB", 2023, update_every, RRDSET_TYPE_STACKED); + } + + m->rd_space_avail = rrddim_add(m->st_space, "avail", NULL, bsize, 1024 * 1024 * 1024, RRDDIM_ABSOLUTE); + m->rd_space_used = rrddim_add(m->st_space, "used", NULL, bsize, 1024 * 1024 * 1024, RRDDIM_ABSOLUTE); + m->rd_space_reserved = rrddim_add(m->st_space, "reserved_for_root", "reserved for root", bsize, 1024 * 1024 * 1024, RRDDIM_ABSOLUTE); + } + else + rrdset_next(m->st_space); + + rrddim_set_by_pointer(m->st_space, m->rd_space_avail, (collected_number)bavail); + rrddim_set_by_pointer(m->st_space, m->rd_space_used, (collected_number)bused); + rrddim_set_by_pointer(m->st_space, m->rd_space_reserved, (collected_number)breserved_root); + rrdset_done(m->st_space); + + rendered++; + } + + // -------------------------------------------------------------------------- + + if(do_inodes == CONFIG_ONDEMAND_YES || (do_inodes == CONFIG_ONDEMAND_ONDEMAND && (favail || freserved_root || fused))) { + if(unlikely(!m->st_inodes)) { + m->do_inodes = CONFIG_ONDEMAND_YES; + m->st_inodes = rrdset_find_bytype("disk_inodes", disk); + if(unlikely(!m->st_inodes)) { + char title[4096 + 1]; + snprintfz(title, 4096, "Disk Files (inodes) Usage for %s [%s]", family, mi->mount_source); + m->st_inodes = rrdset_create("disk_inodes", disk, NULL, family, "disk.inodes", title, "Inodes", 2024, update_every, RRDSET_TYPE_STACKED); + } + + m->rd_inodes_avail = rrddim_add(m->st_inodes, "avail", NULL, 1, 1, RRDDIM_ABSOLUTE); + m->rd_inodes_used = rrddim_add(m->st_inodes, "used", NULL, 1, 1, RRDDIM_ABSOLUTE); + m->rd_inodes_reserved = rrddim_add(m->st_inodes, "reserved_for_root", "reserved for root", 1, 1, RRDDIM_ABSOLUTE); + } + else + rrdset_next(m->st_inodes); + + rrddim_set_by_pointer(m->st_inodes, m->rd_inodes_avail, (collected_number)favail); + rrddim_set_by_pointer(m->st_inodes, m->rd_inodes_used, (collected_number)fused); + rrddim_set_by_pointer(m->st_inodes, m->rd_inodes_reserved, (collected_number)freserved_root); + rrdset_done(m->st_inodes); + + rendered++; + } + + // -------------------------------------------------------------------------- + + if(likely(rendered)) + m->collected++; +} + +void *proc_diskspace_main(void *ptr) { + struct netdata_static_thread *static_thread = (struct netdata_static_thread *)ptr; + + info("DISKSPACE thread created with task id %d", gettid()); + + if(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL) != 0) + error("Cannot set pthread cancel type to DEFERRED."); + + if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0) + error("Cannot set pthread cancel state to ENABLE."); + + int vdo_cpu_netdata = config_get_boolean("plugin:proc", "netdata server resources", 1); + + int update_every = (int)config_get_number("plugin:proc:diskspace", "update every", rrd_update_every); + if(update_every < rrd_update_every) + update_every = rrd_update_every; + + check_for_new_mountpoints_every = (int)config_get_number("plugin:proc:diskspace", "check for new mount points every", check_for_new_mountpoints_every); + if(check_for_new_mountpoints_every < update_every) + check_for_new_mountpoints_every = update_every; + + struct rusage thread; + + usec_t last = 0, dt = 0; + usec_t step = update_every * USEC_PER_SEC; + for(;;) { + usec_t now = now_monotonic_usec(); + usec_t next = now - (now % step) + step; + + dt = (last)?now - last:0; + + while(now < next) { + sleep_usec(next - now); + now = now_monotonic_usec(); + } + + last = now; + + if(unlikely(netdata_exit)) break; + + + // -------------------------------------------------------------------------- + // this is smart enough not to reload it every time + + mountinfo_reload(0); + + + // -------------------------------------------------------------------------- + // disk space metrics + + struct mountinfo *mi; + for(mi = disk_mountinfo_root; mi; mi = mi->next) { + + if(unlikely(mi->flags & (MOUNTINFO_IS_DUMMY | MOUNTINFO_IS_BIND | MOUNTINFO_IS_SAME_DEV | MOUNTINFO_NO_STAT | MOUNTINFO_NO_SIZE))) + continue; + + do_disk_space_stats(mi, update_every); + if(unlikely(netdata_exit)) break; + } + + if(unlikely(netdata_exit)) break; + + if(vdo_cpu_netdata) { + static RRDSET *stcpu_thread = NULL, *st_duration = NULL; + static RRDDIM *rd_user = NULL, *rd_system = NULL, *rd_duration = NULL; + + // ---------------------------------------------------------------- + + getrusage(RUSAGE_THREAD, &thread); + + if(!stcpu_thread) { + stcpu_thread = rrdset_find("netdata.plugin_diskspace"); + if(!stcpu_thread) stcpu_thread = rrdset_create("netdata", "plugin_diskspace", NULL, "diskspace", NULL + , "NetData Disk Space Plugin CPU usage", "milliseconds/s", 132020 + , update_every, RRDSET_TYPE_STACKED); + + rd_user = rrddim_add(stcpu_thread, "user", NULL, 1, 1000, RRDDIM_INCREMENTAL); + rd_system = rrddim_add(stcpu_thread, "system", NULL, 1, 1000, RRDDIM_INCREMENTAL); + } + else + rrdset_next(stcpu_thread); + + rrddim_set_by_pointer(stcpu_thread, rd_user, thread.ru_utime.tv_sec * 1000000ULL + thread.ru_utime.tv_usec); + rrddim_set_by_pointer(stcpu_thread, rd_system, thread.ru_stime.tv_sec * 1000000ULL + thread.ru_stime.tv_usec); + rrdset_done(stcpu_thread); + + // ---------------------------------------------------------------- + + if(!st_duration) { + st_duration = rrdset_find("netdata.plugin_diskspace_dt"); + if(!st_duration) st_duration = rrdset_create("netdata", "plugin_diskspace_dt", NULL, "diskspace", NULL + , "NetData Disk Space Plugin Duration", "milliseconds/run", 132021 + , update_every, RRDSET_TYPE_AREA); + + rd_duration = rrddim_add(st_duration, "duration", NULL, 1, 1000, RRDDIM_ABSOLUTE); + } + else + rrdset_next(st_duration); + + rrddim_set_by_pointer(st_duration, rd_duration, dt); + rrdset_done(st_duration); + + // ---------------------------------------------------------------- + + if(unlikely(netdata_exit)) break; + } + } + + info("DISKSPACE thread exiting"); + + static_thread->enabled = 0; + pthread_exit(NULL); + return NULL; +} diff --git a/src/plugin_proc_diskspace.h b/src/plugin_proc_diskspace.h new file mode 100644 index 000000000..dcec28f75 --- /dev/null +++ b/src/plugin_proc_diskspace.h @@ -0,0 +1,6 @@ +#ifndef NETDATA_PLUGIN_PROC_DISKSPACE_H +#define NETDATA_PLUGIN_PROC_DISKSPACE_H + +extern void *proc_diskspace_main(void *ptr); + +#endif //NETDATA_PLUGIN_PROC_DISKSPACE_H diff --git a/src/plugin_tc.c b/src/plugin_tc.c index 399fcd6db..0fa595320 100644 --- a/src/plugin_tc.c +++ b/src/plugin_tc.c @@ -44,7 +44,7 @@ struct tc_class { char name_updated; char updated; // updated bytes - int seen; // seen in the tc list (even without bytes) + int unupdated; // the number of times, this has been found un-updated struct tc_class *next; struct tc_class *prev; @@ -100,8 +100,8 @@ avl_tree tc_device_root_index = { tc_device_compare }; -#define tc_device_index_add(st) avl_insert(&tc_device_root_index, (avl *)(st)) -#define tc_device_index_del(st) avl_remove(&tc_device_root_index, (avl *)(st)) +#define tc_device_index_add(st) (struct tc_device *)avl_insert(&tc_device_root_index, (avl *)(st)) +#define tc_device_index_del(st) (struct tc_device *)avl_remove(&tc_device_root_index, (avl *)(st)) static inline struct tc_device *tc_device_index_find(const char *id, uint32_t hash) { struct tc_device tmp; @@ -121,8 +121,8 @@ static int tc_class_compare(void* a, void* b) { else return strcmp(((struct tc_class *)a)->id, ((struct tc_class *)b)->id); } -#define tc_class_index_add(st, rd) avl_insert(&((st)->classes_index), (avl *)(rd)) -#define tc_class_index_del(st, rd) avl_remove(&((st)->classes_index), (avl *)(rd)) +#define tc_class_index_add(st, rd) (struct tc_class *)avl_insert(&((st)->classes_index), (avl *)(rd)) +#define tc_class_index_del(st, rd) (struct tc_class *)avl_remove(&((st)->classes_index), (avl *)(rd)) static inline struct tc_class *tc_class_index_find(struct tc_device *st, const char *id, uint32_t hash) { struct tc_class tmp; @@ -144,9 +144,10 @@ static inline void tc_class_free(struct tc_device *n, struct tc_class *c) { if(c->next) c->next->prev = c->prev; if(c->prev) c->prev->next = c->next; - debug(D_TC_LOOP, "Removing from device '%s' class '%s', parentid '%s', leafid '%s', seen=%d", n->id, c->id, c->parentid?c->parentid:"", c->leafid?c->leafid:"", c->seen); + debug(D_TC_LOOP, "Removing from device '%s' class '%s', parentid '%s', leafid '%s', unused=%d", n->id, c->id, c->parentid?c->parentid:"", c->leafid?c->leafid:"", c->unupdated); - tc_class_index_del(n, c); + if(unlikely(tc_class_index_del(n, c) != c)) + error("plugin_tc: INTERNAL ERROR: attempt remove class '%s' from device '%s': removed a different calls", c->id, n->id); freez(c->id); freez(c->name); @@ -159,7 +160,7 @@ static inline void tc_device_classes_cleanup(struct tc_device *d) { static int cleanup_every = 999; if(unlikely(cleanup_every > 0)) { - cleanup_every = (int) config_get_number("plugin:tc", "cleanup unused classes every", 60); + cleanup_every = (int) config_get_number("plugin:tc", "cleanup unused classes every", 120); if(cleanup_every < 0) cleanup_every = -cleanup_every; } @@ -168,7 +169,7 @@ static inline void tc_device_classes_cleanup(struct tc_device *d) { struct tc_class *c = d->classes; while(c) { - if(unlikely(cleanup_every > 0 && c->seen >= cleanup_every)) { + if(unlikely(cleanup_every && c->unupdated >= cleanup_every)) { struct tc_class *nc = c->next; tc_class_free(d, c); c = nc; @@ -203,6 +204,11 @@ static inline void tc_device_commit(struct tc_device *d) { for(c = d->classes ; c ; c = c->next) { c->isleaf = 1; c->hasparent = 0; + + if(unlikely(!c->updated)) + c->unupdated++; + else + c->unupdated = 0; } // mark the classes as leafs and parents @@ -256,22 +262,22 @@ static inline void tc_device_commit(struct tc_device *d) { if(unlikely(d->enabled == (char)-1)) { char var_name[CONFIG_MAX_NAME + 1]; snprintfz(var_name, CONFIG_MAX_NAME, "qos for %s", d->id); - d->enabled = config_get_boolean_ondemand("plugin:tc", var_name, enable_new_interfaces); + d->enabled = (char)config_get_boolean_ondemand("plugin:tc", var_name, enable_new_interfaces); snprintfz(var_name, CONFIG_MAX_NAME, "traffic chart for %s", d->id); - d->enabled_bytes = config_get_boolean_ondemand("plugin:tc", var_name, enable_bytes); + d->enabled_bytes = (char)config_get_boolean_ondemand("plugin:tc", var_name, enable_bytes); snprintfz(var_name, CONFIG_MAX_NAME, "packets chart for %s", d->id); - d->enabled_packets = config_get_boolean_ondemand("plugin:tc", var_name, enable_packets); + d->enabled_packets = (char)config_get_boolean_ondemand("plugin:tc", var_name, enable_packets); snprintfz(var_name, CONFIG_MAX_NAME, "dropped packets chart for %s", d->id); - d->enabled_dropped = config_get_boolean_ondemand("plugin:tc", var_name, enable_dropped); + d->enabled_dropped = (char)config_get_boolean_ondemand("plugin:tc", var_name, enable_dropped); snprintfz(var_name, CONFIG_MAX_NAME, "tokens chart for %s", d->id); - d->enabled_tokens = config_get_boolean_ondemand("plugin:tc", var_name, enable_tokens); + d->enabled_tokens = (char)config_get_boolean_ondemand("plugin:tc", var_name, enable_tokens); snprintfz(var_name, CONFIG_MAX_NAME, "ctokens chart for %s", d->id); - d->enabled_ctokens = config_get_boolean_ondemand("plugin:tc", var_name, enable_ctokens); + d->enabled_ctokens = (char)config_get_boolean_ondemand("plugin:tc", var_name, enable_ctokens); } debug(D_TC_LOOP, "TC: evaluating TC device '%s'. enabled = %d/%d (bytes: %d/%d, packets: %d/%d, dropped: %d/%d, tokens: %d/%d, ctokens: %d/%d), classes = %d (bytes = %llu, packets = %llu, dropped = %llu, tokens = %llu, ctokens = %llu).", @@ -306,7 +312,7 @@ static inline void tc_device_commit(struct tc_device *d) { } else { debug(D_TC_LOOP, "TC: Updating chart for device '%s'", d->name?d->name:d->id); - rrdset_next_plugins(d->st_bytes); + rrdset_next(d->st_bytes); if(unlikely(d->name_updated && d->name && strcmp(d->id, d->name) != 0)) { rrdset_set_name(d->st_bytes, d->name); @@ -321,8 +327,6 @@ static inline void tc_device_commit(struct tc_device *d) { if(unlikely(!c->updated)) continue; if(c->isleaf && c->hasparent) { - c->seen++; - if(unlikely(!c->rd_bytes)) { c->rd_bytes = rrddim_find(d->st_bytes, c->id); if(unlikely(!c->rd_bytes)) { @@ -367,7 +371,7 @@ static inline void tc_device_commit(struct tc_device *d) { } else { debug(D_TC_LOOP, "TC: Updating _packets chart for device '%s'", d->name?d->name:d->id); - rrdset_next_plugins(d->st_packets); + rrdset_next(d->st_packets); // FIXME // update the family @@ -421,7 +425,7 @@ static inline void tc_device_commit(struct tc_device *d) { } else { debug(D_TC_LOOP, "TC: Updating _dropped chart for device '%s'", d->name?d->name:d->id); - rrdset_next_plugins(d->st_dropped); + rrdset_next(d->st_dropped); // FIXME // update the family @@ -475,7 +479,7 @@ static inline void tc_device_commit(struct tc_device *d) { } else { debug(D_TC_LOOP, "TC: Updating _tokens chart for device '%s'", d->name?d->name:d->id); - rrdset_next_plugins(d->st_tokens); + rrdset_next(d->st_tokens); // FIXME // update the family @@ -529,7 +533,7 @@ static inline void tc_device_commit(struct tc_device *d) { } else { debug(D_TC_LOOP, "TC: Updating _ctokens chart for device '%s'", d->name?d->name:d->id); - rrdset_next_plugins(d->st_ctokens); + rrdset_next(d->st_ctokens); // FIXME // update the family @@ -619,7 +623,8 @@ static inline struct tc_device *tc_device_create(char *id) d->enabled = (char)-1; avl_init(&d->classes_index, tc_class_compare); - tc_device_index_add(d); + if(unlikely(tc_device_index_add(d) != d)) + error("plugin_tc: INTERNAL ERROR: removing device '%s' removed a different device.", d->id); if(!tc_device_root) { tc_device_root = d; @@ -660,11 +665,9 @@ static inline struct tc_class *tc_class_add(struct tc_device *n, char *id, char c->leaf_hash = simple_hash(c->leafid); } - tc_class_index_add(n, c); + if(unlikely(tc_class_index_add(n, c) != c)) + error("plugin_tc: INTERNAL ERROR: attempt index class '%s' on device '%s': already exists", c->id, n->id); } - - c->seen = 1; - return(c); } @@ -677,7 +680,8 @@ static inline void tc_device_free(struct tc_device *n) else tc_device_root = n->prev; } - tc_device_index_del(n); + if(unlikely(tc_device_index_del(n) != n)) + error("plugin_tc: INTERNAL ERROR: removing device '%s' removed a different device.", n->id); while(n->classes) tc_class_free(n, n->classes); @@ -743,9 +747,9 @@ static inline void tc_split_words(char *str, char **words, int max_words) { while(i < max_words) words[i++] = NULL; } -pid_t tc_child_pid = 0; +volatile pid_t tc_child_pid = 0; void *tc_main(void *ptr) { - (void)ptr; + struct netdata_static_thread *static_thread = (struct netdata_static_thread *)ptr; info("TC thread created with task id %d", gettid()); @@ -789,11 +793,10 @@ void *tc_main(void *ptr) { snprintfz(buffer, TC_LINE_MAX, "exec %s %d", tc_script, rrd_update_every); debug(D_TC_LOOP, "executing '%s'", buffer); - fp = mypopen(buffer, &tc_child_pid); + fp = mypopen(buffer, (pid_t *)&tc_child_pid); if(unlikely(!fp)) { error("TC: Cannot popen(\"%s\", \"r\").", buffer); - pthread_exit(NULL); - return NULL; + goto cleanup; } while(fgets(buffer, TC_LINE_MAX, fp) != NULL) { @@ -884,7 +887,7 @@ void *tc_main(void *ptr) { else if(unlikely(device && class && first_hash == SENT_HASH && strcmp(words[0], "Sent") == 0)) { // debug(D_TC_LOOP, "SENT line '%s'", words[1]); if(likely(words[1] && *words[1])) { - class->bytes = strtoull(words[1], NULL, 10); + class->bytes = str2ull(words[1]); class->updated = 1; } else { @@ -892,35 +895,35 @@ void *tc_main(void *ptr) { } if(likely(words[3] && *words[3])) - class->packets = strtoull(words[3], NULL, 10); + class->packets = str2ull(words[3]); if(likely(words[6] && *words[6])) - class->dropped = strtoull(words[6], NULL, 10); + class->dropped = str2ull(words[6]); if(likely(words[8] && *words[8])) - class->overlimits = strtoull(words[8], NULL, 10); + class->overlimits = str2ull(words[8]); if(likely(words[10] && *words[10])) - class->requeues = strtoull(words[8], NULL, 10); + class->requeues = str2ull(words[8]); } else if(unlikely(device && class && class->updated && first_hash == LENDED_HASH && strcmp(words[0], "lended:") == 0)) { // debug(D_TC_LOOP, "LENDED line '%s'", words[1]); if(likely(words[1] && *words[1])) - class->lended = strtoull(words[1], NULL, 10); + class->lended = str2ull(words[1]); if(likely(words[3] && *words[3])) - class->borrowed = strtoull(words[3], NULL, 10); + class->borrowed = str2ull(words[3]); if(likely(words[5] && *words[5])) - class->giants = strtoull(words[5], NULL, 10); + class->giants = str2ull(words[5]); } else if(unlikely(device && class && class->updated && first_hash == TOKENS_HASH && strcmp(words[0], "tokens:") == 0)) { // debug(D_TC_LOOP, "TOKENS line '%s'", words[1]); if(likely(words[1] && *words[1])) - class->tokens = strtoull(words[1], NULL, 10); + class->tokens = str2ull(words[1]); if(likely(words[3] && *words[3])) - class->ctokens = strtoull(words[3], NULL, 10); + class->ctokens = str2ull(words[3]); } else if(unlikely(device && first_hash == SETDEVICENAME_HASH && strcmp(words[0], "SETDEVICENAME") == 0)) { // debug(D_TC_LOOP, "SETDEVICENAME line '%s'", words[1]); @@ -983,7 +986,7 @@ void *tc_main(void *ptr) { } // fgets() failed or loop broke - int code = mypclose(fp, tc_child_pid); + int code = mypclose(fp, (pid_t)tc_child_pid); tc_child_pid = 0; if(unlikely(device)) { @@ -994,8 +997,7 @@ void *tc_main(void *ptr) { if(unlikely(netdata_exit)) { tc_device_free_all(); - pthread_exit(NULL); - return NULL; + goto cleanup; } if(code == 1 || code == 127) { @@ -1004,13 +1006,16 @@ void *tc_main(void *ptr) { error("TC: tc-qos-helper.sh exited with code %d. Disabling it.", code); tc_device_free_all(); - pthread_exit(NULL); - return NULL; + goto cleanup; } sleep((unsigned int) rrd_update_every); } +cleanup: + info("TC thread exiting"); + + static_thread->enabled = 0; pthread_exit(NULL); return NULL; } diff --git a/src/plugin_tc.h b/src/plugin_tc.h index c3abbddd0..9a0a19cce 100644 --- a/src/plugin_tc.h +++ b/src/plugin_tc.h @@ -1,7 +1,7 @@ #ifndef NETDATA_PLUGIN_TC_H #define NETDATA_PLUGIN_TC_H 1 -extern pid_t tc_child_pid; +extern volatile pid_t tc_child_pid; extern void *tc_main(void *ptr); #endif /* NETDATA_PLUGIN_TC_H */ diff --git a/src/plugins_d.c b/src/plugins_d.c index 0030e2216..4b83b5281 100644 --- a/src/plugins_d.c +++ b/src/plugins_d.c @@ -87,15 +87,16 @@ static int pluginsd_split_words(char *str, char **words, int max_words) { void *pluginsd_worker_thread(void *arg) { struct plugind *cd = (struct plugind *)arg; + cd->obsolete = 0; + char line[PLUGINSD_LINE_MAX + 1]; #ifdef DETACH_PLUGINS_FROM_NETDATA - unsigned long long usec = 0, susec = 0; + usec_t usec = 0, susec = 0; struct timeval last = {0, 0} , now = {0, 0}; #endif char *words[MAX_WORDS] = { NULL }; - uint32_t SET_HASH = simple_hash("SET"); uint32_t BEGIN_HASH = simple_hash("BEGIN"); uint32_t END_HASH = simple_hash("END"); uint32_t FLUSH_HASH = simple_hash("FLUSH"); @@ -121,7 +122,6 @@ void *pluginsd_worker_thread(void *arg) info("PLUGINSD: '%s' running on pid %d", cd->fullfilename, cd->pid); RRDSET *st = NULL; - char *s; uint32_t hash; while(likely(fgets(line, PLUGINSD_LINE_MAX, fp) != NULL)) { @@ -132,7 +132,7 @@ void *pluginsd_worker_thread(void *arg) // debug(D_PLUGINSD, "PLUGINSD: %s: %s", cd->filename, line); int w = pluginsd_split_words(line, words, MAX_WORDS); - s = words[0]; + char *s = words[0]; if(unlikely(!s || !*s || !w)) { // debug(D_PLUGINSD, "PLUGINSD: empty line"); continue; @@ -140,9 +140,7 @@ void *pluginsd_worker_thread(void *arg) // debug(D_PLUGINSD, "PLUGINSD: words 0='%s' 1='%s' 2='%s' 3='%s' 4='%s' 5='%s' 6='%s' 7='%s' 8='%s' 9='%s'", words[0], words[1], words[2], words[3], words[4], words[5], words[6], words[7], words[8], words[9]); - hash = simple_hash(s); - - if(likely(hash == SET_HASH && !strcmp(s, "SET"))) { + if(likely(!simple_hash_strcmp(s, "SET", &hash))) { char *dimension = words[1]; char *value = words[2]; @@ -186,10 +184,10 @@ void *pluginsd_worker_thread(void *arg) } if(likely(st->counter_done)) { - unsigned long long microseconds = 0; - if(microseconds_txt && *microseconds_txt) microseconds = strtoull(microseconds_txt, NULL, 10); + usec_t microseconds = 0; + if(microseconds_txt && *microseconds_txt) microseconds = str2ull(microseconds_txt); if(microseconds) rrdset_next_usec(st, microseconds); - else rrdset_next_plugins(st); + else rrdset_next(st); } } else if(likely(hash == END_HASH && !strcmp(s, "END"))) { @@ -241,10 +239,10 @@ void *pluginsd_worker_thread(void *arg) } int priority = 1000; - if(likely(priority_s)) priority = atoi(priority_s); + if(likely(priority_s)) priority = str2i(priority_s); int update_every = cd->update_every; - if(likely(update_every_s)) update_every = atoi(update_every_s); + if(likely(update_every_s)) update_every = str2i(update_every_s); if(unlikely(!update_every)) update_every = cd->update_every; int chart_type = RRDSET_TYPE_LINE; @@ -326,7 +324,7 @@ void *pluginsd_worker_thread(void *arg) else if(unlikely(st->debug)) debug(D_PLUGINSD, "PLUGINSD: dimension %s/%s already exists. Not adding it again.", st->id, id); } else if(unlikely(hash == DISABLE_HASH && !strcmp(s, "DISABLE"))) { - error("PLUGINSD: '%s' called DISABLE. Disabling it.", cd->fullfilename); + info("PLUGINSD: '%s' called DISABLE. Disabling it.", cd->fullfilename); cd->enabled = 0; killpid(cd->pid, SIGTERM); break; @@ -342,17 +340,17 @@ void *pluginsd_worker_thread(void *arg) else if(likely(hash == STOPPING_WAKE_ME_UP_PLEASE_HASH && !strcmp(s, "STOPPING_WAKE_ME_UP_PLEASE"))) { error("PLUGINSD: '%s' (pid %d) called STOPPING_WAKE_ME_UP_PLEASE.", cd->fullfilename, cd->pid); - gettimeofday(&now, NULL); + now_realtime_timeval(&now); if(unlikely(!usec && !susec)) { // our first run - susec = cd->rrd_update_every * 1000000ULL; + susec = cd->rrd_update_every * USEC_PER_SEC; } else { // second+ run - usec = usec_dt(&now, &last) - susec; + usec = dt_usec(&now, &last) - susec; error("PLUGINSD: %s last loop took %llu usec (worked for %llu, sleeped for %llu).\n", cd->fullfilename, usec + susec, usec, susec); - if(unlikely(usec < (rrd_update_every * 1000000ULL / 2ULL))) susec = (rrd_update_every * 1000000ULL) - usec; - else susec = rrd_update_every * 1000000ULL / 2ULL; + if(unlikely(usec < (rrd_update_every * USEC_PER_SEC / 2ULL))) susec = (rrd_update_every * USEC_PER_SEC) - usec; + else susec = rrd_update_every * USEC_PER_SEC / 2ULL; } error("PLUGINSD: %s sleeping for %llu. Will kill with SIGCONT pid %d to wake it up.\n", cd->fullfilename, susec, cd->pid); @@ -394,7 +392,7 @@ void *pluginsd_worker_thread(void *arg) // we have collected something if(likely(cd->serial_failures <= 10)) { - error("PLUGINSD: '%s' exited with error code %d, but has given useful output in the past (%zu times). Waiting a bit before starting it again.", cd->fullfilename, code, cd->successful_collections); + error("PLUGINSD: '%s' exited with error code %d, but has given useful output in the past (%zu times). %s", cd->fullfilename, code, cd->successful_collections, cd->enabled?"Waiting a bit before starting it again.":"Will not start it again - it is disabled."); sleep((unsigned int) (cd->update_every * 10)); } else { @@ -410,7 +408,7 @@ void *pluginsd_worker_thread(void *arg) // we have collected nothing so far if(likely(cd->serial_failures <= 10)) { - error("PLUGINSD: '%s' (pid %d) does not generate useful output but it reports success (exits with 0). Waiting a bit before starting it again.", cd->fullfilename, cd->pid); + error("PLUGINSD: '%s' (pid %d) does not generate useful output but it reports success (exits with 0). %s.", cd->fullfilename, cd->pid, cd->enabled?"Waiting a bit before starting it again.":"Will not start it again - it is disabled."); sleep((unsigned int) (cd->update_every * 10)); } else { @@ -434,7 +432,7 @@ void *pluginsd_worker_thread(void *arg) } void *pluginsd_main(void *ptr) { - (void)ptr; + struct netdata_static_thread *static_thread = (struct netdata_static_thread *)ptr; info("PLUGINS.D thread created with task id %d", gettid()); @@ -462,8 +460,7 @@ void *pluginsd_main(void *ptr) { dir = opendir(dir_name); if(unlikely(!dir)) { error("Cannot open directory '%s'.", dir_name); - pthread_exit(NULL); - return NULL; + goto cleanup; } while(likely((file = readdir(dir)))) { @@ -490,9 +487,9 @@ void *pluginsd_main(void *ptr) { } // check if it runs already - for(cd = pluginsd_root ; likely(cd) ; cd = cd->next) { + for(cd = pluginsd_root ; cd ; cd = cd->next) if(unlikely(strcmp(cd->filename, file->d_name) == 0)) break; - } + if(likely(cd && !cd->obsolete)) { debug(D_PLUGINSD, "PLUGINSD: plugin '%s' is already running", cd->filename); continue; @@ -510,7 +507,7 @@ void *pluginsd_main(void *ptr) { cd->enabled = enabled; cd->update_every = (int) config_get_number(cd->id, "update every", rrd_update_every); - cd->started_t = time(NULL); + cd->started_t = now_realtime_sec(); char *def = ""; snprintfz(cd->cmd, PLUGINSD_CMD_MAX, "exec %s %d %s", cd->fullfilename, cd->update_every, config_get(cd->id, "command options", def)); @@ -518,26 +515,29 @@ void *pluginsd_main(void *ptr) { // link it if(likely(pluginsd_root)) cd->next = pluginsd_root; pluginsd_root = cd; - } - cd->obsolete = 0; - if(unlikely(!cd->enabled)) continue; - - // spawn a new thread for it - if(unlikely(pthread_create(&cd->thread, NULL, pluginsd_worker_thread, cd) != 0)) { - error("PLUGINSD: failed to create new thread for plugin '%s'.", cd->filename); + // it is not currently running cd->obsolete = 1; + + if(cd->enabled) { + // spawn a new thread for it + if(unlikely(pthread_create(&cd->thread, NULL, pluginsd_worker_thread, cd) != 0)) + error("PLUGINSD: failed to create new thread for plugin '%s'.", cd->filename); + + else if(unlikely(pthread_detach(cd->thread) != 0)) + error("PLUGINSD: Cannot request detach of newly created thread for plugin '%s'.", cd->filename); + } } - else if(unlikely(pthread_detach(cd->thread) != 0)) - error("PLUGINSD: Cannot request detach of newly created thread for plugin '%s'.", cd->filename); } closedir(dir); sleep((unsigned int) scan_frequency); } +cleanup: info("PLUGINS.D thread exiting"); + static_thread->enabled = 0; pthread_exit(NULL); return NULL; } diff --git a/src/plugins_d.h b/src/plugins_d.h index 6f1fbd6e1..3c74355a3 100644 --- a/src/plugins_d.h +++ b/src/plugins_d.h @@ -23,8 +23,8 @@ struct plugind { // without collecting values int update_every; // the plugin default data collection frequency - int obsolete; // do not touch this structure after setting this to 1 - int enabled; // if this is enabled or not + volatile int obsolete; // do not touch this structure after setting this to 1 + volatile int enabled; // if this is enabled or not time_t started_t; diff --git a/src/popen.c b/src/popen.c index ad8d7596f..8448b7311 100644 --- a/src/popen.c +++ b/src/popen.c @@ -138,7 +138,7 @@ FILE *mypopen(const char *command, pid_t *pidptr) error("pre-execution of command '%s' on pid %d: failed to set default signal handler for SIGUSR2.", command, getpid()); } - info("executing command: '%s' on pid %d.", command, getpid()); + debug(D_CHILDS, "executing command: '%s' on pid %d.", command, getpid()); execl("/bin/sh", "sh", "-c", command, NULL); exit(1); } diff --git a/src/proc_diskstats.c b/src/proc_diskstats.c index 459d5a133..9ccac6dc7 100644 --- a/src/proc_diskstats.c +++ b/src/proc_diskstats.c @@ -12,6 +12,7 @@ static struct disk { unsigned long minor; int sector_size; int type; + char *mount_point; // disk options caching @@ -23,18 +24,16 @@ static struct disk { int do_qops; int do_util; int do_backlog; - int do_space; - int do_inodes; struct disk *next; } *disk_root = NULL; -static struct mountinfo *disk_mountinfo_root = NULL; - static struct disk *get_disk(unsigned long major, unsigned long minor, char *disk) { static char path_to_get_hw_sector_size[FILENAME_MAX + 1] = ""; static char path_to_get_hw_sector_size_partitions[FILENAME_MAX + 1] = ""; static char path_find_block_device[FILENAME_MAX + 1] = ""; + static struct mountinfo *disk_mountinfo_root = NULL; + struct disk *d; // search for it in our RAM list. @@ -43,11 +42,7 @@ static struct disk *get_disk(unsigned long major, unsigned long minor, char *dis // should not be that many, it should be acceptable for(d = disk_root; d ; d = d->next) if(unlikely(d->major == major && d->minor == minor)) - break; - - // if we found it, return it - if(likely(d)) - return d; + return d; // not found // create a new disk structure @@ -62,7 +57,7 @@ static struct disk *get_disk(unsigned long major, unsigned long minor, char *dis d->next = NULL; // append it to the list - if(!disk_root) + if(unlikely(!disk_root)) disk_root = d; else { struct disk *last; @@ -84,18 +79,19 @@ static struct disk *get_disk(unsigned long major, unsigned long minor, char *dis // find if it is a partition // by checking if /sys/dev/block/MAJOR:MINOR/partition is readable. snprintfz(buffer, FILENAME_MAX, path_find_block_device, major, minor, "partition"); - if(access(buffer, R_OK) == 0) { + if(likely(access(buffer, R_OK) == 0)) { d->type = DISK_TYPE_PARTITION; - } else { + } + else { // find if it is a container // by checking if /sys/dev/block/MAJOR:MINOR/slaves has entries snprintfz(buffer, FILENAME_MAX, path_find_block_device, major, minor, "slaves/"); - DIR *dirp = opendir(buffer); - if (dirp != NULL) { + DIR *dirp = opendir(buffer); + if(likely(dirp != NULL)) { struct dirent *dp; while( (dp = readdir(dirp)) ) { // . and .. are also files in empty folders. - if(strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0) { + if(unlikely(strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0)) { continue; } @@ -104,7 +100,7 @@ static struct disk *get_disk(unsigned long major, unsigned long minor, char *dis // Stop the loop after we found one file. break; } - if(closedir(dirp) == -1) + if(unlikely(closedir(dirp) == -1)) error("Unable to close dir %s", buffer); } } @@ -115,30 +111,25 @@ static struct disk *get_disk(unsigned long major, unsigned long minor, char *dis // mountinfo_find() can be called with NULL disk_mountinfo_root struct mountinfo *mi = mountinfo_find(disk_mountinfo_root, d->major, d->minor); if(unlikely(!mi)) { - // mountinfo_free() can be called with NULL disk_mountinfo_root + // mountinfo_free can be called with NULL mountinfo_free(disk_mountinfo_root); - - // re-read mountinfo in case something changed - disk_mountinfo_root = mountinfo_read(); - - // search again for this disk + disk_mountinfo_root = mountinfo_read(0); mi = mountinfo_find(disk_mountinfo_root, d->major, d->minor); } - if(mi) + if(unlikely(mi)) d->mount_point = strdupz(mi->mount_point); - // no need to check for NULL else d->mount_point = NULL; // ------------------------------------------------------------------------ // find the disk sector size - if(!path_to_get_hw_sector_size[0]) { + if(unlikely(!path_to_get_hw_sector_size[0])) { snprintfz(buffer, FILENAME_MAX, "%s%s", global_host_prefix, "/sys/block/%s/queue/hw_sector_size"); snprintfz(path_to_get_hw_sector_size, FILENAME_MAX, "%s", config_get("plugin:proc:/proc/diskstats", "path to get h/w sector size", buffer)); } - if(!path_to_get_hw_sector_size_partitions[0]) { + if(unlikely(!path_to_get_hw_sector_size_partitions[0])) { snprintfz(buffer, FILENAME_MAX, "%s%s", global_host_prefix, "/sys/dev/block/%lu:%lu/subsystem/%s/../queue/hw_sector_size"); snprintfz(path_to_get_hw_sector_size_partitions, FILENAME_MAX, "%s", config_get("plugin:proc:/proc/diskstats", "path to get h/w sector size for partitions", buffer)); } @@ -149,21 +140,21 @@ static struct disk *get_disk(unsigned long major, unsigned long minor, char *dis // replace all / with ! for(t = tf; *t ;t++) - if(*t == '/') *t = '!'; + if(unlikely(*t == '/')) *t = '!'; - if(d->type == DISK_TYPE_PARTITION) + if(likely(d->type == DISK_TYPE_PARTITION)) snprintfz(buffer, FILENAME_MAX, path_to_get_hw_sector_size_partitions, d->major, d->minor, tf); else snprintfz(buffer, FILENAME_MAX, path_to_get_hw_sector_size, tf); FILE *fpss = fopen(buffer, "r"); - if(fpss) { + if(likely(fpss)) { char buffer2[1024 + 1]; char *tmp = fgets(buffer2, 1024, fpss); - if(tmp) { - d->sector_size = atoi(tmp); - if(d->sector_size <= 0) { + if(likely(tmp)) { + d->sector_size = str2i(tmp); + if(unlikely(d->sector_size <= 0)) { error("Invalid sector size %d for device %s in %s. Assuming 512.", d->sector_size, d->disk, buffer); d->sector_size = 512; } @@ -178,26 +169,41 @@ static struct disk *get_disk(unsigned long major, unsigned long minor, char *dis return d; } -static inline int select_positive_option(int option1, int option2) { - if(option1 == CONFIG_ONDEMAND_YES || option2 == CONFIG_ONDEMAND_YES) - return CONFIG_ONDEMAND_YES; - else if(option1 == CONFIG_ONDEMAND_ONDEMAND || option2 == CONFIG_ONDEMAND_ONDEMAND) - return CONFIG_ONDEMAND_ONDEMAND; +static inline int is_major_enabled(int major) { + static char *major_configs = NULL; + static size_t major_size = 0; + + if(major < 0) return 1; - return CONFIG_ONDEMAND_NO; + size_t wanted_size = (size_t)major + 1; + + if(major_size < wanted_size) { + major_configs = reallocz(major_configs, wanted_size); + + size_t i; + for(i = major_size; i < wanted_size ; i++) + major_configs[i] = -1; + + major_size = wanted_size; + } + + if(major_configs[major] == -1) { + char buffer[CONFIG_MAX_NAME + 1]; + snprintfz(buffer, CONFIG_MAX_NAME, "performance metrics for disks with major %d", major); + major_configs[major] = (char)config_get_boolean("plugin:proc:/proc/diskstats", buffer, 1); + } + + return major_configs[major]; } -int do_proc_diskstats(int update_every, unsigned long long dt) { +int do_proc_diskstats(int update_every, usec_t dt) { + (void)dt; + static procfile *ff = NULL; - static struct statvfs buff_statvfs; - static struct stat buff_stat; static int global_enable_new_disks_detected_at_runtime = CONFIG_ONDEMAND_YES, global_enable_performance_for_physical_disks = CONFIG_ONDEMAND_ONDEMAND, - global_enable_performance_for_virtual_disks = CONFIG_ONDEMAND_NO, + global_enable_performance_for_virtual_disks = CONFIG_ONDEMAND_ONDEMAND, global_enable_performance_for_partitions = CONFIG_ONDEMAND_NO, - global_enable_performance_for_mountpoints = CONFIG_ONDEMAND_NO, - global_enable_performance_for_virtual_mountpoints = CONFIG_ONDEMAND_ONDEMAND, - global_enable_space_for_mountpoints = CONFIG_ONDEMAND_ONDEMAND, global_do_io = CONFIG_ONDEMAND_ONDEMAND, global_do_ops = CONFIG_ONDEMAND_ONDEMAND, global_do_mops = CONFIG_ONDEMAND_ONDEMAND, @@ -205,8 +211,6 @@ int do_proc_diskstats(int update_every, unsigned long long dt) { global_do_qops = CONFIG_ONDEMAND_ONDEMAND, global_do_util = CONFIG_ONDEMAND_ONDEMAND, global_do_backlog = CONFIG_ONDEMAND_ONDEMAND, - global_do_space = CONFIG_ONDEMAND_ONDEMAND, - global_do_inodes = CONFIG_ONDEMAND_ONDEMAND, globals_initialized = 0; if(unlikely(!globals_initialized)) { @@ -215,9 +219,6 @@ int do_proc_diskstats(int update_every, unsigned long long dt) { global_enable_performance_for_physical_disks = config_get_boolean_ondemand("plugin:proc:/proc/diskstats", "performance metrics for physical disks", global_enable_performance_for_physical_disks); global_enable_performance_for_virtual_disks = config_get_boolean_ondemand("plugin:proc:/proc/diskstats", "performance metrics for virtual disks", global_enable_performance_for_virtual_disks); global_enable_performance_for_partitions = config_get_boolean_ondemand("plugin:proc:/proc/diskstats", "performance metrics for partitions", global_enable_performance_for_partitions); - global_enable_performance_for_mountpoints = config_get_boolean_ondemand("plugin:proc:/proc/diskstats", "performance metrics for mounted filesystems", global_enable_performance_for_mountpoints); - global_enable_performance_for_virtual_mountpoints = config_get_boolean_ondemand("plugin:proc:/proc/diskstats", "performance metrics for mounted virtual disks", global_enable_performance_for_virtual_mountpoints); - global_enable_space_for_mountpoints = config_get_boolean_ondemand("plugin:proc:/proc/diskstats", "space metrics for mounted filesystems", global_enable_space_for_mountpoints); global_do_io = config_get_boolean_ondemand("plugin:proc:/proc/diskstats", "bandwidth for all disks", global_do_io); global_do_ops = config_get_boolean_ondemand("plugin:proc:/proc/diskstats", "operations for all disks", global_do_ops); @@ -226,79 +227,77 @@ int do_proc_diskstats(int update_every, unsigned long long dt) { global_do_qops = config_get_boolean_ondemand("plugin:proc:/proc/diskstats", "queued operations for all disks", global_do_qops); global_do_util = config_get_boolean_ondemand("plugin:proc:/proc/diskstats", "utilization percentage for all disks", global_do_util); global_do_backlog = config_get_boolean_ondemand("plugin:proc:/proc/diskstats", "backlog for all disks", global_do_backlog); - global_do_space = config_get_boolean_ondemand("plugin:proc:/proc/diskstats", "space usage for all disks", global_do_space); - global_do_inodes = config_get_boolean_ondemand("plugin:proc:/proc/diskstats", "inodes usage for all disks", global_do_inodes); globals_initialized = 1; } - if(!ff) { + // -------------------------------------------------------------------------- + + if(unlikely(!ff)) { char filename[FILENAME_MAX + 1]; snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/diskstats"); ff = procfile_open(config_get("plugin:proc:/proc/diskstats", "filename to monitor", filename), " \t", PROCFILE_FLAG_DEFAULT); } - if(!ff) return 1; + if(unlikely(!ff)) return 0; ff = procfile_readall(ff); - if(!ff) return 0; // we return 0, so that we will retry to open it next time + if(unlikely(!ff)) return 0; // we return 0, so that we will retry to open it next time - uint32_t lines = procfile_lines(ff), l; - uint32_t words; + size_t lines = procfile_lines(ff), l; for(l = 0; l < lines ;l++) { // -------------------------------------------------------------------------- // Read parameters char *disk; - unsigned long long major = 0, minor = 0, - reads = 0, mreads = 0, readsectors = 0, readms = 0, + unsigned long major = 0, minor = 0; + + collected_number reads = 0, mreads = 0, readsectors = 0, readms = 0, writes = 0, mwrites = 0, writesectors = 0, writems = 0, - queued_ios = 0, busy_ms = 0, backlog_ms = 0, - space_avail = 0, space_avail_root = 0, space_used = 0, - inodes_avail = 0, inodes_avail_root = 0, inodes_used = 0; + queued_ios = 0, busy_ms = 0, backlog_ms = 0; - unsigned long long last_reads = 0, last_readsectors = 0, last_readms = 0, + collected_number last_reads = 0, last_readsectors = 0, last_readms = 0, last_writes = 0, last_writesectors = 0, last_writems = 0, last_busy_ms = 0; - words = procfile_linewords(ff, l); - if(words < 14) continue; + size_t words = procfile_linewords(ff, l); + if(unlikely(words < 14)) continue; - major = strtoull(procfile_lineword(ff, l, 0), NULL, 10); - minor = strtoull(procfile_lineword(ff, l, 1), NULL, 10); + major = str2ul(procfile_lineword(ff, l, 0)); + minor = str2ul(procfile_lineword(ff, l, 1)); disk = procfile_lineword(ff, l, 2); // # of reads completed # of writes completed // This is the total number of reads or writes completed successfully. - reads = strtoull(procfile_lineword(ff, l, 3), NULL, 10); // rd_ios - writes = strtoull(procfile_lineword(ff, l, 7), NULL, 10); // wr_ios + reads = str2ull(procfile_lineword(ff, l, 3)); // rd_ios + writes = str2ull(procfile_lineword(ff, l, 7)); // wr_ios // # of reads merged # of writes merged // Reads and writes which are adjacent to each other may be merged for // efficiency. Thus two 4K reads may become one 8K read before it is // ultimately handed to the disk, and so it will be counted (and queued) - mreads = strtoull(procfile_lineword(ff, l, 4), NULL, 10); // rd_merges_or_rd_sec - mwrites = strtoull(procfile_lineword(ff, l, 8), NULL, 10); // wr_merges + mreads = str2ull(procfile_lineword(ff, l, 4)); // rd_merges_or_rd_sec + mwrites = str2ull(procfile_lineword(ff, l, 8)); // wr_merges // # of sectors read # of sectors written // This is the total number of sectors read or written successfully. - readsectors = strtoull(procfile_lineword(ff, l, 5), NULL, 10); // rd_sec_or_wr_ios - writesectors = strtoull(procfile_lineword(ff, l, 9), NULL, 10); // wr_sec + readsectors = str2ull(procfile_lineword(ff, l, 5)); // rd_sec_or_wr_ios + writesectors = str2ull(procfile_lineword(ff, l, 9)); // wr_sec // # of milliseconds spent reading # of milliseconds spent writing // This is the total number of milliseconds spent by all reads or writes (as // measured from __make_request() to end_that_request_last()). - readms = strtoull(procfile_lineword(ff, l, 6), NULL, 10); // rd_ticks_or_wr_sec - writems = strtoull(procfile_lineword(ff, l, 10), NULL, 10); // wr_ticks + readms = str2ull(procfile_lineword(ff, l, 6)); // rd_ticks_or_wr_sec + writems = str2ull(procfile_lineword(ff, l, 10)); // wr_ticks // # of I/Os currently in progress // The only field that should go to zero. Incremented as requests are // given to appropriate struct request_queue and decremented as they finish. - queued_ios = strtoull(procfile_lineword(ff, l, 11), NULL, 10); // ios_pgr + queued_ios = str2ull(procfile_lineword(ff, l, 11)); // ios_pgr // # of milliseconds spent doing I/Os // This field increases so long as field queued_ios is nonzero. - busy_ms = strtoull(procfile_lineword(ff, l, 12), NULL, 10); // tot_ticks + busy_ms = str2ull(procfile_lineword(ff, l, 12)); // tot_ticks // weighted # of milliseconds spent doing I/Os // This field is incremented at each I/O start, I/O completion, I/O @@ -306,7 +305,7 @@ int do_proc_diskstats(int update_every, unsigned long long dt) { // (field queued_ios) times the number of milliseconds spent doing I/O since the // last update of this field. This can provide an easy measure of both // I/O completion time and the backlog that may be accumulating. - backlog_ms = strtoull(procfile_lineword(ff, l, 13), NULL, 10); // rq_ticks + backlog_ms = str2ull(procfile_lineword(ff, l, 13)); // rq_ticks // -------------------------------------------------------------------------- @@ -336,7 +335,7 @@ int do_proc_diskstats(int update_every, unsigned long long dt) { snprintfz(var_name, 4096, "plugin:proc:/proc/diskstats:%s", disk); int def_enable = config_get_boolean_ondemand(var_name, "enable", global_enable_new_disks_detected_at_runtime); - if(def_enable == CONFIG_ONDEMAND_NO) { + if(unlikely(def_enable == CONFIG_ONDEMAND_NO)) { // the user does not want any metrics for this disk d->do_io = CONFIG_ONDEMAND_NO; d->do_ops = CONFIG_ONDEMAND_NO; @@ -345,15 +344,12 @@ int do_proc_diskstats(int update_every, unsigned long long dt) { d->do_qops = CONFIG_ONDEMAND_NO; d->do_util = CONFIG_ONDEMAND_NO; d->do_backlog = CONFIG_ONDEMAND_NO; - d->do_space = CONFIG_ONDEMAND_NO; - d->do_inodes = CONFIG_ONDEMAND_NO; } else { // this disk is enabled // check its direct settings int def_performance = CONFIG_ONDEMAND_ONDEMAND; - int def_space = (d->mount_point)?CONFIG_ONDEMAND_ONDEMAND:CONFIG_ONDEMAND_NO; // since this is 'on demand' we can figure the performance settings // based on the type of disk @@ -369,15 +365,12 @@ int do_proc_diskstats(int update_every, unsigned long long dt) { case DISK_TYPE_CONTAINER: def_performance = global_enable_performance_for_virtual_disks; - - if(d->mount_point) - def_performance = select_positive_option(def_performance, global_enable_performance_for_virtual_mountpoints); - break; } - if(d->mount_point) - def_performance = select_positive_option(def_performance, global_enable_performance_for_mountpoints); + // check if we have to disable performance for this disk + if(def_performance) + def_performance = is_major_enabled((int)major); // ------------------------------------------------------------ // now we have def_performance and def_space @@ -396,7 +389,7 @@ int do_proc_diskstats(int update_every, unsigned long long dt) { ddo_backlog = CONFIG_ONDEMAND_NO; // we enable individual performance charts only when def_performance is not disabled - if(def_performance != CONFIG_ONDEMAND_NO) { + if(unlikely(def_performance != CONFIG_ONDEMAND_NO)) { ddo_io = global_do_io, ddo_ops = global_do_ops, ddo_mops = global_do_mops, @@ -413,23 +406,6 @@ int do_proc_diskstats(int update_every, unsigned long long dt) { d->do_qops = config_get_boolean_ondemand(var_name, "queued operations", ddo_qops); d->do_util = config_get_boolean_ondemand(var_name, "utilization percentage", ddo_util); d->do_backlog = config_get_boolean_ondemand(var_name, "backlog", ddo_backlog); - - // def_space - if(d->mount_point) { - // check the user configuration (this will also show our 'on demand' decision) - def_space = config_get_boolean_ondemand(var_name, "enable space metrics", def_space); - - int ddo_space = def_space, - ddo_inodes = def_space; - - d->do_space = config_get_boolean_ondemand(var_name, "space usage", ddo_space); - d->do_inodes = config_get_boolean_ondemand(var_name, "inodes usage", ddo_inodes); - } - else { - // don't show settings for this disk - d->do_space = CONFIG_ONDEMAND_NO; - d->do_inodes = CONFIG_ONDEMAND_NO; - } } d->configured = 1; @@ -444,13 +420,13 @@ int do_proc_diskstats(int update_every, unsigned long long dt) { d->do_io = CONFIG_ONDEMAND_YES; st = rrdset_find_bytype(RRD_TYPE_DISK, disk); - if(!st) { + if(unlikely(!st)) { st = rrdset_create(RRD_TYPE_DISK, disk, NULL, family, "disk.io", "Disk I/O Bandwidth", "kilobytes/s", 2000, update_every, RRDSET_TYPE_AREA); rrddim_add(st, "reads", NULL, d->sector_size, 1024, RRDDIM_INCREMENTAL); rrddim_add(st, "writes", NULL, d->sector_size * -1, 1024, RRDDIM_INCREMENTAL); } - else rrdset_next_usec(st, dt); + else rrdset_next(st); last_readsectors = rrddim_set(st, "reads", readsectors); last_writesectors = rrddim_set(st, "writes", writesectors); @@ -463,14 +439,14 @@ int do_proc_diskstats(int update_every, unsigned long long dt) { d->do_ops = CONFIG_ONDEMAND_YES; st = rrdset_find_bytype("disk_ops", disk); - if(!st) { + if(unlikely(!st)) { st = rrdset_create("disk_ops", disk, NULL, family, "disk.ops", "Disk Completed I/O Operations", "operations/s", 2001, update_every, RRDSET_TYPE_LINE); st->isdetail = 1; rrddim_add(st, "reads", NULL, 1, 1, RRDDIM_INCREMENTAL); rrddim_add(st, "writes", NULL, -1, 1, RRDDIM_INCREMENTAL); } - else rrdset_next_usec(st, dt); + else rrdset_next(st); last_reads = rrddim_set(st, "reads", reads); last_writes = rrddim_set(st, "writes", writes); @@ -483,13 +459,13 @@ int do_proc_diskstats(int update_every, unsigned long long dt) { d->do_qops = CONFIG_ONDEMAND_YES; st = rrdset_find_bytype("disk_qops", disk); - if(!st) { + if(unlikely(!st)) { st = rrdset_create("disk_qops", disk, NULL, family, "disk.qops", "Disk Current I/O Operations", "operations", 2002, update_every, RRDSET_TYPE_LINE); st->isdetail = 1; rrddim_add(st, "operations", NULL, 1, 1, RRDDIM_ABSOLUTE); } - else rrdset_next_usec(st, dt); + else rrdset_next(st); rrddim_set(st, "operations", queued_ios); rrdset_done(st); @@ -501,13 +477,13 @@ int do_proc_diskstats(int update_every, unsigned long long dt) { d->do_backlog = CONFIG_ONDEMAND_YES; st = rrdset_find_bytype("disk_backlog", disk); - if(!st) { + if(unlikely(!st)) { st = rrdset_create("disk_backlog", disk, NULL, family, "disk.backlog", "Disk Backlog", "backlog (ms)", 2003, update_every, RRDSET_TYPE_AREA); st->isdetail = 1; rrddim_add(st, "backlog", NULL, 1, 10, RRDDIM_INCREMENTAL); } - else rrdset_next_usec(st, dt); + else rrdset_next(st); rrddim_set(st, "backlog", backlog_ms); rrdset_done(st); @@ -519,13 +495,13 @@ int do_proc_diskstats(int update_every, unsigned long long dt) { d->do_util = CONFIG_ONDEMAND_YES; st = rrdset_find_bytype("disk_util", disk); - if(!st) { + if(unlikely(!st)) { st = rrdset_create("disk_util", disk, NULL, family, "disk.util", "Disk Utilization Time", "% of time working", 2004, update_every, RRDSET_TYPE_AREA); st->isdetail = 1; rrddim_add(st, "utilization", NULL, 1, 10, RRDDIM_INCREMENTAL); } - else rrdset_next_usec(st, dt); + else rrdset_next(st); last_busy_ms = rrddim_set(st, "utilization", busy_ms); rrdset_done(st); @@ -537,14 +513,14 @@ int do_proc_diskstats(int update_every, unsigned long long dt) { d->do_mops = CONFIG_ONDEMAND_YES; st = rrdset_find_bytype("disk_mops", disk); - if(!st) { + if(unlikely(!st)) { st = rrdset_create("disk_mops", disk, NULL, family, "disk.mops", "Disk Merged Operations", "merged operations/s", 2021, update_every, RRDSET_TYPE_LINE); st->isdetail = 1; rrddim_add(st, "reads", NULL, 1, 1, RRDDIM_INCREMENTAL); rrddim_add(st, "writes", NULL, -1, 1, RRDDIM_INCREMENTAL); } - else rrdset_next_usec(st, dt); + else rrdset_next(st); rrddim_set(st, "reads", mreads); rrddim_set(st, "writes", mwrites); @@ -557,14 +533,14 @@ int do_proc_diskstats(int update_every, unsigned long long dt) { d->do_iotime = CONFIG_ONDEMAND_YES; st = rrdset_find_bytype("disk_iotime", disk); - if(!st) { + if(unlikely(!st)) { st = rrdset_create("disk_iotime", disk, NULL, family, "disk.iotime", "Disk Total I/O Time", "milliseconds/s", 2022, update_every, RRDSET_TYPE_LINE); st->isdetail = 1; rrddim_add(st, "reads", NULL, 1, 1, RRDDIM_INCREMENTAL); rrddim_add(st, "writes", NULL, -1, 1, RRDDIM_INCREMENTAL); } - else rrdset_next_usec(st, dt); + else rrdset_next(st); last_readms = rrddim_set(st, "reads", readms); last_writems = rrddim_set(st, "writes", writems); @@ -575,18 +551,18 @@ int do_proc_diskstats(int update_every, unsigned long long dt) { // calculate differential charts // only if this is not the first time we run - if(dt) { + if(likely(dt)) { if( (d->do_iotime == CONFIG_ONDEMAND_YES || (d->do_iotime == CONFIG_ONDEMAND_ONDEMAND && (readms || writems))) && (d->do_ops == CONFIG_ONDEMAND_YES || (d->do_ops == CONFIG_ONDEMAND_ONDEMAND && (reads || writes)))) { st = rrdset_find_bytype("disk_await", disk); - if(!st) { + if(unlikely(!st)) { st = rrdset_create("disk_await", disk, NULL, family, "disk.await", "Average Completed I/O Operation Time", "ms per operation", 2005, update_every, RRDSET_TYPE_LINE); st->isdetail = 1; rrddim_add(st, "reads", NULL, 1, 1, RRDDIM_ABSOLUTE); rrddim_add(st, "writes", NULL, -1, 1, RRDDIM_ABSOLUTE); } - else rrdset_next_usec(st, dt); + else rrdset_next(st); rrddim_set(st, "reads", (reads - last_reads) ? (readms - last_readms) / (reads - last_reads) : 0); rrddim_set(st, "writes", (writes - last_writes) ? (writems - last_writems) / (writes - last_writes) : 0); @@ -596,14 +572,14 @@ int do_proc_diskstats(int update_every, unsigned long long dt) { if( (d->do_io == CONFIG_ONDEMAND_YES || (d->do_io == CONFIG_ONDEMAND_ONDEMAND && (readsectors || writesectors))) && (d->do_ops == CONFIG_ONDEMAND_YES || (d->do_ops == CONFIG_ONDEMAND_ONDEMAND && (reads || writes)))) { st = rrdset_find_bytype("disk_avgsz", disk); - if(!st) { + if(unlikely(!st)) { st = rrdset_create("disk_avgsz", disk, NULL, family, "disk.avgsz", "Average Completed I/O Operation Bandwidth", "kilobytes per operation", 2006, update_every, RRDSET_TYPE_AREA); st->isdetail = 1; rrddim_add(st, "reads", NULL, d->sector_size, 1024, RRDDIM_ABSOLUTE); rrddim_add(st, "writes", NULL, d->sector_size * -1, 1024, RRDDIM_ABSOLUTE); } - else rrdset_next_usec(st, dt); + else rrdset_next(st); rrddim_set(st, "reads", (reads - last_reads) ? (readsectors - last_readsectors) / (reads - last_reads) : 0); rrddim_set(st, "writes", (writes - last_writes) ? (writesectors - last_writesectors) / (writes - last_writes) : 0); @@ -613,87 +589,18 @@ int do_proc_diskstats(int update_every, unsigned long long dt) { if( (d->do_util == CONFIG_ONDEMAND_YES || (d->do_util == CONFIG_ONDEMAND_ONDEMAND && busy_ms)) && (d->do_ops == CONFIG_ONDEMAND_YES || (d->do_ops == CONFIG_ONDEMAND_ONDEMAND && (reads || writes)))) { st = rrdset_find_bytype("disk_svctm", disk); - if(!st) { + if(unlikely(!st)) { st = rrdset_create("disk_svctm", disk, NULL, family, "disk.svctm", "Average Service Time", "ms per operation", 2007, update_every, RRDSET_TYPE_LINE); st->isdetail = 1; rrddim_add(st, "svctm", NULL, 1, 1, RRDDIM_ABSOLUTE); } - else rrdset_next_usec(st, dt); + else rrdset_next(st); rrddim_set(st, "svctm", ((reads - last_reads) + (writes - last_writes)) ? (busy_ms - last_busy_ms) / ((reads - last_reads) + (writes - last_writes)) : 0); rrdset_done(st); } } - - // -------------------------------------------------------------------------- - // space metrics - - if(d->mount_point && (d->do_space || d->do_inodes) ) { - // collect space metrics using statvfs - - if (statvfs(d->mount_point, &buff_statvfs) < 0) - error("Failed statvfs() for '%s' (disk '%s')", d->mount_point, d->disk); - else { - space_avail = buff_statvfs.f_bavail * buff_statvfs.f_bsize; - space_avail_root = (buff_statvfs.f_bfree - buff_statvfs.f_bavail) * buff_statvfs.f_bsize; - space_used = (buff_statvfs.f_blocks - buff_statvfs.f_bfree) * buff_statvfs.f_bsize; - - inodes_avail = buff_statvfs.f_favail; - inodes_avail_root = buff_statvfs.f_ffree - buff_statvfs.f_favail; - inodes_used = buff_statvfs.f_files - buff_statvfs.f_ffree; - - // verify we collected the metrics for the right disk. - // if not the mountpoint has changed. - - if(stat(d->mount_point, &buff_stat) == -1) - error("Failed to stat() for '%s' (disk '%s')", d->mount_point, d->disk); - else { - if(major(buff_stat.st_dev) == major && minor(buff_stat.st_dev) == minor) { - - // -------------------------------------------------------------------------- - - if(d->do_space == CONFIG_ONDEMAND_YES || (d->do_space == CONFIG_ONDEMAND_ONDEMAND && (space_avail || space_avail_root || space_used))) { - st = rrdset_find_bytype("disk_space", disk); - if(!st) { - st = rrdset_create("disk_space", disk, NULL, family, "disk.space", "Disk Space Usage", "GB", 2023, update_every, RRDSET_TYPE_STACKED); - st->isdetail = 1; - - rrddim_add(st, "avail", NULL, 1, 1024*1024*1024, RRDDIM_ABSOLUTE); - rrddim_add(st, "used" , NULL, 1, 1024*1024*1024, RRDDIM_ABSOLUTE); - rrddim_add(st, "reserved_for_root", "reserved for root", 1, 1024*1024*1024, RRDDIM_ABSOLUTE); - } - else rrdset_next_usec(st, dt); - - rrddim_set(st, "avail", space_avail); - rrddim_set(st, "used", space_used); - rrddim_set(st, "reserved_for_root", space_avail_root); - rrdset_done(st); - } - - // -------------------------------------------------------------------------- - - if(d->do_inodes == CONFIG_ONDEMAND_YES || (d->do_inodes == CONFIG_ONDEMAND_ONDEMAND && (inodes_avail || inodes_avail_root || inodes_used))) { - st = rrdset_find_bytype("disk_inodes", disk); - if(!st) { - st = rrdset_create("disk_inodes", disk, NULL, family, "disk.inodes", "Disk Inodes Usage", "Inodes", 2024, update_every, RRDSET_TYPE_STACKED); - st->isdetail = 1; - - rrddim_add(st, "avail", NULL, 1, 1, RRDDIM_ABSOLUTE); - rrddim_add(st, "used" , NULL, 1, 1, RRDDIM_ABSOLUTE); - rrddim_add(st, "reserved_for_root", "reserved for root", 1, 1, RRDDIM_ABSOLUTE); - } - else rrdset_next_usec(st, dt); - - rrddim_set(st, "avail", inodes_avail); - rrddim_set(st, "used", inodes_used); - rrddim_set(st, "reserved_for_root", inodes_avail_root); - rrdset_done(st); - } - } - } - } - } } return 0; diff --git a/src/proc_interrupts.c b/src/proc_interrupts.c index f277a5a90..f663c0fdd 100644 --- a/src/proc_interrupts.c +++ b/src/proc_interrupts.c @@ -2,70 +2,92 @@ #define MAX_INTERRUPT_NAME 50 +struct cpu_interrupt { + unsigned long long value; + RRDDIM *rd; +}; + struct interrupt { int used; char *id; char name[MAX_INTERRUPT_NAME + 1]; + RRDDIM *rd; unsigned long long total; - unsigned long long value[]; + struct cpu_interrupt cpu[]; }; // since each interrupt is variable in size // we use this to calculate its record size -#define recordsize(cpus) (sizeof(struct interrupt) + (cpus * sizeof(unsigned long long))) +#define recordsize(cpus) (sizeof(struct interrupt) + (cpus * sizeof(struct cpu_interrupt))) // given a base, get a pointer to each record #define irrindex(base, line, cpus) ((struct interrupt *)&((char *)(base))[line * recordsize(cpus)]) -static inline struct interrupt *get_interrupts_array(int lines, int cpus) { +static inline struct interrupt *get_interrupts_array(size_t lines, int cpus) { static struct interrupt *irrs = NULL; - static int allocated = 0; + static size_t allocated = 0; + + if(unlikely(lines != allocated)) { + size_t l; + int c; - if(lines > allocated) { irrs = (struct interrupt *)reallocz(irrs, lines * recordsize(cpus)); + + // reset all interrupt RRDDIM pointers as any line could have shifted + for(l = 0; l < lines ;l++) { + struct interrupt *irr = irrindex(irrs, l, cpus); + irr->rd = NULL; + irr->name[0] = '\0'; + for(c = 0; c < cpus ;c++) + irr->cpu[c].rd = NULL; + } + allocated = lines; } return irrs; } -int do_proc_interrupts(int update_every, unsigned long long dt) { +int do_proc_interrupts(int update_every, usec_t dt) { + (void)dt; static procfile *ff = NULL; static int cpus = -1, do_per_core = -1; struct interrupt *irrs = NULL; - if(dt) {}; - - if(do_per_core == -1) do_per_core = config_get_boolean("plugin:proc:/proc/interrupts", "interrupts per core", 1); + if(unlikely(do_per_core == -1)) + do_per_core = config_get_boolean("plugin:proc:/proc/interrupts", "interrupts per core", 1); - if(!ff) { + if(unlikely(!ff)) { char filename[FILENAME_MAX + 1]; snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/interrupts"); ff = procfile_open(config_get("plugin:proc:/proc/interrupts", "filename to monitor", filename), " \t", PROCFILE_FLAG_DEFAULT); } - if(!ff) return 1; + if(unlikely(!ff)) + return 1; ff = procfile_readall(ff); - if(!ff) return 0; // we return 0, so that we will retry to open it next time + if(unlikely(!ff)) + return 0; // we return 0, so that we will retry to open it next time - uint32_t lines = procfile_lines(ff), l; - uint32_t words = procfile_linewords(ff, 0), w; + size_t lines = procfile_lines(ff), l; + size_t words = procfile_linewords(ff, 0); - if(!lines) { + if(unlikely(!lines)) { error("Cannot read /proc/interrupts, zero lines reported."); return 1; } // find how many CPUs are there - if(cpus == -1) { + if(unlikely(cpus == -1)) { + uint32_t w; cpus = 0; for(w = 0; w < words ; w++) { - if(strncmp(procfile_lineword(ff, 0, w), "CPU", 3) == 0) + if(likely(strncmp(procfile_lineword(ff, 0, w), "CPU", 3) == 0)) cpus++; } } - if(!cpus) { + if(unlikely(!cpus)) { error("PLUGIN: PROC_INTERRUPTS: Cannot find the number of CPUs in /proc/interrupts"); return 1; } @@ -81,31 +103,36 @@ int do_proc_interrupts(int update_every, unsigned long long dt) { irr->total = 0; words = procfile_linewords(ff, l); - if(!words) continue; + if(unlikely(!words)) continue; irr->id = procfile_lineword(ff, l, 0); - if(!irr->id || !irr->id[0]) continue; + if(unlikely(!irr->id || !irr->id[0])) continue; - int idlen = strlen(irr->id); - if(irr->id[idlen - 1] == ':') + size_t idlen = strlen(irr->id); + if(unlikely(idlen && irr->id[idlen - 1] == ':')) irr->id[idlen - 1] = '\0'; int c; for(c = 0; c < cpus ;c++) { - if((c + 1) < (int)words) - irr->value[c] = strtoull(procfile_lineword(ff, l, (uint32_t)(c + 1)), NULL, 10); + if(likely((c + 1) < (int)words)) + irr->cpu[c].value = str2ull(procfile_lineword(ff, l, (uint32_t)(c + 1))); else - irr->value[c] = 0; + irr->cpu[c].value = 0; - irr->total += irr->value[c]; + irr->total += irr->cpu[c].value; } - if(isdigit(irr->id[0]) && (uint32_t)(cpus + 2) < words) { + if(unlikely(isdigit(irr->id[0]) && (uint32_t)(cpus + 2) < words)) { strncpyz(irr->name, procfile_lineword(ff, l, words - 1), MAX_INTERRUPT_NAME); - int nlen = strlen(irr->name); - if(nlen < (MAX_INTERRUPT_NAME-1)) { + size_t nlen = strlen(irr->name); + idlen = strlen(irr->id); + if(likely(nlen + 1 + idlen <= MAX_INTERRUPT_NAME)) { irr->name[nlen] = '_'; - strncpyz(&irr->name[nlen + 1], irr->id, MAX_INTERRUPT_NAME - nlen); + strncpyz(&irr->name[nlen + 1], irr->id, MAX_INTERRUPT_NAME - nlen - 1); + } + else { + irr->name[MAX_INTERRUPT_NAME - idlen - 1] = '_'; + strncpyz(&irr->name[MAX_INTERRUPT_NAME - idlen], irr->id, idlen); } } else { @@ -120,49 +147,59 @@ int do_proc_interrupts(int update_every, unsigned long long dt) { // -------------------------------------------------------------------- st = rrdset_find_bytype("system", "interrupts"); - if(!st) { - st = rrdset_create("system", "interrupts", NULL, "interrupts", NULL, "System interrupts", "interrupts/s", 1000, update_every, RRDSET_TYPE_STACKED); - - for(l = 0; l < lines ;l++) { - struct interrupt *irr = irrindex(irrs, l, cpus); - if(!irr->used) continue; - rrddim_add(st, irr->id, irr->name, 1, 1, RRDDIM_INCREMENTAL); - } - } + if(unlikely(!st)) st = rrdset_create("system", "interrupts", NULL, "interrupts", NULL, "System interrupts", "interrupts/s", 1000, update_every, RRDSET_TYPE_STACKED); else rrdset_next(st); for(l = 0; l < lines ;l++) { struct interrupt *irr = irrindex(irrs, l, cpus); - if(!irr->used) continue; - rrddim_set(st, irr->id, irr->total); + if(unlikely(!irr->used)) continue; + // some interrupt may have changed without changing the total number of lines + // if the same number of interrupts have been added and removed between two + // calls of this function. + if(unlikely(!irr->rd || strncmp(irr->rd->name, irr->name, MAX_INTERRUPT_NAME) != 0)) { + irr->rd = rrddim_find(st, irr->id); + if(unlikely(!irr->rd)) + irr->rd = rrddim_add(st, irr->id, irr->name, 1, 1, RRDDIM_INCREMENTAL); + else + rrddim_set_name(st, irr->rd, irr->name); + + // also reset per cpu RRDDIMs to avoid repeating strncmp() in the per core loop + if(likely(do_per_core)) { + int c; + for (c = 0; c < cpus ;c++) + irr->cpu[c].rd = NULL; + } + } + rrddim_set_by_pointer(st, irr->rd, irr->total); } rrdset_done(st); - if(do_per_core) { + if(likely(do_per_core)) { int c; - for(c = 0; c < cpus ; c++) { + for(c = 0; c < cpus ;c++) { char id[50+1]; snprintfz(id, 50, "cpu%d_interrupts", c); st = rrdset_find_bytype("cpu", id); - if(!st) { + if(unlikely(!st)) { char title[100+1]; snprintfz(title, 100, "CPU%d Interrupts", c); st = rrdset_create("cpu", id, NULL, "interrupts", "cpu.interrupts", title, "interrupts/s", 1100 + c, update_every, RRDSET_TYPE_STACKED); - - for(l = 0; l < lines ;l++) { - struct interrupt *irr = irrindex(irrs, l, cpus); - if(!irr->used) continue; - rrddim_add(st, irr->id, irr->name, 1, 1, RRDDIM_INCREMENTAL); - } } else rrdset_next(st); for(l = 0; l < lines ;l++) { struct interrupt *irr = irrindex(irrs, l, cpus); - if(!irr->used) continue; - rrddim_set(st, irr->id, irr->value[c]); + if(unlikely(!irr->used)) continue; + if(unlikely(!irr->cpu[c].rd)) { + irr->cpu[c].rd = rrddim_find(st, irr->id); + if(unlikely(!irr->cpu[c].rd)) + irr->cpu[c].rd = rrddim_add(st, irr->id, irr->name, 1, 1, RRDDIM_INCREMENTAL); + else + rrddim_set_name(st, irr->cpu[c].rd, irr->name); + } + rrddim_set_by_pointer(st, irr->cpu[c].rd, irr->cpu[c].value); } rrdset_done(st); } diff --git a/src/proc_loadavg.c b/src/proc_loadavg.c index 44ea70191..4326ffb7d 100644 --- a/src/proc_loadavg.c +++ b/src/proc_loadavg.c @@ -3,30 +3,35 @@ // linux calculates this once every 5 seconds #define MIN_LOADAVG_UPDATE_EVERY 5 -int do_proc_loadavg(int update_every, unsigned long long dt) { +int do_proc_loadavg(int update_every, usec_t dt) { static procfile *ff = NULL; static int do_loadavg = -1, do_all_processes = -1; + static usec_t last_loadavg_usec = 0; + static RRDSET *load_chart = NULL, *processes_chart = NULL; - if(dt) {}; - - if(!ff) { + if(unlikely(!ff)) { char filename[FILENAME_MAX + 1]; snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/loadavg"); + ff = procfile_open(config_get("plugin:proc:/proc/loadavg", "filename to monitor", filename), " \t,:|/", PROCFILE_FLAG_DEFAULT); + if(unlikely(!ff)) + return 1; } - if(!ff) return 1; ff = procfile_readall(ff); - if(!ff) return 0; // we return 0, so that we will retry to open it next time + if(unlikely(!ff)) + return 0; // we return 0, so that we will retry to open it next time - if(do_loadavg == -1) do_loadavg = config_get_boolean("plugin:proc:/proc/loadavg", "enable load average", 1); - if(do_all_processes == -1) do_all_processes = config_get_boolean("plugin:proc:/proc/loadavg", "enable total processes", 1); + if(unlikely(do_loadavg == -1)) { + do_loadavg = config_get_boolean("plugin:proc:/proc/loadavg", "enable load average", 1); + do_all_processes = config_get_boolean("plugin:proc:/proc/loadavg", "enable total processes", 1); + } - if(procfile_lines(ff) < 1) { + if(unlikely(procfile_lines(ff) < 1)) { error("/proc/loadavg has no lines."); return 1; } - if(procfile_linewords(ff, 0) < 6) { + if(unlikely(procfile_linewords(ff, 0) < 6)) { error("/proc/loadavg has less than 6 words in it."); return 1; } @@ -35,45 +40,51 @@ int do_proc_loadavg(int update_every, unsigned long long dt) { double load5 = strtod(procfile_lineword(ff, 0, 1), NULL); double load15 = strtod(procfile_lineword(ff, 0, 2), NULL); - //unsigned long long running_processes = strtoull(procfile_lineword(ff, 0, 3), NULL, 10); - unsigned long long active_processes = strtoull(procfile_lineword(ff, 0, 4), NULL, 10); - //unsigned long long next_pid = strtoull(procfile_lineword(ff, 0, 5), NULL, 10); + //unsigned long long running_processes = str2ull(procfile_lineword(ff, 0, 3)); + unsigned long long active_processes = str2ull(procfile_lineword(ff, 0, 4)); + //unsigned long long next_pid = str2ull(procfile_lineword(ff, 0, 5)); - RRDSET *st; - // -------------------------------------------------------------------- - if(do_loadavg) { - st = rrdset_find_byname("system.load"); - if(!st) { - st = rrdset_create("system", "load", NULL, "load", NULL, "System Load Average", "load", 100, (update_every < MIN_LOADAVG_UPDATE_EVERY)?MIN_LOADAVG_UPDATE_EVERY:update_every, RRDSET_TYPE_LINE); - - rrddim_add(st, "load1", NULL, 1, 1000, RRDDIM_ABSOLUTE); - rrddim_add(st, "load5", NULL, 1, 1000, RRDDIM_ABSOLUTE); - rrddim_add(st, "load15", NULL, 1, 1000, RRDDIM_ABSOLUTE); + if(last_loadavg_usec <= dt) { + if(likely(do_loadavg)) { + if(unlikely(!load_chart)) { + load_chart = rrdset_find_byname("system.load"); + if(unlikely(!load_chart)) { + load_chart = rrdset_create("system", "load", NULL, "load", NULL, "System Load Average", "load", 100, (update_every < MIN_LOADAVG_UPDATE_EVERY) ? MIN_LOADAVG_UPDATE_EVERY : update_every, RRDSET_TYPE_LINE); + rrddim_add(load_chart, "load1", NULL, 1, 1000, RRDDIM_ABSOLUTE); + rrddim_add(load_chart, "load5", NULL, 1, 1000, RRDDIM_ABSOLUTE); + rrddim_add(load_chart, "load15", NULL, 1, 1000, RRDDIM_ABSOLUTE); + } + } + else + rrdset_next(load_chart); + + rrddim_set(load_chart, "load1", (collected_number) (load1 * 1000)); + rrddim_set(load_chart, "load5", (collected_number) (load5 * 1000)); + rrddim_set(load_chart, "load15", (collected_number) (load15 * 1000)); + rrdset_done(load_chart); } - else rrdset_next(st); - rrddim_set(st, "load1", load1 * 1000); - rrddim_set(st, "load5", load5 * 1000); - rrddim_set(st, "load15", load15 * 1000); - rrdset_done(st); + last_loadavg_usec = load_chart->update_every * USEC_PER_SEC; } + else last_loadavg_usec -= dt; // -------------------------------------------------------------------- - if(do_all_processes) { - st = rrdset_find_byname("system.active_processes"); - if(!st) { - st = rrdset_create("system", "active_processes", NULL, "processes", NULL, "System Active Processes", "processes", 750, update_every, RRDSET_TYPE_LINE); - - rrddim_add(st, "active", NULL, 1, 1, RRDDIM_ABSOLUTE); + if(likely(do_all_processes)) { + if(unlikely(!processes_chart)) { + processes_chart = rrdset_find_byname("system.active_processes"); + if(unlikely(!processes_chart)) { + processes_chart = rrdset_create("system", "active_processes", NULL, "processes", NULL, "System Active Processes", "processes", 750, update_every, RRDSET_TYPE_LINE); + rrddim_add(processes_chart, "active", NULL, 1, 1, RRDDIM_ABSOLUTE); + } } - else rrdset_next(st); + else rrdset_next(processes_chart); - rrddim_set(st, "active", active_processes); - rrdset_done(st); + rrddim_set(processes_chart, "active", active_processes); + rrdset_done(processes_chart); } return 0; diff --git a/src/proc_meminfo.c b/src/proc_meminfo.c index 999c9538d..19ba8da3c 100644 --- a/src/proc_meminfo.c +++ b/src/proc_meminfo.c @@ -1,96 +1,135 @@ #include "common.h" -#define MAX_PROC_MEMINFO_LINE 4096 -#define MAX_PROC_MEMINFO_NAME 1024 +int do_proc_meminfo(int update_every, usec_t dt) { + (void)dt; -int do_proc_meminfo(int update_every, unsigned long long dt) { static procfile *ff = NULL; - static int do_ram = -1, do_swap = -1, do_hwcorrupt = -1, do_committed = -1, do_writeback = -1, do_kernel = -1, do_slab = -1; - if(do_ram == -1) do_ram = config_get_boolean("plugin:proc:/proc/meminfo", "system ram", 1); - if(do_swap == -1) do_swap = config_get_boolean_ondemand("plugin:proc:/proc/meminfo", "system swap", CONFIG_ONDEMAND_ONDEMAND); - if(do_hwcorrupt == -1) do_hwcorrupt = config_get_boolean_ondemand("plugin:proc:/proc/meminfo", "hardware corrupted ECC", CONFIG_ONDEMAND_ONDEMAND); - if(do_committed == -1) do_committed = config_get_boolean("plugin:proc:/proc/meminfo", "committed memory", 1); - if(do_writeback == -1) do_writeback = config_get_boolean("plugin:proc:/proc/meminfo", "writeback memory", 1); - if(do_kernel == -1) do_kernel = config_get_boolean("plugin:proc:/proc/meminfo", "kernel memory", 1); - if(do_slab == -1) do_slab = config_get_boolean("plugin:proc:/proc/meminfo", "slab memory", 1); - - (void)dt; + static ARL_BASE *arl_base = NULL; + static ARL_ENTRY *arl_hwcorrupted = NULL; + + static unsigned long long + MemTotal = 0, + MemFree = 0, + Buffers = 0, + Cached = 0, + //SwapCached = 0, + //Active = 0, + //Inactive = 0, + //ActiveAnon = 0, + //InactiveAnon = 0, + //ActiveFile = 0, + //InactiveFile = 0, + //Unevictable = 0, + //Mlocked = 0, + SwapTotal = 0, + SwapFree = 0, + Dirty = 0, + Writeback = 0, + //AnonPages = 0, + //Mapped = 0, + //Shmem = 0, + Slab = 0, + SReclaimable = 0, + SUnreclaim = 0, + KernelStack = 0, + PageTables = 0, + NFS_Unstable = 0, + Bounce = 0, + WritebackTmp = 0, + //CommitLimit = 0, + Committed_AS = 0, + //VmallocTotal = 0, + VmallocUsed = 0, + //VmallocChunk = 0, + //AnonHugePages = 0, + //HugePages_Total = 0, + //HugePages_Free = 0, + //HugePages_Rsvd = 0, + //HugePages_Surp = 0, + //Hugepagesize = 0, + //DirectMap4k = 0, + //DirectMap2M = 0, + HardwareCorrupted = 0; + + if(unlikely(!arl_base)) { + do_ram = config_get_boolean("plugin:proc:/proc/meminfo", "system ram", 1); + do_swap = config_get_boolean_ondemand("plugin:proc:/proc/meminfo", "system swap", CONFIG_ONDEMAND_ONDEMAND); + do_hwcorrupt = config_get_boolean_ondemand("plugin:proc:/proc/meminfo", "hardware corrupted ECC", CONFIG_ONDEMAND_ONDEMAND); + do_committed = config_get_boolean("plugin:proc:/proc/meminfo", "committed memory", 1); + do_writeback = config_get_boolean("plugin:proc:/proc/meminfo", "writeback memory", 1); + do_kernel = config_get_boolean("plugin:proc:/proc/meminfo", "kernel memory", 1); + do_slab = config_get_boolean("plugin:proc:/proc/meminfo", "slab memory", 1); + + arl_base = arl_create("meminfo", NULL, 60); + arl_expect(arl_base, "MemTotal", &MemTotal); + arl_expect(arl_base, "MemFree", &MemFree); + arl_expect(arl_base, "Buffers", &Buffers); + arl_expect(arl_base, "Cached", &Cached); + //arl_expect(arl_base, "SwapCached", &SwapCached); + //arl_expect(arl_base, "Active", &Active); + //arl_expect(arl_base, "Inactive", &Inactive); + //arl_expect(arl_base, "ActiveAnon", &ActiveAnon); + //arl_expect(arl_base, "InactiveAnon", &InactiveAnon); + //arl_expect(arl_base, "ActiveFile", &ActiveFile); + //arl_expect(arl_base, "InactiveFile", &InactiveFile); + //arl_expect(arl_base, "Unevictable", &Unevictable); + //arl_expect(arl_base, "Mlocked", &Mlocked); + arl_expect(arl_base, "SwapTotal", &SwapTotal); + arl_expect(arl_base, "SwapFree", &SwapFree); + arl_expect(arl_base, "Dirty", &Dirty); + arl_expect(arl_base, "Writeback", &Writeback); + //arl_expect(arl_base, "AnonPages", &AnonPages); + //arl_expect(arl_base, "Mapped", &Mapped); + //arl_expect(arl_base, "Shmem", &Shmem); + arl_expect(arl_base, "Slab", &Slab); + arl_expect(arl_base, "SReclaimable", &SReclaimable); + arl_expect(arl_base, "SUnreclaim", &SUnreclaim); + arl_expect(arl_base, "KernelStack", &KernelStack); + arl_expect(arl_base, "PageTables", &PageTables); + arl_expect(arl_base, "NFS_Unstable", &NFS_Unstable); + arl_expect(arl_base, "Bounce", &Bounce); + arl_expect(arl_base, "WritebackTmp", &WritebackTmp); + //arl_expect(arl_base, "CommitLimit", &CommitLimit); + arl_expect(arl_base, "Committed_AS", &Committed_AS); + //arl_expect(arl_base, "VmallocTotal", &VmallocTotal); + arl_expect(arl_base, "VmallocUsed", &VmallocUsed); + //arl_expect(arl_base, "VmallocChunk", &VmallocChunk); + arl_hwcorrupted = arl_expect(arl_base, "HardwareCorrupted", &HardwareCorrupted); + //arl_expect(arl_base, "AnonHugePages", &AnonHugePages); + //arl_expect(arl_base, "HugePages_Total", &HugePages_Total); + //arl_expect(arl_base, "HugePages_Free", &HugePages_Free); + //arl_expect(arl_base, "HugePages_Rsvd", &HugePages_Rsvd); + //arl_expect(arl_base, "HugePages_Surp", &HugePages_Surp); + //arl_expect(arl_base, "Hugepagesize", &Hugepagesize); + //arl_expect(arl_base, "DirectMap4k", &DirectMap4k); + //arl_expect(arl_base, "DirectMap2M", &DirectMap2M); + } - if(!ff) { + if(unlikely(!ff)) { char filename[FILENAME_MAX + 1]; snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/meminfo"); ff = procfile_open(config_get("plugin:proc:/proc/meminfo", "filename to monitor", filename), " \t:", PROCFILE_FLAG_DEFAULT); + if(unlikely(!ff)) + return 1; } - if(!ff) return 1; ff = procfile_readall(ff); - if(!ff) return 0; // we return 0, so that we will retry to open it next time - - uint32_t lines = procfile_lines(ff), l; - uint32_t words; + if(unlikely(!ff)) + return 0; // we return 0, so that we will retry to open it next time - int hwcorrupted = 0; + size_t lines = procfile_lines(ff), l; - unsigned long long MemTotal = 0, MemFree = 0, Buffers = 0, Cached = 0, SwapCached = 0, - Active = 0, Inactive = 0, ActiveAnon = 0, InactiveAnon = 0, ActiveFile = 0, InactiveFile = 0, - Unevictable = 0, Mlocked = 0, SwapTotal = 0, SwapFree = 0, Dirty = 0, Writeback = 0, AnonPages = 0, - Mapped = 0, Shmem = 0, Slab = 0, SReclaimable = 0, SUnreclaim = 0, KernelStack = 0, PageTables = 0, - NFS_Unstable = 0, Bounce = 0, WritebackTmp = 0, CommitLimit = 0, Committed_AS = 0, - VmallocTotal = 0, VmallocUsed = 0, VmallocChunk = 0, - AnonHugePages = 0, HugePages_Total = 0, HugePages_Free = 0, HugePages_Rsvd = 0, HugePages_Surp = 0, Hugepagesize = 0, - DirectMap4k = 0, DirectMap2M = 0, HardwareCorrupted = 0; + arl_begin(arl_base); for(l = 0; l < lines ;l++) { - words = procfile_linewords(ff, l); - if(words < 2) continue; - - char *name = procfile_lineword(ff, l, 0); - unsigned long long value = strtoull(procfile_lineword(ff, l, 1), NULL, 10); - - if(!MemTotal && strcmp(name, "MemTotal") == 0) MemTotal = value; - else if(!MemFree && strcmp(name, "MemFree") == 0) MemFree = value; - else if(!Buffers && strcmp(name, "Buffers") == 0) Buffers = value; - else if(!Cached && strcmp(name, "Cached") == 0) Cached = value; - else if(!SwapCached && strcmp(name, "SwapCached") == 0) SwapCached = value; - else if(!Active && strcmp(name, "Active") == 0) Active = value; - else if(!Inactive && strcmp(name, "Inactive") == 0) Inactive = value; - else if(!ActiveAnon && strcmp(name, "ActiveAnon") == 0) ActiveAnon = value; - else if(!InactiveAnon && strcmp(name, "InactiveAnon") == 0) InactiveAnon = value; - else if(!ActiveFile && strcmp(name, "ActiveFile") == 0) ActiveFile = value; - else if(!InactiveFile && strcmp(name, "InactiveFile") == 0) InactiveFile = value; - else if(!Unevictable && strcmp(name, "Unevictable") == 0) Unevictable = value; - else if(!Mlocked && strcmp(name, "Mlocked") == 0) Mlocked = value; - else if(!SwapTotal && strcmp(name, "SwapTotal") == 0) SwapTotal = value; - else if(!SwapFree && strcmp(name, "SwapFree") == 0) SwapFree = value; - else if(!Dirty && strcmp(name, "Dirty") == 0) Dirty = value; - else if(!Writeback && strcmp(name, "Writeback") == 0) Writeback = value; - else if(!AnonPages && strcmp(name, "AnonPages") == 0) AnonPages = value; - else if(!Mapped && strcmp(name, "Mapped") == 0) Mapped = value; - else if(!Shmem && strcmp(name, "Shmem") == 0) Shmem = value; - else if(!Slab && strcmp(name, "Slab") == 0) Slab = value; - else if(!SReclaimable && strcmp(name, "SReclaimable") == 0) SReclaimable = value; - else if(!SUnreclaim && strcmp(name, "SUnreclaim") == 0) SUnreclaim = value; - else if(!KernelStack && strcmp(name, "KernelStack") == 0) KernelStack = value; - else if(!PageTables && strcmp(name, "PageTables") == 0) PageTables = value; - else if(!NFS_Unstable && strcmp(name, "NFS_Unstable") == 0) NFS_Unstable = value; - else if(!Bounce && strcmp(name, "Bounce") == 0) Bounce = value; - else if(!WritebackTmp && strcmp(name, "WritebackTmp") == 0) WritebackTmp = value; - else if(!CommitLimit && strcmp(name, "CommitLimit") == 0) CommitLimit = value; - else if(!Committed_AS && strcmp(name, "Committed_AS") == 0) Committed_AS = value; - else if(!VmallocTotal && strcmp(name, "VmallocTotal") == 0) VmallocTotal = value; - else if(!VmallocUsed && strcmp(name, "VmallocUsed") == 0) VmallocUsed = value; - else if(!VmallocChunk && strcmp(name, "VmallocChunk") == 0) VmallocChunk = value; - else if(!HardwareCorrupted && strcmp(name, "HardwareCorrupted") == 0) { HardwareCorrupted = value; hwcorrupted = 1; } - else if(!AnonHugePages && strcmp(name, "AnonHugePages") == 0) AnonHugePages = value; - else if(!HugePages_Total && strcmp(name, "HugePages_Total") == 0) HugePages_Total = value; - else if(!HugePages_Free && strcmp(name, "HugePages_Free") == 0) HugePages_Free = value; - else if(!HugePages_Rsvd && strcmp(name, "HugePages_Rsvd") == 0) HugePages_Rsvd = value; - else if(!HugePages_Surp && strcmp(name, "HugePages_Surp") == 0) HugePages_Surp = value; - else if(!Hugepagesize && strcmp(name, "Hugepagesize") == 0) Hugepagesize = value; - else if(!DirectMap4k && strcmp(name, "DirectMap4k") == 0) DirectMap4k = value; - else if(!DirectMap2M && strcmp(name, "DirectMap2M") == 0) DirectMap2M = value; + size_t words = procfile_linewords(ff, l); + if(unlikely(words < 2)) continue; + + if(unlikely(arl_check(arl_base, + procfile_lineword(ff, l, 0), + procfile_lineword(ff, l, 1)))) break; } RRDSET *st; @@ -143,12 +182,12 @@ int do_proc_meminfo(int update_every, unsigned long long dt) { // -------------------------------------------------------------------- - if(hwcorrupted && do_hwcorrupt && HardwareCorrupted > 0) { + if(arl_hwcorrupted->flags & ARL_ENTRY_FLAG_FOUND && (do_hwcorrupt == CONFIG_ONDEMAND_YES || (do_hwcorrupt == CONFIG_ONDEMAND_ONDEMAND && HardwareCorrupted > 0))) { do_hwcorrupt = CONFIG_ONDEMAND_YES; st = rrdset_find("mem.hwcorrupt"); if(!st) { - st = rrdset_create("mem", "hwcorrupt", NULL, "errors", NULL, "Hardware Corrupted ECC", "MB", 9000, update_every, RRDSET_TYPE_LINE); + st = rrdset_create("mem", "hwcorrupt", NULL, "ecc", NULL, "Hardware Corrupted ECC", "MB", 9000, update_every, RRDSET_TYPE_LINE); st->isdetail = 1; rrddim_add(st, "HardwareCorrupted", NULL, 1, 1024, RRDDIM_ABSOLUTE); diff --git a/src/proc_net_dev.c b/src/proc_net_dev.c index 53981182a..82661abd4 100644 --- a/src/proc_net_dev.c +++ b/src/proc_net_dev.c @@ -1,234 +1,369 @@ #include "common.h" -int do_proc_net_dev(int update_every, unsigned long long dt) { +struct netdev { + char *name; + uint32_t hash; + size_t len; + + // flags + int configured; + int enabled; + + int do_bandwidth; + int do_packets; + int do_errors; + int do_drops; + int do_fifo; + int do_compressed; + int do_events; + + // data collected + unsigned long long rbytes; + unsigned long long rpackets; + unsigned long long rerrors; + unsigned long long rdrops; + unsigned long long rfifo; + unsigned long long rframe; + unsigned long long rcompressed; + unsigned long long rmulticast; + + unsigned long long tbytes; + unsigned long long tpackets; + unsigned long long terrors; + unsigned long long tdrops; + unsigned long long tfifo; + unsigned long long tcollisions; + unsigned long long tcarrier; + unsigned long long tcompressed; + + // charts + RRDSET *st_bandwidth; + RRDSET *st_packets; + RRDSET *st_errors; + RRDSET *st_drops; + RRDSET *st_fifo; + RRDSET *st_compressed; + RRDSET *st_events; + + // dimensions + RRDDIM *rd_rbytes; + RRDDIM *rd_rpackets; + RRDDIM *rd_rerrors; + RRDDIM *rd_rdrops; + RRDDIM *rd_rfifo; + RRDDIM *rd_rframe; + RRDDIM *rd_rcompressed; + RRDDIM *rd_rmulticast; + + RRDDIM *rd_tbytes; + RRDDIM *rd_tpackets; + RRDDIM *rd_terrors; + RRDDIM *rd_tdrops; + RRDDIM *rd_tfifo; + RRDDIM *rd_tcollisions; + RRDDIM *rd_tcarrier; + RRDDIM *rd_tcompressed; + + struct netdev *next; +}; + +static struct netdev *netdev_root = NULL; + +static struct netdev *get_netdev(const char *name) { + static struct netdev *last = NULL; + struct netdev *d; + + uint32_t hash = simple_hash(name); + + // search it, from the last position to the end + for(d = last ; d ; d = d->next) { + if(unlikely(hash == d->hash && !strcmp(name, d->name))) { + last = d->next; + return d; + } + } + + // search it from the beginning to the last position we used + for(d = netdev_root ; d != last ; d = d->next) { + if(unlikely(hash == d->hash && !strcmp(name, d->name))) { + last = d->next; + return d; + } + } + + // create a new one + d = callocz(1, sizeof(struct netdev)); + d->name = strdupz(name); + d->hash = simple_hash(d->name); + d->len = strlen(d->name); + + // link it to the end + if(netdev_root) { + struct netdev *e; + for(e = netdev_root; e->next ; e = e->next) ; + e->next = d; + } + else + netdev_root = d; + + return d; +} + +int do_proc_net_dev(int update_every, usec_t dt) { + (void)dt; + + static SIMPLE_PATTERN *disabled_list = NULL; static procfile *ff = NULL; - static int enable_new_interfaces = -1, enable_ifb_interfaces = -1; + static int enable_new_interfaces = -1; static int do_bandwidth = -1, do_packets = -1, do_errors = -1, do_drops = -1, do_fifo = -1, do_compressed = -1, do_events = -1; - if(dt) {}; + if(unlikely(enable_new_interfaces == -1)) { + enable_new_interfaces = config_get_boolean_ondemand("plugin:proc:/proc/net/dev", "enable new interfaces detected at runtime", CONFIG_ONDEMAND_ONDEMAND); + + do_bandwidth = config_get_boolean_ondemand("plugin:proc:/proc/net/dev", "bandwidth for all interfaces", CONFIG_ONDEMAND_ONDEMAND); + do_packets = config_get_boolean_ondemand("plugin:proc:/proc/net/dev", "packets for all interfaces", CONFIG_ONDEMAND_ONDEMAND); + do_errors = config_get_boolean_ondemand("plugin:proc:/proc/net/dev", "errors for all interfaces", CONFIG_ONDEMAND_ONDEMAND); + do_drops = config_get_boolean_ondemand("plugin:proc:/proc/net/dev", "drops for all interfaces", CONFIG_ONDEMAND_ONDEMAND); + do_fifo = config_get_boolean_ondemand("plugin:proc:/proc/net/dev", "fifo for all interfaces", CONFIG_ONDEMAND_ONDEMAND); + do_compressed = config_get_boolean_ondemand("plugin:proc:/proc/net/dev", "compressed packets for all interfaces", CONFIG_ONDEMAND_ONDEMAND); + do_events = config_get_boolean_ondemand("plugin:proc:/proc/net/dev", "frames, collisions, carrier counters for all interfaces", CONFIG_ONDEMAND_ONDEMAND); + + disabled_list = simple_pattern_create( + config_get("plugin:proc:/proc/net/dev", "disable by default interfaces matching", "lo fireqos* *-ifb") + , SIMPLE_PATTERN_EXACT); + } - if(!ff) { + if(unlikely(!ff)) { char filename[FILENAME_MAX + 1]; snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/net/dev"); ff = procfile_open(config_get("plugin:proc:/proc/net/dev", "filename to monitor", filename), " \t,:|", PROCFILE_FLAG_DEFAULT); + if(unlikely(!ff)) return 1; } - if(!ff) return 1; ff = procfile_readall(ff); - if(!ff) return 0; // we return 0, so that we will retry to open it next time + if(unlikely(!ff)) return 0; // we return 0, so that we will retry to open it next time - if(enable_new_interfaces == -1) enable_new_interfaces = config_get_boolean_ondemand("plugin:proc:/proc/net/dev", "enable new interfaces detected at runtime", CONFIG_ONDEMAND_ONDEMAND); - if(enable_ifb_interfaces == -1) enable_ifb_interfaces = config_get_boolean_ondemand("plugin:proc:/proc/net/dev", "enable ifb interfaces", CONFIG_ONDEMAND_NO); + size_t lines = procfile_lines(ff), l; + for(l = 2; l < lines ;l++) { + // require 17 words on each line + if(unlikely(procfile_linewords(ff, l) < 17)) continue; - if(do_bandwidth == -1) do_bandwidth = config_get_boolean_ondemand("plugin:proc:/proc/net/dev", "bandwidth for all interfaces", CONFIG_ONDEMAND_ONDEMAND); - if(do_packets == -1) do_packets = config_get_boolean_ondemand("plugin:proc:/proc/net/dev", "packets for all interfaces", CONFIG_ONDEMAND_ONDEMAND); - if(do_errors == -1) do_errors = config_get_boolean_ondemand("plugin:proc:/proc/net/dev", "errors for all interfaces", CONFIG_ONDEMAND_ONDEMAND); - if(do_drops == -1) do_drops = config_get_boolean_ondemand("plugin:proc:/proc/net/dev", "drops for all interfaces", CONFIG_ONDEMAND_ONDEMAND); - if(do_fifo == -1) do_fifo = config_get_boolean_ondemand("plugin:proc:/proc/net/dev", "fifo for all interfaces", CONFIG_ONDEMAND_ONDEMAND); - if(do_compressed == -1) do_compressed = config_get_boolean_ondemand("plugin:proc:/proc/net/dev", "compressed packets for all interfaces", CONFIG_ONDEMAND_ONDEMAND); - if(do_events == -1) do_events = config_get_boolean_ondemand("plugin:proc:/proc/net/dev", "frames, collisions, carrier counters for all interfaces", CONFIG_ONDEMAND_ONDEMAND); + struct netdev *d = get_netdev(procfile_lineword(ff, l, 0)); - uint32_t lines = procfile_lines(ff), l; - uint32_t words; + if(unlikely(!d->configured)) { + // this is the first time we see this interface - char *iface; - unsigned long long rbytes, rpackets, rerrors, rdrops, rfifo, rframe, rcompressed, rmulticast; - unsigned long long tbytes, tpackets, terrors, tdrops, tfifo, tcollisions, tcarrier, tcompressed; + // remember we configured it + d->configured = 1; - for(l = 2; l < lines ;l++) { - words = procfile_linewords(ff, l); - if(words < 17) continue; - - iface = procfile_lineword(ff, l, 0); - - rbytes = strtoull(procfile_lineword(ff, l, 1), NULL, 10); - rpackets = strtoull(procfile_lineword(ff, l, 2), NULL, 10); - rerrors = strtoull(procfile_lineword(ff, l, 3), NULL, 10); - rdrops = strtoull(procfile_lineword(ff, l, 4), NULL, 10); - rfifo = strtoull(procfile_lineword(ff, l, 5), NULL, 10); - rframe = strtoull(procfile_lineword(ff, l, 6), NULL, 10); - rcompressed = strtoull(procfile_lineword(ff, l, 7), NULL, 10); - rmulticast = strtoull(procfile_lineword(ff, l, 8), NULL, 10); - - tbytes = strtoull(procfile_lineword(ff, l, 9), NULL, 10); - tpackets = strtoull(procfile_lineword(ff, l, 10), NULL, 10); - terrors = strtoull(procfile_lineword(ff, l, 11), NULL, 10); - tdrops = strtoull(procfile_lineword(ff, l, 12), NULL, 10); - tfifo = strtoull(procfile_lineword(ff, l, 13), NULL, 10); - tcollisions = strtoull(procfile_lineword(ff, l, 14), NULL, 10); - tcarrier = strtoull(procfile_lineword(ff, l, 15), NULL, 10); - tcompressed = strtoull(procfile_lineword(ff, l, 16), NULL, 10); - - int ddo_bandwidth = do_bandwidth, ddo_packets = do_packets, ddo_errors = do_errors, ddo_drops = do_drops, ddo_fifo = do_fifo, ddo_compressed = do_compressed, ddo_events = do_events; - - int default_enable = enable_new_interfaces; - - // prevent unused interfaces from creating charts - if(strcmp(iface, "lo") == 0) - default_enable = 0; - else { - int len = strlen(iface); - if(len >= 4 && strcmp(&iface[len-4], "-ifb") == 0) - default_enable = enable_ifb_interfaces; - } + d->enabled = enable_new_interfaces; + + if(d->enabled) + d->enabled = !simple_pattern_matches(disabled_list, d->name); - // check if the user wants it - { char var_name[512 + 1]; - snprintfz(var_name, 512, "plugin:proc:/proc/net/dev:%s", iface); - default_enable = config_get_boolean_ondemand(var_name, "enabled", default_enable); - if(default_enable == CONFIG_ONDEMAND_NO) continue; - if(default_enable == CONFIG_ONDEMAND_ONDEMAND && !rbytes && !tbytes) continue; - - ddo_bandwidth = config_get_boolean_ondemand(var_name, "bandwidth", ddo_bandwidth); - ddo_packets = config_get_boolean_ondemand(var_name, "packets", ddo_packets); - ddo_errors = config_get_boolean_ondemand(var_name, "errors", ddo_errors); - ddo_drops = config_get_boolean_ondemand(var_name, "drops", ddo_drops); - ddo_fifo = config_get_boolean_ondemand(var_name, "fifo", ddo_fifo); - ddo_compressed = config_get_boolean_ondemand(var_name, "compressed", ddo_compressed); - ddo_events = config_get_boolean_ondemand(var_name, "events", ddo_events); - - if(ddo_bandwidth == CONFIG_ONDEMAND_ONDEMAND && rbytes == 0 && tbytes == 0) ddo_bandwidth = 0; - if(ddo_errors == CONFIG_ONDEMAND_ONDEMAND && rerrors == 0 && terrors == 0) ddo_errors = 0; - if(ddo_drops == CONFIG_ONDEMAND_ONDEMAND && rdrops == 0 && tdrops == 0) ddo_drops = 0; - if(ddo_fifo == CONFIG_ONDEMAND_ONDEMAND && rfifo == 0 && tfifo == 0) ddo_fifo = 0; - if(ddo_compressed == CONFIG_ONDEMAND_ONDEMAND && rcompressed == 0 && tcompressed == 0) ddo_compressed = 0; - if(ddo_events == CONFIG_ONDEMAND_ONDEMAND && rframe == 0 && tcollisions == 0 && tcarrier == 0) ddo_events = 0; - - // for absolute values, we need to switch the setting to 'yes' - // to allow it refresh from now on - // if(ddo_fifo == CONFIG_ONDEMAND_ONDEMAND) config_set(var_name, "fifo", "yes"); + snprintfz(var_name, 512, "plugin:proc:/proc/net/dev:%s", d->name); + d->enabled = config_get_boolean_ondemand(var_name, "enabled", d->enabled); + + if(d->enabled == CONFIG_ONDEMAND_NO) + continue; + + d->do_bandwidth = config_get_boolean_ondemand(var_name, "bandwidth", do_bandwidth); + d->do_packets = config_get_boolean_ondemand(var_name, "packets", do_packets); + d->do_errors = config_get_boolean_ondemand(var_name, "errors", do_errors); + d->do_drops = config_get_boolean_ondemand(var_name, "drops", do_drops); + d->do_fifo = config_get_boolean_ondemand(var_name, "fifo", do_fifo); + d->do_compressed = config_get_boolean_ondemand(var_name, "compressed", do_compressed); + d->do_events = config_get_boolean_ondemand(var_name, "events", do_events); } - RRDSET *st; + if(unlikely(!d->enabled)) + continue; + + d->rbytes = str2ull(procfile_lineword(ff, l, 1)); + d->rpackets = str2ull(procfile_lineword(ff, l, 2)); + d->rerrors = str2ull(procfile_lineword(ff, l, 3)); + d->rdrops = str2ull(procfile_lineword(ff, l, 4)); + d->rfifo = str2ull(procfile_lineword(ff, l, 5)); + d->rframe = str2ull(procfile_lineword(ff, l, 6)); + d->rcompressed = str2ull(procfile_lineword(ff, l, 7)); + d->rmulticast = str2ull(procfile_lineword(ff, l, 8)); + + d->tbytes = str2ull(procfile_lineword(ff, l, 9)); + d->tpackets = str2ull(procfile_lineword(ff, l, 10)); + d->terrors = str2ull(procfile_lineword(ff, l, 11)); + d->tdrops = str2ull(procfile_lineword(ff, l, 12)); + d->tfifo = str2ull(procfile_lineword(ff, l, 13)); + d->tcollisions = str2ull(procfile_lineword(ff, l, 14)); + d->tcarrier = str2ull(procfile_lineword(ff, l, 15)); + d->tcompressed = str2ull(procfile_lineword(ff, l, 16)); // -------------------------------------------------------------------- - if(ddo_bandwidth) { - st = rrdset_find_bytype("net", iface); - if(!st) { - st = rrdset_create("net", iface, NULL, iface, "net.net", "Bandwidth", "kilobits/s", 7000, update_every, RRDSET_TYPE_AREA); + if(unlikely((d->do_bandwidth == CONFIG_ONDEMAND_ONDEMAND && (d->rbytes || d->tbytes)))) + d->do_bandwidth = CONFIG_ONDEMAND_YES; - rrddim_add(st, "received", NULL, 8, 1024, RRDDIM_INCREMENTAL); - rrddim_add(st, "sent", NULL, -8, 1024, RRDDIM_INCREMENTAL); + if(d->do_bandwidth == CONFIG_ONDEMAND_YES) { + if(unlikely(!d->st_bandwidth)) { + d->st_bandwidth = rrdset_find_bytype("net", d->name); + + if(!d->st_bandwidth) + d->st_bandwidth = rrdset_create("net", d->name, NULL, d->name, "net.net", "Bandwidth", "kilobits/s", 7000, update_every, RRDSET_TYPE_AREA); + + d->rd_rbytes = rrddim_add(d->st_bandwidth, "received", NULL, 8, 1024, RRDDIM_INCREMENTAL); + d->rd_tbytes = rrddim_add(d->st_bandwidth, "sent", NULL, -8, 1024, RRDDIM_INCREMENTAL); } - else rrdset_next(st); + else rrdset_next(d->st_bandwidth); - rrddim_set(st, "received", rbytes); - rrddim_set(st, "sent", tbytes); - rrdset_done(st); + rrddim_set_by_pointer(d->st_bandwidth, d->rd_rbytes, d->rbytes); + rrddim_set_by_pointer(d->st_bandwidth, d->rd_tbytes, d->tbytes); + rrdset_done(d->st_bandwidth); } // -------------------------------------------------------------------- - if(ddo_packets) { - st = rrdset_find_bytype("net_packets", iface); - if(!st) { - st = rrdset_create("net_packets", iface, NULL, iface, "net.packets", "Packets", "packets/s", 7001, update_every, RRDSET_TYPE_LINE); - st->isdetail = 1; + if(unlikely((d->do_packets == CONFIG_ONDEMAND_ONDEMAND && (d->rpackets || d->tpackets || d->rmulticast)))) + d->do_packets = CONFIG_ONDEMAND_YES; + + if(d->do_packets == CONFIG_ONDEMAND_YES) { + if(unlikely(!d->st_packets)) { + d->st_packets = rrdset_find_bytype("net_packets", d->name); + + if(!d->st_packets) + d->st_packets = rrdset_create("net_packets", d->name, NULL, d->name, "net.packets", "Packets", "packets/s", 7001, update_every, RRDSET_TYPE_LINE); + + d->st_packets->isdetail = 1; - rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "multicast", NULL, 1, 1, RRDDIM_INCREMENTAL); + d->rd_rpackets = rrddim_add(d->st_packets, "received", NULL, 1, 1, RRDDIM_INCREMENTAL); + d->rd_tpackets = rrddim_add(d->st_packets, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL); + d->rd_rmulticast = rrddim_add(d->st_packets, "multicast", NULL, 1, 1, RRDDIM_INCREMENTAL); } - else rrdset_next(st); + else rrdset_next(d->st_packets); - rrddim_set(st, "received", rpackets); - rrddim_set(st, "sent", tpackets); - rrddim_set(st, "multicast", rmulticast); - rrdset_done(st); + rrddim_set_by_pointer(d->st_packets, d->rd_rpackets, d->rpackets); + rrddim_set_by_pointer(d->st_packets, d->rd_tpackets, d->tpackets); + rrddim_set_by_pointer(d->st_packets, d->rd_rmulticast, d->rmulticast); + rrdset_done(d->st_packets); } // -------------------------------------------------------------------- - if(ddo_errors) { - st = rrdset_find_bytype("net_errors", iface); - if(!st) { - st = rrdset_create("net_errors", iface, NULL, iface, "net.errors", "Interface Errors", "errors/s", 7002, update_every, RRDSET_TYPE_LINE); - st->isdetail = 1; + if(unlikely((d->do_errors == CONFIG_ONDEMAND_ONDEMAND && (d->rerrors || d->terrors)))) + d->do_errors = CONFIG_ONDEMAND_YES; - rrddim_add(st, "inbound", NULL, 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "outbound", NULL, -1, 1, RRDDIM_INCREMENTAL); + if(d->do_errors == CONFIG_ONDEMAND_YES) { + if(unlikely(!d->st_errors)) { + d->st_errors = rrdset_find_bytype("net_errors", d->name); + + if(!d->st_errors) + d->st_errors = rrdset_create("net_errors", d->name, NULL, d->name, "net.errors", "Interface Errors", "errors/s", 7002, update_every, RRDSET_TYPE_LINE); + + d->st_errors->isdetail = 1; + + d->rd_rerrors = rrddim_add(d->st_errors, "inbound", NULL, 1, 1, RRDDIM_INCREMENTAL); + d->rd_terrors = rrddim_add(d->st_errors, "outbound", NULL, -1, 1, RRDDIM_INCREMENTAL); } - else rrdset_next(st); + else rrdset_next(d->st_errors); - rrddim_set(st, "inbound", rerrors); - rrddim_set(st, "outbound", terrors); - rrdset_done(st); + rrddim_set_by_pointer(d->st_errors, d->rd_rerrors, d->rerrors); + rrddim_set_by_pointer(d->st_errors, d->rd_terrors, d->terrors); + rrdset_done(d->st_errors); } // -------------------------------------------------------------------- - if(ddo_drops) { - st = rrdset_find_bytype("net_drops", iface); - if(!st) { - st = rrdset_create("net_drops", iface, NULL, iface, "net.drops", "Interface Drops", "drops/s", 7003, update_every, RRDSET_TYPE_LINE); - st->isdetail = 1; + if(unlikely((d->do_drops == CONFIG_ONDEMAND_ONDEMAND && (d->rdrops || d->tdrops)))) + d->do_drops = CONFIG_ONDEMAND_YES; + + if(d->do_drops == CONFIG_ONDEMAND_YES) { + if(unlikely(!d->st_drops)) { + d->st_drops = rrdset_find_bytype("net_drops", d->name); - rrddim_add(st, "inbound", NULL, 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "outbound", NULL, -1, 1, RRDDIM_INCREMENTAL); + if(!d->st_drops) + d->st_drops = rrdset_create("net_drops", d->name, NULL, d->name, "net.drops", "Interface Drops", "drops/s", 7003, update_every, RRDSET_TYPE_LINE); + + d->st_drops->isdetail = 1; + + d->rd_rdrops = rrddim_add(d->st_drops, "inbound", NULL, 1, 1, RRDDIM_INCREMENTAL); + d->rd_tdrops = rrddim_add(d->st_drops, "outbound", NULL, -1, 1, RRDDIM_INCREMENTAL); } - else rrdset_next(st); + else rrdset_next(d->st_drops); - rrddim_set(st, "inbound", rdrops); - rrddim_set(st, "outbound", tdrops); - rrdset_done(st); + rrddim_set_by_pointer(d->st_drops, d->rd_rdrops, d->rdrops); + rrddim_set_by_pointer(d->st_drops, d->rd_tdrops, d->tdrops); + rrdset_done(d->st_drops); } // -------------------------------------------------------------------- - if(ddo_fifo) { - st = rrdset_find_bytype("net_fifo", iface); - if(!st) { - st = rrdset_create("net_fifo", iface, NULL, iface, "net.fifo", "Interface FIFO Buffer Errors", "errors", 7004, update_every, RRDSET_TYPE_LINE); - st->isdetail = 1; + if(unlikely((d->do_fifo == CONFIG_ONDEMAND_ONDEMAND && (d->rfifo || d->tfifo)))) + d->do_fifo = CONFIG_ONDEMAND_YES; + + if(d->do_fifo == CONFIG_ONDEMAND_YES) { + if(unlikely(!d->st_fifo)) { + d->st_fifo = rrdset_find_bytype("net_fifo", d->name); + + if(!d->st_fifo) + d->st_fifo = rrdset_create("net_fifo", d->name, NULL, d->name, "net.fifo", "Interface FIFO Buffer Errors", "errors", 7004, update_every, RRDSET_TYPE_LINE); + + d->st_fifo->isdetail = 1; - rrddim_add(st, "receive", NULL, 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "transmit", NULL, -1, 1, RRDDIM_INCREMENTAL); + d->rd_rfifo = rrddim_add(d->st_fifo, "receive", NULL, 1, 1, RRDDIM_INCREMENTAL); + d->rd_tfifo = rrddim_add(d->st_fifo, "transmit", NULL, -1, 1, RRDDIM_INCREMENTAL); } - else rrdset_next(st); + else rrdset_next(d->st_fifo); - rrddim_set(st, "receive", rfifo); - rrddim_set(st, "transmit", tfifo); - rrdset_done(st); + rrddim_set_by_pointer(d->st_fifo, d->rd_rfifo, d->rfifo); + rrddim_set_by_pointer(d->st_fifo, d->rd_tfifo, d->tfifo); + rrdset_done(d->st_fifo); } // -------------------------------------------------------------------- - if(ddo_compressed) { - st = rrdset_find_bytype("net_compressed", iface); - if(!st) { - st = rrdset_create("net_compressed", iface, NULL, iface, "net.compressed", "Compressed Packets", "packets/s", 7005, update_every, RRDSET_TYPE_LINE); - st->isdetail = 1; + if(unlikely((d->do_compressed == CONFIG_ONDEMAND_ONDEMAND && (d->rcompressed || d->tcompressed)))) + d->do_compressed = CONFIG_ONDEMAND_YES; - rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL); + if(d->do_compressed == CONFIG_ONDEMAND_YES) { + if(unlikely(!d->st_compressed)) { + d->st_compressed = rrdset_find_bytype("net_compressed", d->name); + if(!d->st_compressed) + d->st_compressed = rrdset_create("net_compressed", d->name, NULL, d->name, "net.compressed", "Compressed Packets", "packets/s", 7005, update_every, RRDSET_TYPE_LINE); + + d->st_compressed->isdetail = 1; + + d->rd_rcompressed = rrddim_add(d->st_compressed, "received", NULL, 1, 1, RRDDIM_INCREMENTAL); + d->rd_tcompressed = rrddim_add(d->st_compressed, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL); } - else rrdset_next(st); + else rrdset_next(d->st_compressed); - rrddim_set(st, "received", rcompressed); - rrddim_set(st, "sent", tcompressed); - rrdset_done(st); + rrddim_set_by_pointer(d->st_compressed, d->rd_rcompressed, d->rcompressed); + rrddim_set_by_pointer(d->st_compressed, d->rd_tcompressed, d->tcompressed); + rrdset_done(d->st_compressed); } // -------------------------------------------------------------------- - if(ddo_events) { - st = rrdset_find_bytype("net_events", iface); - if(!st) { - st = rrdset_create("net_events", iface, NULL, iface, "net.events", "Network Interface Events", "events/s", 7006, update_every, RRDSET_TYPE_LINE); - st->isdetail = 1; + if(unlikely((d->do_events == CONFIG_ONDEMAND_ONDEMAND && (d->rframe || d->tcollisions || d->tcarrier)))) + d->do_events = CONFIG_ONDEMAND_YES; + + if(d->do_events == CONFIG_ONDEMAND_YES) { + if(unlikely(!d->st_events)) { + d->st_events = rrdset_find_bytype("net_events", d->name); + if(!d->st_events) + d->st_events = rrdset_create("net_events", d->name, NULL, d->name, "net.events", "Network Interface Events", "events/s", 7006, update_every, RRDSET_TYPE_LINE); + + d->st_events->isdetail = 1; - rrddim_add(st, "frames", NULL, 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "collisions", NULL, -1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "carrier", NULL, -1, 1, RRDDIM_INCREMENTAL); + d->rd_rframe = rrddim_add(d->st_events, "frames", NULL, 1, 1, RRDDIM_INCREMENTAL); + d->rd_tcollisions = rrddim_add(d->st_events, "collisions", NULL, -1, 1, RRDDIM_INCREMENTAL); + d->rd_tcarrier = rrddim_add(d->st_events, "carrier", NULL, -1, 1, RRDDIM_INCREMENTAL); } - else rrdset_next(st); + else rrdset_next(d->st_events); - rrddim_set(st, "frames", rframe); - rrddim_set(st, "collisions", tcollisions); - rrddim_set(st, "carrier", tcarrier); - rrdset_done(st); + rrddim_set_by_pointer(d->st_events, d->rd_rframe, d->rframe); + rrddim_set_by_pointer(d->st_events, d->rd_tcollisions, d->tcollisions); + rrddim_set_by_pointer(d->st_events, d->rd_tcarrier, d->tcarrier); + rrdset_done(d->st_events); } } diff --git a/src/proc_net_ip_vs_stats.c b/src/proc_net_ip_vs_stats.c index de3e0e460..34cadaea7 100644 --- a/src/proc_net_ip_vs_stats.c +++ b/src/proc_net_ip_vs_stats.c @@ -1,9 +1,8 @@ #include "common.h" #define RRD_TYPE_NET_IPVS "ipvs" -#define RRD_TYPE_NET_IPVS_LEN strlen(RRD_TYPE_NET_IPVS) -int do_proc_net_ip_vs_stats(int update_every, unsigned long long dt) { +int do_proc_net_ip_vs_stats(int update_every, usec_t dt) { static int do_bandwidth = -1, do_sockets = -1, do_packets = -1; static procfile *ff = NULL; diff --git a/src/proc_net_netstat.c b/src/proc_net_netstat.c index ea38acf29..8741a71c9 100644 --- a/src/proc_net_netstat.c +++ b/src/proc_net_netstat.c @@ -1,197 +1,22 @@ #include "common.h" - -struct netstat_columns { - char *name; - uint32_t hash; - unsigned long long value; - int multiplier; // not needed everywhere - char *label; // not needed everywhere -}; - -static struct netstat_columns tcpext_data[] = { - { "SyncookiesSent", 0, 0, 1, NULL }, - { "SyncookiesRecv", 0, 0, 1, NULL }, - { "SyncookiesFailed", 0, 0, 1, NULL }, - { "EmbryonicRsts", 0, 0, 1, NULL }, - { "PruneCalled", 0, 0, 1, NULL }, - { "RcvPruned", 0, 0, 1, NULL }, - { "OfoPruned", 0, 0, 1, NULL }, - { "OutOfWindowIcmps", 0, 0, 1, NULL }, - { "LockDroppedIcmps", 0, 0, 1, NULL }, - { "ArpFilter", 0, 0, 1, NULL }, - { "TW", 0, 0, 1, NULL }, - { "TWRecycled", 0, 0, 1, NULL }, - { "TWKilled", 0, 0, 1, NULL }, - { "PAWSPassive", 0, 0, 1, NULL }, - { "PAWSActive", 0, 0, 1, NULL }, - { "PAWSEstab", 0, 0, 1, NULL }, - { "DelayedACKs", 0, 0, 1, NULL }, - { "DelayedACKLocked", 0, 0, 1, NULL }, - { "DelayedACKLost", 0, 0, 1, NULL }, - { "ListenOverflows", 0, 0, 1, NULL }, - { "ListenDrops", 0, 0, 1, NULL }, - { "TCPPrequeued", 0, 0, 1, NULL }, - { "TCPDirectCopyFromBacklog", 0, 0, 1, NULL }, - { "TCPDirectCopyFromPrequeue", 0, 0, 1, NULL }, - { "TCPPrequeueDropped", 0, 0, 1, NULL }, - { "TCPHPHits", 0, 0, 1, NULL }, - { "TCPHPHitsToUser", 0, 0, 1, NULL }, - { "TCPPureAcks", 0, 0, 1, NULL }, - { "TCPHPAcks", 0, 0, 1, NULL }, - { "TCPRenoRecovery", 0, 0, 1, NULL }, - { "TCPSackRecovery", 0, 0, 1, NULL }, - { "TCPSACKReneging", 0, 0, 1, NULL }, - { "TCPFACKReorder", 0, 0, 1, NULL }, - { "TCPSACKReorder", 0, 0, 1, NULL }, - { "TCPRenoReorder", 0, 0, 1, NULL }, - { "TCPTSReorder", 0, 0, 1, NULL }, - { "TCPFullUndo", 0, 0, 1, NULL }, - { "TCPPartialUndo", 0, 0, 1, NULL }, - { "TCPDSACKUndo", 0, 0, 1, NULL }, - { "TCPLossUndo", 0, 0, 1, NULL }, - { "TCPLostRetransmit", 0, 0, 1, NULL }, - { "TCPRenoFailures", 0, 0, 1, NULL }, - { "TCPSackFailures", 0, 0, 1, NULL }, - { "TCPLossFailures", 0, 0, 1, NULL }, - { "TCPFastRetrans", 0, 0, 1, NULL }, - { "TCPForwardRetrans", 0, 0, 1, NULL }, - { "TCPSlowStartRetrans", 0, 0, 1, NULL }, - { "TCPTimeouts", 0, 0, 1, NULL }, - { "TCPLossProbes", 0, 0, 1, NULL }, - { "TCPLossProbeRecovery", 0, 0, 1, NULL }, - { "TCPRenoRecoveryFail", 0, 0, 1, NULL }, - { "TCPSackRecoveryFail", 0, 0, 1, NULL }, - { "TCPSchedulerFailed", 0, 0, 1, NULL }, - { "TCPRcvCollapsed", 0, 0, 1, NULL }, - { "TCPDSACKOldSent", 0, 0, 1, NULL }, - { "TCPDSACKOfoSent", 0, 0, 1, NULL }, - { "TCPDSACKRecv", 0, 0, 1, NULL }, - { "TCPDSACKOfoRecv", 0, 0, 1, NULL }, - { "TCPAbortOnData", 0, 0, 1, NULL }, - { "TCPAbortOnClose", 0, 0, 1, NULL }, - { "TCPAbortOnMemory", 0, 0, 1, NULL }, - { "TCPAbortOnTimeout", 0, 0, 1, NULL }, - { "TCPAbortOnLinger", 0, 0, 1, NULL }, - { "TCPAbortFailed", 0, 0, 1, NULL }, - { "TCPMemoryPressures", 0, 0, 1, NULL }, - { "TCPSACKDiscard", 0, 0, 1, NULL }, - { "TCPDSACKIgnoredOld", 0, 0, 1, NULL }, - { "TCPDSACKIgnoredNoUndo", 0, 0, 1, NULL }, - { "TCPSpuriousRTOs", 0, 0, 1, NULL }, - { "TCPMD5NotFound", 0, 0, 1, NULL }, - { "TCPMD5Unexpected", 0, 0, 1, NULL }, - { "TCPSackShifted", 0, 0, 1, NULL }, - { "TCPSackMerged", 0, 0, 1, NULL }, - { "TCPSackShiftFallback", 0, 0, 1, NULL }, - { "TCPBacklogDrop", 0, 0, 1, NULL }, - { "TCPMinTTLDrop", 0, 0, 1, NULL }, - { "TCPDeferAcceptDrop", 0, 0, 1, NULL }, - { "IPReversePathFilter", 0, 0, 1, NULL }, - { "TCPTimeWaitOverflow", 0, 0, 1, NULL }, - { "TCPReqQFullDoCookies", 0, 0, 1, NULL }, - { "TCPReqQFullDrop", 0, 0, 1, NULL }, - { "TCPRetransFail", 0, 0, 1, NULL }, - { "TCPRcvCoalesce", 0, 0, 1, NULL }, - { "TCPOFOQueue", 0, 0, 1, NULL }, - { "TCPOFODrop", 0, 0, 1, NULL }, - { "TCPOFOMerge", 0, 0, 1, NULL }, - { "TCPChallengeACK", 0, 0, 1, NULL }, - { "TCPSYNChallenge", 0, 0, 1, NULL }, - { "TCPFastOpenActive", 0, 0, 1, NULL }, - { "TCPFastOpenActiveFail", 0, 0, 1, NULL }, - { "TCPFastOpenPassive", 0, 0, 1, NULL }, - { "TCPFastOpenPassiveFail", 0, 0, 1, NULL }, - { "TCPFastOpenListenOverflow", 0, 0, 1, NULL }, - { "TCPFastOpenCookieReqd", 0, 0, 1, NULL }, - { "TCPSpuriousRtxHostQueues", 0, 0, 1, NULL }, - { "BusyPollRxPackets", 0, 0, 1, NULL }, - { "TCPAutoCorking", 0, 0, 1, NULL }, - { "TCPFromZeroWindowAdv", 0, 0, 1, NULL }, - { "TCPToZeroWindowAdv", 0, 0, 1, NULL }, - { "TCPWantZeroWindowAdv", 0, 0, 1, NULL }, - { "TCPSynRetrans", 0, 0, 1, NULL }, - { "TCPOrigDataSent", 0, 0, 1, NULL }, - { "TCPHystartTrainDetect", 0, 0, 1, NULL }, - { "TCPHystartTrainCwnd", 0, 0, 1, NULL }, - { "TCPHystartDelayDetect", 0, 0, 1, NULL }, - { "TCPHystartDelayCwnd", 0, 0, 1, NULL }, - { "TCPACKSkippedSynRecv", 0, 0, 1, NULL }, - { "TCPACKSkippedPAWS", 0, 0, 1, NULL }, - { "TCPACKSkippedSeq", 0, 0, 1, NULL }, - { "TCPACKSkippedFinWait2", 0, 0, 1, NULL }, - { "TCPACKSkippedTimeWait", 0, 0, 1, NULL }, - { "TCPACKSkippedChallenge", 0, 0, 1, NULL }, - { "TCPWinProbe", 0, 0, 1, NULL }, - { "TCPKeepAlive", 0, 0, 1, NULL }, - { "TCPMTUPFail", 0, 0, 1, NULL }, - { "TCPMTUPSuccess", 0, 0, 1, NULL }, - { NULL, 0, 0, 0, NULL } -}; - -static struct netstat_columns ipext_data[] = { - { "InNoRoutes", 0, 0, 1, NULL }, - { "InTruncatedPkts", 0, 0, 1, NULL }, - { "InMcastPkts", 0, 0, 1, NULL }, - { "OutMcastPkts", 0, 0, 1, NULL }, - { "InBcastPkts", 0, 0, 1, NULL }, - { "OutBcastPkts", 0, 0, 1, NULL }, - { "InOctets", 0, 0, 1, NULL }, - { "OutOctets", 0, 0, 1, NULL }, - { "InMcastOctets", 0, 0, 1, NULL }, - { "OutMcastOctets", 0, 0, 1, NULL }, - { "InBcastOctets", 0, 0, 1, NULL }, - { "OutBcastOctets", 0, 0, 1, NULL }, - { "InCsumErrors", 0, 0, 1, NULL }, - { "InNoECTPkts", 0, 0, 1, NULL }, - { "InECT1Pkts", 0, 0, 1, NULL }, - { "InECT0Pkts", 0, 0, 1, NULL }, - { "InCEPkts", 0, 0, 1, NULL }, - { NULL, 0, 0, 0, NULL } -}; - -static void hash_array(struct netstat_columns *nc) { - int i; - - for(i = 0; nc[i].name ;i++) - nc[i].hash = simple_hash(nc[i].name); -} - -static unsigned long long *netstat_columns_find(struct netstat_columns *nc, const char *name) { - uint32_t i, hash = simple_hash(name); - - for(i = 0; nc[i].name ;i++) - if(unlikely(nc[i].hash == hash && !strcmp(nc[i].name, name))) - return &nc[i].value; - - fatal("Cannot find key '%s' in /proc/net/netstat internal array.", name); -} - -static void parse_line_pair(procfile *ff, struct netstat_columns *nc, uint32_t header_line, uint32_t values_line) { - uint32_t hwords = procfile_linewords(ff, header_line); - uint32_t vwords = procfile_linewords(ff, values_line); - uint32_t w, i; +static void parse_line_pair(procfile *ff, ARL_BASE *base, size_t header_line, size_t values_line) { + size_t hwords = procfile_linewords(ff, header_line); + size_t vwords = procfile_linewords(ff, values_line); + size_t w; if(unlikely(vwords > hwords)) { - error("File /proc/net/netstat on header line %u has %u words, but on value line %u has %u words.", header_line, hwords, values_line, vwords); + error("File /proc/net/netstat on header line %zu has %zu words, but on value line %zu has %zu words.", header_line, hwords, values_line, vwords); vwords = hwords; } for(w = 1; w < vwords ;w++) { - char *key = procfile_lineword(ff, header_line, w); - uint32_t hash = simple_hash(key); - - for(i = 0 ; nc[i].name ;i++) { - if(unlikely(hash == nc[i].hash && !strcmp(key, nc[i].name))) { - nc[i].value = strtoull(procfile_lineword(ff, values_line, w), NULL, 10); - break; - } - } + if(unlikely(arl_check(base, procfile_lineword(ff, header_line, w), procfile_lineword(ff, values_line, w)))) + break; } } - -int do_proc_net_netstat(int update_every, unsigned long long dt) { +int do_proc_net_netstat(int update_every, usec_t dt) { (void)dt; static int do_bandwidth = -1, do_inerrors = -1, do_mcast = -1, do_bcast = -1, do_mcast_p = -1, do_bcast_p = -1, do_ecn = -1, \ @@ -199,178 +24,80 @@ int do_proc_net_netstat(int update_every, unsigned long long dt) { static uint32_t hash_ipext = 0, hash_tcpext = 0; static procfile *ff = NULL; - static unsigned long long *tcpext_TCPRenoReorder = NULL; - static unsigned long long *tcpext_TCPFACKReorder = NULL; - static unsigned long long *tcpext_TCPSACKReorder = NULL; - static unsigned long long *tcpext_TCPTSReorder = NULL; - - static unsigned long long *tcpext_SyncookiesSent = NULL; - static unsigned long long *tcpext_SyncookiesRecv = NULL; - static unsigned long long *tcpext_SyncookiesFailed = NULL; + static ARL_BASE *arl_tcpext = NULL; + static ARL_BASE *arl_ipext = NULL; + + // -------------------------------------------------------------------- + // IPv4 + + // IPv4 bandwidth + static unsigned long long ipext_InOctets = 0; + static unsigned long long ipext_OutOctets = 0; + + // IPv4 input errors + static unsigned long long ipext_InNoRoutes = 0; + static unsigned long long ipext_InTruncatedPkts = 0; + static unsigned long long ipext_InCsumErrors = 0; + + // IPv4 multicast bandwidth + static unsigned long long ipext_InMcastOctets = 0; + static unsigned long long ipext_OutMcastOctets = 0; + + // IPv4 multicast packets + static unsigned long long ipext_InMcastPkts = 0; + static unsigned long long ipext_OutMcastPkts = 0; + + // IPv4 broadcast bandwidth + static unsigned long long ipext_InBcastOctets = 0; + static unsigned long long ipext_OutBcastOctets = 0; + + // IPv4 broadcast packets + static unsigned long long ipext_InBcastPkts = 0; + static unsigned long long ipext_OutBcastPkts = 0; + + // IPv4 ECN + static unsigned long long ipext_InNoECTPkts = 0; + static unsigned long long ipext_InECT1Pkts = 0; + static unsigned long long ipext_InECT0Pkts = 0; + static unsigned long long ipext_InCEPkts = 0; + + // -------------------------------------------------------------------- + // IPv4 TCP + + // IPv4 TCP Reordering + static unsigned long long tcpext_TCPRenoReorder = 0; + static unsigned long long tcpext_TCPFACKReorder = 0; + static unsigned long long tcpext_TCPSACKReorder = 0; + static unsigned long long tcpext_TCPTSReorder = 0; + + // IPv4 TCP SYN Cookies + static unsigned long long tcpext_SyncookiesSent = 0; + static unsigned long long tcpext_SyncookiesRecv = 0; + static unsigned long long tcpext_SyncookiesFailed = 0; + + // IPv4 TCP Out Of Order Queue + // http://www.spinics.net/lists/netdev/msg204696.html + static unsigned long long tcpext_TCPOFOQueue = 0; // Number of packets queued in OFO queue + static unsigned long long tcpext_TCPOFODrop = 0; // Number of packets meant to be queued in OFO but dropped because socket rcvbuf limit hit. + static unsigned long long tcpext_TCPOFOMerge = 0; // Number of packets in OFO that were merged with other packets. + static unsigned long long tcpext_OfoPruned = 0; // packets dropped from out-of-order queue because of socket buffer overrun + + // IPv4 TCP connection resets + // https://github.com/ecki/net-tools/blob/bd8bceaed2311651710331a7f8990c3e31be9840/statistics.c + static unsigned long long tcpext_TCPAbortOnData = 0; // connections reset due to unexpected data + static unsigned long long tcpext_TCPAbortOnClose = 0; // connections reset due to early user close + static unsigned long long tcpext_TCPAbortOnMemory = 0; // connections aborted due to memory pressure + static unsigned long long tcpext_TCPAbortOnTimeout = 0; // connections aborted due to timeout + static unsigned long long tcpext_TCPAbortOnLinger = 0; // connections aborted after user close in linger timeout + static unsigned long long tcpext_TCPAbortFailed = 0; // times unable to send RST due to no memory + + // IPv4 TCP memory pressures + static unsigned long long tcpext_TCPMemoryPressures = 0; + + if(unlikely(!arl_ipext)) { + hash_ipext = simple_hash("IpExt"); + hash_tcpext = simple_hash("TcpExt"); - static unsigned long long *tcpext_TCPOFOQueue = NULL; // Number of packets queued in OFO queue - static unsigned long long *tcpext_TCPOFODrop = NULL; // Number of packets meant to be queued in OFO but dropped because socket rcvbuf limit hit. - static unsigned long long *tcpext_TCPOFOMerge = NULL; // Number of packets in OFO that were merged with other packets. - static unsigned long long *tcpext_OfoPruned = NULL; // packets dropped from out-of-order queue because of socket buffer overrun - - static unsigned long long *tcpext_TCPAbortOnData = NULL; // connections reset due to unexpected data - static unsigned long long *tcpext_TCPAbortOnClose = NULL; // connections reset due to early user close - static unsigned long long *tcpext_TCPAbortOnMemory = NULL; // connections aborted due to memory pressure - static unsigned long long *tcpext_TCPAbortOnTimeout = NULL; // connections aborted due to timeout - static unsigned long long *tcpext_TCPAbortOnLinger = NULL; // connections aborted after user close in linger timeout - static unsigned long long *tcpext_TCPAbortFailed = NULL; // times unable to send RST due to no memory - - static unsigned long long *tcpext_TCPMemoryPressures = NULL; - -/* - // connection rejects - static unsigned long long *tcpext_PAWSActive = NULL; // active connections rejected because of time stamp - static unsigned long long *tcpext_PAWSPassive = NULL; // passive connections rejected because of time stamp - - static unsigned long long *tcpext_TCPTimeouts = NULL; - - static unsigned long long *tcpext_TCPDSACKUndo = NULL; - static unsigned long long *tcpext_TCPDSACKOldSent = NULL; - static unsigned long long *tcpext_TCPDSACKOfoSent = NULL; - static unsigned long long *tcpext_TCPDSACKRecv = NULL; - static unsigned long long *tcpext_TCPDSACKOfoRecv = NULL; - static unsigned long long *tcpext_TCPDSACKIgnoredOld = NULL; - static unsigned long long *tcpext_TCPDSACKIgnoredNoUndo = NULL; - - - static unsigned long long *tcpext_EmbryonicRsts = NULL; - - static unsigned long long *tcpext_PruneCalled = NULL; - static unsigned long long *tcpext_RcvPruned = NULL; - static unsigned long long *tcpext_OutOfWindowIcmps = NULL; - static unsigned long long *tcpext_LockDroppedIcmps = NULL; - static unsigned long long *tcpext_ArpFilter = NULL; - - static unsigned long long *tcpext_TW = NULL; - static unsigned long long *tcpext_TWRecycled = NULL; - static unsigned long long *tcpext_TWKilled = NULL; - - static unsigned long long *tcpext_PAWSEstab = NULL; - - static unsigned long long *tcpext_DelayedACKs = NULL; - static unsigned long long *tcpext_DelayedACKLocked = NULL; - static unsigned long long *tcpext_DelayedACKLost = NULL; - - static unsigned long long *tcpext_ListenOverflows = NULL; - static unsigned long long *tcpext_ListenDrops = NULL; - - static unsigned long long *tcpext_TCPPrequeued = NULL; - - static unsigned long long *tcpext_TCPDirectCopyFromBacklog = NULL; - static unsigned long long *tcpext_TCPDirectCopyFromPrequeue = NULL; - static unsigned long long *tcpext_TCPPrequeueDropped = NULL; - - static unsigned long long *tcpext_TCPHPHits = NULL; - static unsigned long long *tcpext_TCPHPHitsToUser = NULL; - static unsigned long long *tcpext_TCPHPAcks = NULL; - - static unsigned long long *tcpext_TCPPureAcks = NULL; - static unsigned long long *tcpext_TCPRenoRecovery = NULL; - - static unsigned long long *tcpext_TCPSackRecovery = NULL; - static unsigned long long *tcpext_TCPSackFailures = NULL; - static unsigned long long *tcpext_TCPSACKReneging = NULL; - static unsigned long long *tcpext_TCPSackRecoveryFail = NULL; - static unsigned long long *tcpext_TCPSACKDiscard = NULL; - static unsigned long long *tcpext_TCPSackShifted = NULL; - static unsigned long long *tcpext_TCPSackMerged = NULL; - static unsigned long long *tcpext_TCPSackShiftFallback = NULL; - - - static unsigned long long *tcpext_TCPFullUndo = NULL; - static unsigned long long *tcpext_TCPPartialUndo = NULL; - - static unsigned long long *tcpext_TCPLossUndo = NULL; - static unsigned long long *tcpext_TCPLostRetransmit = NULL; - - static unsigned long long *tcpext_TCPRenoFailures = NULL; - - static unsigned long long *tcpext_TCPLossFailures = NULL; - static unsigned long long *tcpext_TCPFastRetrans = NULL; - static unsigned long long *tcpext_TCPForwardRetrans = NULL; - static unsigned long long *tcpext_TCPSlowStartRetrans = NULL; - static unsigned long long *tcpext_TCPLossProbes = NULL; - static unsigned long long *tcpext_TCPLossProbeRecovery = NULL; - static unsigned long long *tcpext_TCPRenoRecoveryFail = NULL; - static unsigned long long *tcpext_TCPSchedulerFailed = NULL; - static unsigned long long *tcpext_TCPRcvCollapsed = NULL; - - static unsigned long long *tcpext_TCPSpuriousRTOs = NULL; - static unsigned long long *tcpext_TCPMD5NotFound = NULL; - static unsigned long long *tcpext_TCPMD5Unexpected = NULL; - - static unsigned long long *tcpext_TCPBacklogDrop = NULL; - static unsigned long long *tcpext_TCPMinTTLDrop = NULL; - static unsigned long long *tcpext_TCPDeferAcceptDrop = NULL; - static unsigned long long *tcpext_IPReversePathFilter = NULL; - static unsigned long long *tcpext_TCPTimeWaitOverflow = NULL; - static unsigned long long *tcpext_TCPReqQFullDoCookies = NULL; - static unsigned long long *tcpext_TCPReqQFullDrop = NULL; - static unsigned long long *tcpext_TCPRetransFail = NULL; - static unsigned long long *tcpext_TCPRcvCoalesce = NULL; - - static unsigned long long *tcpext_TCPChallengeACK = NULL; - static unsigned long long *tcpext_TCPSYNChallenge = NULL; - - static unsigned long long *tcpext_TCPFastOpenActive = NULL; - static unsigned long long *tcpext_TCPFastOpenActiveFail = NULL; - static unsigned long long *tcpext_TCPFastOpenPassive = NULL; - static unsigned long long *tcpext_TCPFastOpenPassiveFail = NULL; - static unsigned long long *tcpext_TCPFastOpenListenOverflow = NULL; - static unsigned long long *tcpext_TCPFastOpenCookieReqd = NULL; - - static unsigned long long *tcpext_TCPSpuriousRtxHostQueues = NULL; - static unsigned long long *tcpext_BusyPollRxPackets = NULL; - static unsigned long long *tcpext_TCPAutoCorking = NULL; - static unsigned long long *tcpext_TCPFromZeroWindowAdv = NULL; - static unsigned long long *tcpext_TCPToZeroWindowAdv = NULL; - static unsigned long long *tcpext_TCPWantZeroWindowAdv = NULL; - static unsigned long long *tcpext_TCPSynRetrans = NULL; - static unsigned long long *tcpext_TCPOrigDataSent = NULL; - - static unsigned long long *tcpext_TCPHystartTrainDetect = NULL; - static unsigned long long *tcpext_TCPHystartTrainCwnd = NULL; - static unsigned long long *tcpext_TCPHystartDelayDetect = NULL; - static unsigned long long *tcpext_TCPHystartDelayCwnd = NULL; - - static unsigned long long *tcpext_TCPACKSkippedSynRecv = NULL; - static unsigned long long *tcpext_TCPACKSkippedPAWS = NULL; - static unsigned long long *tcpext_TCPACKSkippedSeq = NULL; - static unsigned long long *tcpext_TCPACKSkippedFinWait2 = NULL; - static unsigned long long *tcpext_TCPACKSkippedTimeWait = NULL; - static unsigned long long *tcpext_TCPACKSkippedChallenge = NULL; - - static unsigned long long *tcpext_TCPWinProbe = NULL; - static unsigned long long *tcpext_TCPKeepAlive = NULL; - - static unsigned long long *tcpext_TCPMTUPFail = NULL; - static unsigned long long *tcpext_TCPMTUPSuccess = NULL; -*/ - - static unsigned long long *ipext_InNoRoutes = NULL; - static unsigned long long *ipext_InTruncatedPkts = NULL; - static unsigned long long *ipext_InMcastPkts = NULL; - static unsigned long long *ipext_OutMcastPkts = NULL; - static unsigned long long *ipext_InBcastPkts = NULL; - static unsigned long long *ipext_OutBcastPkts = NULL; - static unsigned long long *ipext_InOctets = NULL; - static unsigned long long *ipext_OutOctets = NULL; - static unsigned long long *ipext_InMcastOctets = NULL; - static unsigned long long *ipext_OutMcastOctets = NULL; - static unsigned long long *ipext_InBcastOctets = NULL; - static unsigned long long *ipext_OutBcastOctets = NULL; - static unsigned long long *ipext_InCsumErrors = NULL; - static unsigned long long *ipext_InNoECTPkts = NULL; - static unsigned long long *ipext_InECT1Pkts = NULL; - static unsigned long long *ipext_InECT0Pkts = NULL; - static unsigned long long *ipext_InCEPkts = NULL; - - if(unlikely(do_bandwidth == -1)) { do_bandwidth = config_get_boolean_ondemand("plugin:proc:/proc/net/netstat", "bandwidth", CONFIG_ONDEMAND_ONDEMAND); do_inerrors = config_get_boolean_ondemand("plugin:proc:/proc/net/netstat", "input errors", CONFIG_ONDEMAND_ONDEMAND); do_mcast = config_get_boolean_ondemand("plugin:proc:/proc/net/netstat", "multicast bandwidth", CONFIG_ONDEMAND_ONDEMAND); @@ -385,200 +112,124 @@ int do_proc_net_netstat(int update_every, unsigned long long dt) { do_tcpext_connaborts = config_get_boolean_ondemand("plugin:proc:/proc/net/netstat", "TCP connection aborts", CONFIG_ONDEMAND_ONDEMAND); do_tcpext_memory = config_get_boolean_ondemand("plugin:proc:/proc/net/netstat", "TCP memory pressures", CONFIG_ONDEMAND_ONDEMAND); - hash_ipext = simple_hash("IpExt"); - hash_tcpext = simple_hash("TcpExt"); + arl_ipext = arl_create("netstat/ipext", NULL, 60); + arl_tcpext = arl_create("netstat/tcpext", NULL, 60); + + // -------------------------------------------------------------------- + // IPv4 + + if(do_bandwidth != CONFIG_ONDEMAND_NO) { + arl_expect(arl_ipext, "InOctets", &ipext_InOctets); + arl_expect(arl_ipext, "OutOctets", &ipext_OutOctets); + } + + if(do_inerrors != CONFIG_ONDEMAND_NO) { + arl_expect(arl_ipext, "InNoRoutes", &ipext_InNoRoutes); + arl_expect(arl_ipext, "InTruncatedPkts", &ipext_InTruncatedPkts); + arl_expect(arl_ipext, "InCsumErrors", &ipext_InCsumErrors); + } - hash_array(ipext_data); - hash_array(tcpext_data); - - // Reordering - tcpext_TCPFACKReorder = netstat_columns_find(tcpext_data, "TCPFACKReorder"); - tcpext_TCPSACKReorder = netstat_columns_find(tcpext_data, "TCPSACKReorder"); - tcpext_TCPRenoReorder = netstat_columns_find(tcpext_data, "TCPRenoReorder"); - tcpext_TCPTSReorder = netstat_columns_find(tcpext_data, "TCPTSReorder"); - - // SYN Cookies - tcpext_SyncookiesSent = netstat_columns_find(tcpext_data, "SyncookiesSent"); - tcpext_SyncookiesRecv = netstat_columns_find(tcpext_data, "SyncookiesRecv"); - tcpext_SyncookiesFailed = netstat_columns_find(tcpext_data, "SyncookiesFailed"); - - // Out Of Order Queue - // http://www.spinics.net/lists/netdev/msg204696.html - tcpext_TCPOFOQueue = netstat_columns_find(tcpext_data, "TCPOFOQueue"); // Number of packets queued in OFO queue - tcpext_TCPOFODrop = netstat_columns_find(tcpext_data, "TCPOFODrop"); // Number of packets meant to be queued in OFO but dropped because socket rcvbuf limit hit. - tcpext_TCPOFOMerge = netstat_columns_find(tcpext_data, "TCPOFOMerge"); // Number of packets in OFO that were merged with other packets. - tcpext_OfoPruned = netstat_columns_find(tcpext_data, "OfoPruned"); // packets dropped from out-of-order queue because of socket buffer overrun - - // connection resets - // https://github.com/ecki/net-tools/blob/bd8bceaed2311651710331a7f8990c3e31be9840/statistics.c - tcpext_TCPAbortOnData = netstat_columns_find(tcpext_data, "TCPAbortOnData"); // connections reset due to unexpected data - tcpext_TCPAbortOnClose = netstat_columns_find(tcpext_data, "TCPAbortOnClose"); // connections reset due to early user close - tcpext_TCPAbortOnMemory = netstat_columns_find(tcpext_data, "TCPAbortOnMemory"); // connections aborted due to memory pressure - tcpext_TCPAbortOnTimeout = netstat_columns_find(tcpext_data, "TCPAbortOnTimeout"); // connections aborted due to timeout - tcpext_TCPAbortOnLinger = netstat_columns_find(tcpext_data, "TCPAbortOnLinger"); // connections aborted after user close in linger timeout - tcpext_TCPAbortFailed = netstat_columns_find(tcpext_data, "TCPAbortFailed"); // times unable to send RST due to no memory - - tcpext_TCPMemoryPressures = netstat_columns_find(tcpext_data, "TCPMemoryPressures"); - - /* - tcpext_EmbryonicRsts = netstat_columns_find(tcpext_data, "EmbryonicRsts"); - tcpext_PruneCalled = netstat_columns_find(tcpext_data, "PruneCalled"); - tcpext_RcvPruned = netstat_columns_find(tcpext_data, "RcvPruned"); - tcpext_OutOfWindowIcmps = netstat_columns_find(tcpext_data, "OutOfWindowIcmps"); - tcpext_LockDroppedIcmps = netstat_columns_find(tcpext_data, "LockDroppedIcmps"); - tcpext_ArpFilter = netstat_columns_find(tcpext_data, "ArpFilter"); - tcpext_TW = netstat_columns_find(tcpext_data, "TW"); - tcpext_TWRecycled = netstat_columns_find(tcpext_data, "TWRecycled"); - tcpext_TWKilled = netstat_columns_find(tcpext_data, "TWKilled"); - tcpext_PAWSPassive = netstat_columns_find(tcpext_data, "PAWSPassive"); - tcpext_PAWSActive = netstat_columns_find(tcpext_data, "PAWSActive"); - tcpext_PAWSEstab = netstat_columns_find(tcpext_data, "PAWSEstab"); - tcpext_DelayedACKs = netstat_columns_find(tcpext_data, "DelayedACKs"); - tcpext_DelayedACKLocked = netstat_columns_find(tcpext_data, "DelayedACKLocked"); - tcpext_DelayedACKLost = netstat_columns_find(tcpext_data, "DelayedACKLost"); - tcpext_ListenOverflows = netstat_columns_find(tcpext_data, "ListenOverflows"); - tcpext_ListenDrops = netstat_columns_find(tcpext_data, "ListenDrops"); - tcpext_TCPPrequeued = netstat_columns_find(tcpext_data, "TCPPrequeued"); - tcpext_TCPDirectCopyFromBacklog = netstat_columns_find(tcpext_data, "TCPDirectCopyFromBacklog"); - tcpext_TCPDirectCopyFromPrequeue = netstat_columns_find(tcpext_data, "TCPDirectCopyFromPrequeue"); - tcpext_TCPPrequeueDropped = netstat_columns_find(tcpext_data, "TCPPrequeueDropped"); - tcpext_TCPHPHits = netstat_columns_find(tcpext_data, "TCPHPHits"); - tcpext_TCPHPHitsToUser = netstat_columns_find(tcpext_data, "TCPHPHitsToUser"); - tcpext_TCPPureAcks = netstat_columns_find(tcpext_data, "TCPPureAcks"); - tcpext_TCPHPAcks = netstat_columns_find(tcpext_data, "TCPHPAcks"); - tcpext_TCPRenoRecovery = netstat_columns_find(tcpext_data, "TCPRenoRecovery"); - tcpext_TCPSackRecovery = netstat_columns_find(tcpext_data, "TCPSackRecovery"); - tcpext_TCPSACKReneging = netstat_columns_find(tcpext_data, "TCPSACKReneging"); - tcpext_TCPFullUndo = netstat_columns_find(tcpext_data, "TCPFullUndo"); - tcpext_TCPPartialUndo = netstat_columns_find(tcpext_data, "TCPPartialUndo"); - tcpext_TCPDSACKUndo = netstat_columns_find(tcpext_data, "TCPDSACKUndo"); - tcpext_TCPLossUndo = netstat_columns_find(tcpext_data, "TCPLossUndo"); - tcpext_TCPLostRetransmit = netstat_columns_find(tcpext_data, "TCPLostRetransmit"); - tcpext_TCPRenoFailures = netstat_columns_find(tcpext_data, "TCPRenoFailures"); - tcpext_TCPSackFailures = netstat_columns_find(tcpext_data, "TCPSackFailures"); - tcpext_TCPLossFailures = netstat_columns_find(tcpext_data, "TCPLossFailures"); - tcpext_TCPFastRetrans = netstat_columns_find(tcpext_data, "TCPFastRetrans"); - tcpext_TCPForwardRetrans = netstat_columns_find(tcpext_data, "TCPForwardRetrans"); - tcpext_TCPSlowStartRetrans = netstat_columns_find(tcpext_data, "TCPSlowStartRetrans"); - tcpext_TCPTimeouts = netstat_columns_find(tcpext_data, "TCPTimeouts"); - tcpext_TCPLossProbes = netstat_columns_find(tcpext_data, "TCPLossProbes"); - tcpext_TCPLossProbeRecovery = netstat_columns_find(tcpext_data, "TCPLossProbeRecovery"); - tcpext_TCPRenoRecoveryFail = netstat_columns_find(tcpext_data, "TCPRenoRecoveryFail"); - tcpext_TCPSackRecoveryFail = netstat_columns_find(tcpext_data, "TCPSackRecoveryFail"); - tcpext_TCPSchedulerFailed = netstat_columns_find(tcpext_data, "TCPSchedulerFailed"); - tcpext_TCPRcvCollapsed = netstat_columns_find(tcpext_data, "TCPRcvCollapsed"); - tcpext_TCPDSACKOldSent = netstat_columns_find(tcpext_data, "TCPDSACKOldSent"); - tcpext_TCPDSACKOfoSent = netstat_columns_find(tcpext_data, "TCPDSACKOfoSent"); - tcpext_TCPDSACKRecv = netstat_columns_find(tcpext_data, "TCPDSACKRecv"); - tcpext_TCPDSACKOfoRecv = netstat_columns_find(tcpext_data, "TCPDSACKOfoRecv"); - tcpext_TCPSACKDiscard = netstat_columns_find(tcpext_data, "TCPSACKDiscard"); - tcpext_TCPDSACKIgnoredOld = netstat_columns_find(tcpext_data, "TCPDSACKIgnoredOld"); - tcpext_TCPDSACKIgnoredNoUndo = netstat_columns_find(tcpext_data, "TCPDSACKIgnoredNoUndo"); - tcpext_TCPSpuriousRTOs = netstat_columns_find(tcpext_data, "TCPSpuriousRTOs"); - tcpext_TCPMD5NotFound = netstat_columns_find(tcpext_data, "TCPMD5NotFound"); - tcpext_TCPMD5Unexpected = netstat_columns_find(tcpext_data, "TCPMD5Unexpected"); - tcpext_TCPSackShifted = netstat_columns_find(tcpext_data, "TCPSackShifted"); - tcpext_TCPSackMerged = netstat_columns_find(tcpext_data, "TCPSackMerged"); - tcpext_TCPSackShiftFallback = netstat_columns_find(tcpext_data, "TCPSackShiftFallback"); - tcpext_TCPBacklogDrop = netstat_columns_find(tcpext_data, "TCPBacklogDrop"); - tcpext_TCPMinTTLDrop = netstat_columns_find(tcpext_data, "TCPMinTTLDrop"); - tcpext_TCPDeferAcceptDrop = netstat_columns_find(tcpext_data, "TCPDeferAcceptDrop"); - tcpext_IPReversePathFilter = netstat_columns_find(tcpext_data, "IPReversePathFilter"); - tcpext_TCPTimeWaitOverflow = netstat_columns_find(tcpext_data, "TCPTimeWaitOverflow"); - tcpext_TCPReqQFullDoCookies = netstat_columns_find(tcpext_data, "TCPReqQFullDoCookies"); - tcpext_TCPReqQFullDrop = netstat_columns_find(tcpext_data, "TCPReqQFullDrop"); - tcpext_TCPRetransFail = netstat_columns_find(tcpext_data, "TCPRetransFail"); - tcpext_TCPRcvCoalesce = netstat_columns_find(tcpext_data, "TCPRcvCoalesce"); - tcpext_TCPChallengeACK = netstat_columns_find(tcpext_data, "TCPChallengeACK"); - tcpext_TCPSYNChallenge = netstat_columns_find(tcpext_data, "TCPSYNChallenge"); - tcpext_TCPFastOpenActive = netstat_columns_find(tcpext_data, "TCPFastOpenActive"); - tcpext_TCPFastOpenActiveFail = netstat_columns_find(tcpext_data, "TCPFastOpenActiveFail"); - tcpext_TCPFastOpenPassive = netstat_columns_find(tcpext_data, "TCPFastOpenPassive"); - tcpext_TCPFastOpenPassiveFail = netstat_columns_find(tcpext_data, "TCPFastOpenPassiveFail"); - tcpext_TCPFastOpenListenOverflow = netstat_columns_find(tcpext_data, "TCPFastOpenListenOverflow"); - tcpext_TCPFastOpenCookieReqd = netstat_columns_find(tcpext_data, "TCPFastOpenCookieReqd"); - tcpext_TCPSpuriousRtxHostQueues = netstat_columns_find(tcpext_data, "TCPSpuriousRtxHostQueues"); - tcpext_BusyPollRxPackets = netstat_columns_find(tcpext_data, "BusyPollRxPackets"); - tcpext_TCPAutoCorking = netstat_columns_find(tcpext_data, "TCPAutoCorking"); - tcpext_TCPFromZeroWindowAdv = netstat_columns_find(tcpext_data, "TCPFromZeroWindowAdv"); - tcpext_TCPToZeroWindowAdv = netstat_columns_find(tcpext_data, "TCPToZeroWindowAdv"); - tcpext_TCPWantZeroWindowAdv = netstat_columns_find(tcpext_data, "TCPWantZeroWindowAdv"); - tcpext_TCPSynRetrans = netstat_columns_find(tcpext_data, "TCPSynRetrans"); - tcpext_TCPOrigDataSent = netstat_columns_find(tcpext_data, "TCPOrigDataSent"); - tcpext_TCPHystartTrainDetect = netstat_columns_find(tcpext_data, "TCPHystartTrainDetect"); - tcpext_TCPHystartTrainCwnd = netstat_columns_find(tcpext_data, "TCPHystartTrainCwnd"); - tcpext_TCPHystartDelayDetect = netstat_columns_find(tcpext_data, "TCPHystartDelayDetect"); - tcpext_TCPHystartDelayCwnd = netstat_columns_find(tcpext_data, "TCPHystartDelayCwnd"); - tcpext_TCPACKSkippedSynRecv = netstat_columns_find(tcpext_data, "TCPACKSkippedSynRecv"); - tcpext_TCPACKSkippedPAWS = netstat_columns_find(tcpext_data, "TCPACKSkippedPAWS"); - tcpext_TCPACKSkippedSeq = netstat_columns_find(tcpext_data, "TCPACKSkippedSeq"); - tcpext_TCPACKSkippedFinWait2 = netstat_columns_find(tcpext_data, "TCPACKSkippedFinWait2"); - tcpext_TCPACKSkippedTimeWait = netstat_columns_find(tcpext_data, "TCPACKSkippedTimeWait"); - tcpext_TCPACKSkippedChallenge = netstat_columns_find(tcpext_data, "TCPACKSkippedChallenge"); - tcpext_TCPWinProbe = netstat_columns_find(tcpext_data, "TCPWinProbe"); - tcpext_TCPKeepAlive = netstat_columns_find(tcpext_data, "TCPKeepAlive"); - tcpext_TCPMTUPFail = netstat_columns_find(tcpext_data, "TCPMTUPFail"); - tcpext_TCPMTUPSuccess = netstat_columns_find(tcpext_data, "TCPMTUPSuccess"); -*/ - ipext_InNoRoutes = netstat_columns_find(ipext_data, "InNoRoutes"); - ipext_InTruncatedPkts = netstat_columns_find(ipext_data, "InTruncatedPkts"); - ipext_InMcastPkts = netstat_columns_find(ipext_data, "InMcastPkts"); - ipext_OutMcastPkts = netstat_columns_find(ipext_data, "OutMcastPkts"); - ipext_InBcastPkts = netstat_columns_find(ipext_data, "InBcastPkts"); - ipext_OutBcastPkts = netstat_columns_find(ipext_data, "OutBcastPkts"); - ipext_InOctets = netstat_columns_find(ipext_data, "InOctets"); - ipext_OutOctets = netstat_columns_find(ipext_data, "OutOctets"); - ipext_InMcastOctets = netstat_columns_find(ipext_data, "InMcastOctets"); - ipext_OutMcastOctets = netstat_columns_find(ipext_data, "OutMcastOctets"); - ipext_InBcastOctets = netstat_columns_find(ipext_data, "InBcastOctets"); - ipext_OutBcastOctets = netstat_columns_find(ipext_data, "OutBcastOctets"); - ipext_InCsumErrors = netstat_columns_find(ipext_data, "InCsumErrors"); - ipext_InNoECTPkts = netstat_columns_find(ipext_data, "InNoECTPkts"); - ipext_InECT1Pkts = netstat_columns_find(ipext_data, "InECT1Pkts"); - ipext_InECT0Pkts = netstat_columns_find(ipext_data, "InECT0Pkts"); - ipext_InCEPkts = netstat_columns_find(ipext_data, "InCEPkts"); + if(do_mcast != CONFIG_ONDEMAND_NO) { + arl_expect(arl_ipext, "InMcastOctets", &ipext_InMcastOctets); + arl_expect(arl_ipext, "OutMcastOctets", &ipext_OutMcastOctets); + } + + if(do_mcast_p != CONFIG_ONDEMAND_NO) { + arl_expect(arl_ipext, "InMcastPkts", &ipext_InMcastPkts); + arl_expect(arl_ipext, "OutMcastPkts", &ipext_OutMcastPkts); + } + + if(do_bcast != CONFIG_ONDEMAND_NO) { + arl_expect(arl_ipext, "InBcastPkts", &ipext_InBcastPkts); + arl_expect(arl_ipext, "OutBcastPkts", &ipext_OutBcastPkts); + } + + if(do_bcast_p != CONFIG_ONDEMAND_NO) { + arl_expect(arl_ipext, "InBcastOctets", &ipext_InBcastOctets); + arl_expect(arl_ipext, "OutBcastOctets", &ipext_OutBcastOctets); + } + + if(do_ecn != CONFIG_ONDEMAND_NO) { + arl_expect(arl_ipext, "InNoECTPkts", &ipext_InNoECTPkts); + arl_expect(arl_ipext, "InECT1Pkts", &ipext_InECT1Pkts); + arl_expect(arl_ipext, "InECT0Pkts", &ipext_InECT0Pkts); + arl_expect(arl_ipext, "InCEPkts", &ipext_InCEPkts); + } + + // -------------------------------------------------------------------- + // IPv4 TCP + + if(do_tcpext_reorder != CONFIG_ONDEMAND_NO) { + arl_expect(arl_tcpext, "TCPFACKReorder", &tcpext_TCPFACKReorder); + arl_expect(arl_tcpext, "TCPSACKReorder", &tcpext_TCPSACKReorder); + arl_expect(arl_tcpext, "TCPRenoReorder", &tcpext_TCPRenoReorder); + arl_expect(arl_tcpext, "TCPTSReorder", &tcpext_TCPTSReorder); + } + + if(do_tcpext_syscookies != CONFIG_ONDEMAND_NO) { + arl_expect(arl_tcpext, "SyncookiesSent", &tcpext_SyncookiesSent); + arl_expect(arl_tcpext, "SyncookiesRecv", &tcpext_SyncookiesRecv); + arl_expect(arl_tcpext, "SyncookiesFailed", &tcpext_SyncookiesFailed); + } + + if(do_tcpext_ofo != CONFIG_ONDEMAND_NO) { + arl_expect(arl_tcpext, "TCPOFOQueue", &tcpext_TCPOFOQueue); + arl_expect(arl_tcpext, "TCPOFODrop", &tcpext_TCPOFODrop); + arl_expect(arl_tcpext, "TCPOFOMerge", &tcpext_TCPOFOMerge); + arl_expect(arl_tcpext, "OfoPruned", &tcpext_OfoPruned); + } + + if(do_tcpext_connaborts != CONFIG_ONDEMAND_NO) { + arl_expect(arl_tcpext, "TCPAbortOnData", &tcpext_TCPAbortOnData); + arl_expect(arl_tcpext, "TCPAbortOnClose", &tcpext_TCPAbortOnClose); + arl_expect(arl_tcpext, "TCPAbortOnMemory", &tcpext_TCPAbortOnMemory); + arl_expect(arl_tcpext, "TCPAbortOnTimeout", &tcpext_TCPAbortOnTimeout); + arl_expect(arl_tcpext, "TCPAbortOnLinger", &tcpext_TCPAbortOnLinger); + arl_expect(arl_tcpext, "TCPAbortFailed", &tcpext_TCPAbortFailed); + } + + if(do_tcpext_memory != CONFIG_ONDEMAND_NO) { + arl_expect(arl_tcpext, "TCPMemoryPressures", &tcpext_TCPMemoryPressures); + } } if(unlikely(!ff)) { char filename[FILENAME_MAX + 1]; snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/net/netstat"); ff = procfile_open(config_get("plugin:proc:/proc/net/netstat", "filename to monitor", filename), " \t:", PROCFILE_FLAG_DEFAULT); + if(unlikely(!ff)) return 1; } - if(unlikely(!ff)) return 1; ff = procfile_readall(ff); if(unlikely(!ff)) return 0; // we return 0, so that we will retry to open it next time - uint32_t lines = procfile_lines(ff), l; - uint32_t words; + size_t lines = procfile_lines(ff), l; + size_t words; for(l = 0; l < lines ;l++) { char *key = procfile_lineword(ff, l, 0); uint32_t hash = simple_hash(key); if(unlikely(hash == hash_ipext && strcmp(key, "IpExt") == 0)) { - uint32_t h = l++; + size_t h = l++; - if(strcmp(procfile_lineword(ff, l, 0), "IpExt") != 0) { - error("Cannot read IpExt line from /proc/net/netstat."); - break; - } words = procfile_linewords(ff, l); - if(words < 2) { - error("Cannot read /proc/net/netstat IpExt line. Expected 2+ params, read %u.", words); + if(unlikely(words < 2)) { + error("Cannot read /proc/net/netstat IpExt line. Expected 2+ params, read %zu.", words); continue; } - parse_line_pair(ff, ipext_data, h, l); + arl_begin(arl_ipext); + parse_line_pair(ff, arl_ipext, h, l); RRDSET *st; // -------------------------------------------------------------------- - if(do_bandwidth == CONFIG_ONDEMAND_YES || (do_bandwidth == CONFIG_ONDEMAND_ONDEMAND && (*ipext_InOctets || *ipext_OutOctets))) { + if(do_bandwidth == CONFIG_ONDEMAND_YES || (do_bandwidth == CONFIG_ONDEMAND_ONDEMAND && (ipext_InOctets || ipext_OutOctets))) { do_bandwidth = CONFIG_ONDEMAND_YES; st = rrdset_find("system.ipv4"); - if(!st) { + if(unlikely(!st)) { st = rrdset_create("system", "ipv4", NULL, "network", NULL, "IPv4 Bandwidth", "kilobits/s", 500, update_every, RRDSET_TYPE_AREA); rrddim_add(st, "InOctets", "received", 8, 1024, RRDDIM_INCREMENTAL); @@ -586,17 +237,17 @@ int do_proc_net_netstat(int update_every, unsigned long long dt) { } else rrdset_next(st); - rrddim_set(st, "InOctets", *ipext_InOctets); - rrddim_set(st, "OutOctets", *ipext_OutOctets); + rrddim_set(st, "InOctets", ipext_InOctets); + rrddim_set(st, "OutOctets", ipext_OutOctets); rrdset_done(st); } // -------------------------------------------------------------------- - if(do_inerrors == CONFIG_ONDEMAND_YES || (do_inerrors == CONFIG_ONDEMAND_ONDEMAND && (*ipext_InNoRoutes || *ipext_InTruncatedPkts))) { + if(do_inerrors == CONFIG_ONDEMAND_YES || (do_inerrors == CONFIG_ONDEMAND_ONDEMAND && (ipext_InNoRoutes || ipext_InTruncatedPkts))) { do_inerrors = CONFIG_ONDEMAND_YES; st = rrdset_find("ipv4.inerrors"); - if(!st) { + if(unlikely(!st)) { st = rrdset_create("ipv4", "inerrors", NULL, "errors", NULL, "IPv4 Input Errors", "packets/s", 4000, update_every, RRDSET_TYPE_LINE); st->isdetail = 1; @@ -606,18 +257,18 @@ int do_proc_net_netstat(int update_every, unsigned long long dt) { } else rrdset_next(st); - rrddim_set(st, "InNoRoutes", *ipext_InNoRoutes); - rrddim_set(st, "InTruncatedPkts", *ipext_InTruncatedPkts); - rrddim_set(st, "InCsumErrors", *ipext_InCsumErrors); + rrddim_set(st, "InNoRoutes", ipext_InNoRoutes); + rrddim_set(st, "InTruncatedPkts", ipext_InTruncatedPkts); + rrddim_set(st, "InCsumErrors", ipext_InCsumErrors); rrdset_done(st); } // -------------------------------------------------------------------- - if(do_mcast == CONFIG_ONDEMAND_YES || (do_mcast == CONFIG_ONDEMAND_ONDEMAND && (*ipext_InMcastOctets || *ipext_OutMcastOctets))) { + if(do_mcast == CONFIG_ONDEMAND_YES || (do_mcast == CONFIG_ONDEMAND_ONDEMAND && (ipext_InMcastOctets || ipext_OutMcastOctets))) { do_mcast = CONFIG_ONDEMAND_YES; st = rrdset_find("ipv4.mcast"); - if(!st) { + if(unlikely(!st)) { st = rrdset_create("ipv4", "mcast", NULL, "multicast", NULL, "IPv4 Multicast Bandwidth", "kilobits/s", 9000, update_every, RRDSET_TYPE_AREA); st->isdetail = 1; @@ -626,17 +277,17 @@ int do_proc_net_netstat(int update_every, unsigned long long dt) { } else rrdset_next(st); - rrddim_set(st, "InMcastOctets", *ipext_InMcastOctets); - rrddim_set(st, "OutMcastOctets", *ipext_OutMcastOctets); + rrddim_set(st, "InMcastOctets", ipext_InMcastOctets); + rrddim_set(st, "OutMcastOctets", ipext_OutMcastOctets); rrdset_done(st); } // -------------------------------------------------------------------- - if(do_bcast == CONFIG_ONDEMAND_YES || (do_bcast == CONFIG_ONDEMAND_ONDEMAND && (*ipext_InBcastOctets || *ipext_OutBcastOctets))) { + if(do_bcast == CONFIG_ONDEMAND_YES || (do_bcast == CONFIG_ONDEMAND_ONDEMAND && (ipext_InBcastOctets || ipext_OutBcastOctets))) { do_bcast = CONFIG_ONDEMAND_YES; st = rrdset_find("ipv4.bcast"); - if(!st) { + if(unlikely(!st)) { st = rrdset_create("ipv4", "bcast", NULL, "broadcast", NULL, "IPv4 Broadcast Bandwidth", "kilobits/s", 8000, update_every, RRDSET_TYPE_AREA); st->isdetail = 1; @@ -645,17 +296,17 @@ int do_proc_net_netstat(int update_every, unsigned long long dt) { } else rrdset_next(st); - rrddim_set(st, "InBcastOctets", *ipext_InBcastOctets); - rrddim_set(st, "OutBcastOctets", *ipext_OutBcastOctets); + rrddim_set(st, "InBcastOctets", ipext_InBcastOctets); + rrddim_set(st, "OutBcastOctets", ipext_OutBcastOctets); rrdset_done(st); } // -------------------------------------------------------------------- - if(do_mcast_p == CONFIG_ONDEMAND_YES || (do_mcast_p == CONFIG_ONDEMAND_ONDEMAND && (*ipext_InMcastPkts || *ipext_OutMcastPkts))) { + if(do_mcast_p == CONFIG_ONDEMAND_YES || (do_mcast_p == CONFIG_ONDEMAND_ONDEMAND && (ipext_InMcastPkts || ipext_OutMcastPkts))) { do_mcast_p = CONFIG_ONDEMAND_YES; st = rrdset_find("ipv4.mcastpkts"); - if(!st) { + if(unlikely(!st)) { st = rrdset_create("ipv4", "mcastpkts", NULL, "multicast", NULL, "IPv4 Multicast Packets", "packets/s", 8600, update_every, RRDSET_TYPE_LINE); st->isdetail = 1; @@ -664,17 +315,17 @@ int do_proc_net_netstat(int update_every, unsigned long long dt) { } else rrdset_next(st); - rrddim_set(st, "InMcastPkts", *ipext_InMcastPkts); - rrddim_set(st, "OutMcastPkts", *ipext_OutMcastPkts); + rrddim_set(st, "InMcastPkts", ipext_InMcastPkts); + rrddim_set(st, "OutMcastPkts", ipext_OutMcastPkts); rrdset_done(st); } // -------------------------------------------------------------------- - if(do_bcast_p == CONFIG_ONDEMAND_YES || (do_bcast_p == CONFIG_ONDEMAND_ONDEMAND && (*ipext_InBcastPkts || *ipext_OutBcastPkts))) { + if(do_bcast_p == CONFIG_ONDEMAND_YES || (do_bcast_p == CONFIG_ONDEMAND_ONDEMAND && (ipext_InBcastPkts || ipext_OutBcastPkts))) { do_bcast_p = CONFIG_ONDEMAND_YES; st = rrdset_find("ipv4.bcastpkts"); - if(!st) { + if(unlikely(!st)) { st = rrdset_create("ipv4", "bcastpkts", NULL, "broadcast", NULL, "IPv4 Broadcast Packets", "packets/s", 8500, update_every, RRDSET_TYPE_LINE); st->isdetail = 1; @@ -683,17 +334,17 @@ int do_proc_net_netstat(int update_every, unsigned long long dt) { } else rrdset_next(st); - rrddim_set(st, "InBcastPkts", *ipext_InBcastPkts); - rrddim_set(st, "OutBcastPkts", *ipext_OutBcastPkts); + rrddim_set(st, "InBcastPkts", ipext_InBcastPkts); + rrddim_set(st, "OutBcastPkts", ipext_OutBcastPkts); rrdset_done(st); } // -------------------------------------------------------------------- - if(do_ecn == CONFIG_ONDEMAND_YES || (do_ecn == CONFIG_ONDEMAND_ONDEMAND && (*ipext_InCEPkts || *ipext_InECT0Pkts || *ipext_InECT1Pkts || *ipext_InNoECTPkts))) { + if(do_ecn == CONFIG_ONDEMAND_YES || (do_ecn == CONFIG_ONDEMAND_ONDEMAND && (ipext_InCEPkts || ipext_InECT0Pkts || ipext_InECT1Pkts || ipext_InNoECTPkts))) { do_ecn = CONFIG_ONDEMAND_YES; st = rrdset_find("ipv4.ecnpkts"); - if(!st) { + if(unlikely(!st)) { st = rrdset_create("ipv4", "ecnpkts", NULL, "ecn", NULL, "IPv4 ECN Statistics", "packets/s", 8700, update_every, RRDSET_TYPE_LINE); st->isdetail = 1; @@ -704,52 +355,49 @@ int do_proc_net_netstat(int update_every, unsigned long long dt) { } else rrdset_next(st); - rrddim_set(st, "InCEPkts", *ipext_InCEPkts); - rrddim_set(st, "InNoECTPkts", *ipext_InNoECTPkts); - rrddim_set(st, "InECT0Pkts", *ipext_InECT0Pkts); - rrddim_set(st, "InECT1Pkts", *ipext_InECT1Pkts); + rrddim_set(st, "InCEPkts", ipext_InCEPkts); + rrddim_set(st, "InNoECTPkts", ipext_InNoECTPkts); + rrddim_set(st, "InECT0Pkts", ipext_InECT0Pkts); + rrddim_set(st, "InECT1Pkts", ipext_InECT1Pkts); rrdset_done(st); } } else if(unlikely(hash == hash_tcpext && strcmp(key, "TcpExt") == 0)) { - uint32_t h = l++; + size_t h = l++; - if(strcmp(procfile_lineword(ff, l, 0), "TcpExt") != 0) { - error("Cannot read TcpExt line from /proc/net/netstat."); - break; - } words = procfile_linewords(ff, l); - if(words < 2) { - error("Cannot read /proc/net/netstat TcpExt line. Expected 2+ params, read %u.", words); + if(unlikely(words < 2)) { + error("Cannot read /proc/net/netstat TcpExt line. Expected 2+ params, read %zu.", words); continue; } - parse_line_pair(ff, tcpext_data, h, l); + arl_begin(arl_tcpext); + parse_line_pair(ff, arl_tcpext, h, l); RRDSET *st; // -------------------------------------------------------------------- - if(do_tcpext_memory == CONFIG_ONDEMAND_YES || (do_tcpext_memory == CONFIG_ONDEMAND_ONDEMAND && (*tcpext_TCPMemoryPressures))) { + if(do_tcpext_memory == CONFIG_ONDEMAND_YES || (do_tcpext_memory == CONFIG_ONDEMAND_ONDEMAND && (tcpext_TCPMemoryPressures))) { do_tcpext_memory = CONFIG_ONDEMAND_YES; st = rrdset_find("ipv4.tcpmemorypressures"); - if(!st) { + if(unlikely(!st)) { st = rrdset_create("ipv4", "tcpmemorypressures", NULL, "tcp", NULL, "TCP Memory Pressures", "events/s", 3000, update_every, RRDSET_TYPE_LINE); rrddim_add(st, "TCPMemoryPressures", "pressures", 1, 1, RRDDIM_INCREMENTAL); } else rrdset_next(st); - rrddim_set(st, "TCPMemoryPressures", *tcpext_TCPMemoryPressures); + rrddim_set(st, "TCPMemoryPressures", tcpext_TCPMemoryPressures); rrdset_done(st); } // -------------------------------------------------------------------- - if(do_tcpext_connaborts == CONFIG_ONDEMAND_YES || (do_tcpext_connaborts == CONFIG_ONDEMAND_ONDEMAND && (*tcpext_TCPAbortOnData || *tcpext_TCPAbortOnClose || *tcpext_TCPAbortOnMemory || *tcpext_TCPAbortOnTimeout || *tcpext_TCPAbortOnLinger || *tcpext_TCPAbortFailed))) { + if(do_tcpext_connaborts == CONFIG_ONDEMAND_YES || (do_tcpext_connaborts == CONFIG_ONDEMAND_ONDEMAND && (tcpext_TCPAbortOnData || tcpext_TCPAbortOnClose || tcpext_TCPAbortOnMemory || tcpext_TCPAbortOnTimeout || tcpext_TCPAbortOnLinger || tcpext_TCPAbortFailed))) { do_tcpext_connaborts = CONFIG_ONDEMAND_YES; st = rrdset_find("ipv4.tcpconnaborts"); - if(!st) { + if(unlikely(!st)) { st = rrdset_create("ipv4", "tcpconnaborts", NULL, "tcp", NULL, "TCP Connection Aborts", "connections/s", 3010, update_every, RRDSET_TYPE_LINE); rrddim_add(st, "TCPAbortOnData", "baddata", 1, 1, RRDDIM_INCREMENTAL); @@ -761,20 +409,20 @@ int do_proc_net_netstat(int update_every, unsigned long long dt) { } else rrdset_next(st); - rrddim_set(st, "TCPAbortOnData", *tcpext_TCPAbortOnData); - rrddim_set(st, "TCPAbortOnClose", *tcpext_TCPAbortOnClose); - rrddim_set(st, "TCPAbortOnMemory", *tcpext_TCPAbortOnMemory); - rrddim_set(st, "TCPAbortOnTimeout", *tcpext_TCPAbortOnTimeout); - rrddim_set(st, "TCPAbortOnLinger", *tcpext_TCPAbortOnLinger); - rrddim_set(st, "TCPAbortFailed", *tcpext_TCPAbortFailed); + rrddim_set(st, "TCPAbortOnData", tcpext_TCPAbortOnData); + rrddim_set(st, "TCPAbortOnClose", tcpext_TCPAbortOnClose); + rrddim_set(st, "TCPAbortOnMemory", tcpext_TCPAbortOnMemory); + rrddim_set(st, "TCPAbortOnTimeout", tcpext_TCPAbortOnTimeout); + rrddim_set(st, "TCPAbortOnLinger", tcpext_TCPAbortOnLinger); + rrddim_set(st, "TCPAbortFailed", tcpext_TCPAbortFailed); rrdset_done(st); } // -------------------------------------------------------------------- - if(do_tcpext_reorder == CONFIG_ONDEMAND_YES || (do_tcpext_reorder == CONFIG_ONDEMAND_ONDEMAND && (*tcpext_TCPRenoReorder || *tcpext_TCPFACKReorder || *tcpext_TCPSACKReorder || *tcpext_TCPTSReorder))) { + if(do_tcpext_reorder == CONFIG_ONDEMAND_YES || (do_tcpext_reorder == CONFIG_ONDEMAND_ONDEMAND && (tcpext_TCPRenoReorder || tcpext_TCPFACKReorder || tcpext_TCPSACKReorder || tcpext_TCPTSReorder))) { do_tcpext_reorder = CONFIG_ONDEMAND_YES; st = rrdset_find("ipv4.tcpreorders"); - if(!st) { + if(unlikely(!st)) { st = rrdset_create("ipv4", "tcpreorders", NULL, "tcp", NULL, "TCP Reordered Packets by Detection Method", "packets/s", 3020, update_every, RRDSET_TYPE_LINE); rrddim_add(st, "TCPTSReorder", "timestamp", 1, 1, RRDDIM_INCREMENTAL); @@ -784,19 +432,19 @@ int do_proc_net_netstat(int update_every, unsigned long long dt) { } else rrdset_next(st); - rrddim_set(st, "TCPTSReorder", *tcpext_TCPTSReorder); - rrddim_set(st, "TCPSACKReorder", *tcpext_TCPSACKReorder); - rrddim_set(st, "TCPFACKReorder", *tcpext_TCPFACKReorder); - rrddim_set(st, "TCPRenoReorder", *tcpext_TCPRenoReorder); + rrddim_set(st, "TCPTSReorder", tcpext_TCPTSReorder); + rrddim_set(st, "TCPSACKReorder", tcpext_TCPSACKReorder); + rrddim_set(st, "TCPFACKReorder", tcpext_TCPFACKReorder); + rrddim_set(st, "TCPRenoReorder", tcpext_TCPRenoReorder); rrdset_done(st); } // -------------------------------------------------------------------- - if(do_tcpext_ofo == CONFIG_ONDEMAND_YES || (do_tcpext_ofo == CONFIG_ONDEMAND_ONDEMAND && (*tcpext_TCPOFOQueue || *tcpext_TCPOFODrop || *tcpext_TCPOFOMerge))) { + if(do_tcpext_ofo == CONFIG_ONDEMAND_YES || (do_tcpext_ofo == CONFIG_ONDEMAND_ONDEMAND && (tcpext_TCPOFOQueue || tcpext_TCPOFODrop || tcpext_TCPOFOMerge))) { do_tcpext_ofo = CONFIG_ONDEMAND_YES; st = rrdset_find("ipv4.tcpofo"); - if(!st) { + if(unlikely(!st)) { st = rrdset_create("ipv4", "tcpofo", NULL, "tcp", NULL, "TCP Out-Of-Order Queue", "packets/s", 3050, update_every, RRDSET_TYPE_LINE); rrddim_add(st, "TCPOFOQueue", "inqueue", 1, 1, RRDDIM_INCREMENTAL); @@ -806,19 +454,19 @@ int do_proc_net_netstat(int update_every, unsigned long long dt) { } else rrdset_next(st); - rrddim_set(st, "TCPOFOQueue", *tcpext_TCPOFOQueue); - rrddim_set(st, "TCPOFODrop", *tcpext_TCPOFODrop); - rrddim_set(st, "TCPOFOMerge", *tcpext_TCPOFOMerge); - rrddim_set(st, "OfoPruned", *tcpext_OfoPruned); + rrddim_set(st, "TCPOFOQueue", tcpext_TCPOFOQueue); + rrddim_set(st, "TCPOFODrop", tcpext_TCPOFODrop); + rrddim_set(st, "TCPOFOMerge", tcpext_TCPOFOMerge); + rrddim_set(st, "OfoPruned", tcpext_OfoPruned); rrdset_done(st); } // -------------------------------------------------------------------- - if(do_tcpext_syscookies == CONFIG_ONDEMAND_YES || (do_tcpext_syscookies == CONFIG_ONDEMAND_ONDEMAND && (*tcpext_SyncookiesSent || *tcpext_SyncookiesRecv || *tcpext_SyncookiesFailed))) { + if(do_tcpext_syscookies == CONFIG_ONDEMAND_YES || (do_tcpext_syscookies == CONFIG_ONDEMAND_ONDEMAND && (tcpext_SyncookiesSent || tcpext_SyncookiesRecv || tcpext_SyncookiesFailed))) { do_tcpext_syscookies = CONFIG_ONDEMAND_YES; st = rrdset_find("ipv4.tcpsyncookies"); - if(!st) { + if(unlikely(!st)) { st = rrdset_create("ipv4", "tcpsyncookies", NULL, "tcp", NULL, "TCP SYN Cookies", "packets/s", 3100, update_every, RRDSET_TYPE_LINE); rrddim_add(st, "SyncookiesRecv", "received", 1, 1, RRDDIM_INCREMENTAL); @@ -827,9 +475,9 @@ int do_proc_net_netstat(int update_every, unsigned long long dt) { } else rrdset_next(st); - rrddim_set(st, "SyncookiesRecv", *tcpext_SyncookiesRecv); - rrddim_set(st, "SyncookiesSent", *tcpext_SyncookiesSent); - rrddim_set(st, "SyncookiesFailed", *tcpext_SyncookiesFailed); + rrddim_set(st, "SyncookiesRecv", tcpext_SyncookiesRecv); + rrddim_set(st, "SyncookiesSent", tcpext_SyncookiesSent); + rrddim_set(st, "SyncookiesFailed", tcpext_SyncookiesFailed); rrdset_done(st); } diff --git a/src/proc_net_rpc_nfs.c b/src/proc_net_rpc_nfs.c index 98acdd814..9dba08d56 100644 --- a/src/proc_net_rpc_nfs.c +++ b/src/proc_net_rpc_nfs.c @@ -127,7 +127,7 @@ struct nfs_procs nfs_proc4_values[] = { { "", 0ULL, 0 } }; -int do_proc_net_rpc_nfs(int update_every, unsigned long long dt) { +int do_proc_net_rpc_nfs(int update_every, usec_t dt) { (void)dt; static procfile *ff = NULL; @@ -151,36 +151,35 @@ int do_proc_net_rpc_nfs(int update_every, unsigned long long dt) { if(do_proc4 == -1) do_proc4 = config_get_boolean("plugin:proc:/proc/net/rpc/nfs", "NFS v4 procedures", 1); // if they are enabled, reset them to 1 - // later we do them =2 to avoid doing strcmp for all lines + // later we do them =2 to avoid doing strcmp() for all lines if(do_net) do_net = 1; if(do_rpc) do_rpc = 1; if(do_proc2) do_proc2 = 1; if(do_proc3) do_proc3 = 1; if(do_proc4) do_proc4 = 1; - uint32_t lines = procfile_lines(ff), l; - uint32_t words; + size_t lines = procfile_lines(ff), l; char *type; unsigned long long net_count = 0, net_udp_count = 0, net_tcp_count = 0, net_tcp_connections = 0; unsigned long long rpc_calls = 0, rpc_retransmits = 0, rpc_auth_refresh = 0; for(l = 0; l < lines ;l++) { - words = procfile_linewords(ff, l); + size_t words = procfile_linewords(ff, l); if(!words) continue; type = procfile_lineword(ff, l, 0); if(do_net == 1 && strcmp(type, "net") == 0) { if(words < 5) { - error("%s line of /proc/net/rpc/nfs has %u words, expected %d", type, words, 5); + error("%s line of /proc/net/rpc/nfs has %zu words, expected %d", type, words, 5); continue; } - net_count = strtoull(procfile_lineword(ff, l, 1), NULL, 10); - net_udp_count = strtoull(procfile_lineword(ff, l, 2), NULL, 10); - net_tcp_count = strtoull(procfile_lineword(ff, l, 3), NULL, 10); - net_tcp_connections = strtoull(procfile_lineword(ff, l, 4), NULL, 10); + net_count = str2ull(procfile_lineword(ff, l, 1)); + net_udp_count = str2ull(procfile_lineword(ff, l, 2)); + net_tcp_count = str2ull(procfile_lineword(ff, l, 3)); + net_tcp_connections = str2ull(procfile_lineword(ff, l, 4)); unsigned long long sum = net_count + net_udp_count + net_tcp_count + net_tcp_connections; if(sum == 0ULL) do_net = -1; @@ -188,13 +187,13 @@ int do_proc_net_rpc_nfs(int update_every, unsigned long long dt) { } else if(do_rpc == 1 && strcmp(type, "rpc") == 0) { if(words < 4) { - error("%s line of /proc/net/rpc/nfs has %u words, expected %d", type, words, 6); + error("%s line of /proc/net/rpc/nfs has %zu words, expected %d", type, words, 6); continue; } - rpc_calls = strtoull(procfile_lineword(ff, l, 1), NULL, 10); - rpc_retransmits = strtoull(procfile_lineword(ff, l, 2), NULL, 10); - rpc_auth_refresh = strtoull(procfile_lineword(ff, l, 3), NULL, 10); + rpc_calls = str2ull(procfile_lineword(ff, l, 1)); + rpc_retransmits = str2ull(procfile_lineword(ff, l, 2)); + rpc_auth_refresh = str2ull(procfile_lineword(ff, l, 3)); unsigned long long sum = rpc_calls + rpc_retransmits + rpc_auth_refresh; if(sum == 0ULL) do_rpc = -1; @@ -207,7 +206,7 @@ int do_proc_net_rpc_nfs(int update_every, unsigned long long dt) { unsigned long long sum = 0; unsigned int i, j; for(i = 0, j = 2; j < words && nfs_proc2_values[i].name[0] ; i++, j++) { - nfs_proc2_values[i].value = strtoull(procfile_lineword(ff, l, j), NULL, 10); + nfs_proc2_values[i].value = str2ull(procfile_lineword(ff, l, j)); nfs_proc2_values[i].present = 1; sum += nfs_proc2_values[i].value; } @@ -228,7 +227,7 @@ int do_proc_net_rpc_nfs(int update_every, unsigned long long dt) { unsigned long long sum = 0; unsigned int i, j; for(i = 0, j = 2; j < words && nfs_proc3_values[i].name[0] ; i++, j++) { - nfs_proc3_values[i].value = strtoull(procfile_lineword(ff, l, j), NULL, 10); + nfs_proc3_values[i].value = str2ull(procfile_lineword(ff, l, j)); nfs_proc3_values[i].present = 1; sum += nfs_proc3_values[i].value; } @@ -249,7 +248,7 @@ int do_proc_net_rpc_nfs(int update_every, unsigned long long dt) { unsigned long long sum = 0; unsigned int i, j; for(i = 0, j = 2; j < words && nfs_proc4_values[i].name[0] ; i++, j++) { - nfs_proc4_values[i].value = strtoull(procfile_lineword(ff, l, j), NULL, 10); + nfs_proc4_values[i].value = str2ull(procfile_lineword(ff, l, j)); nfs_proc4_values[i].present = 1; sum += nfs_proc4_values[i].value; } diff --git a/src/proc_net_rpc_nfsd.c b/src/proc_net_rpc_nfsd.c index 817e6c86a..02a8c8f90 100644 --- a/src/proc_net_rpc_nfsd.c +++ b/src/proc_net_rpc_nfsd.c @@ -209,7 +209,7 @@ struct nfsd_procs nfsd4_ops_values[] = { }; -int do_proc_net_rpc_nfsd(int update_every, unsigned long long dt) { +int do_proc_net_rpc_nfsd(int update_every, usec_t dt) { static procfile *ff = NULL; static int do_rc = -1, do_fh = -1, do_io = -1, do_th = -1, do_ra = -1, do_net = -1, do_rpc = -1, do_proc2 = -1, do_proc3 = -1, do_proc4 = -1, do_proc4ops = -1; static int ra_warning = 0, th_warning = 0, proc2_warning = 0, proc3_warning = 0, proc4_warning = 0, proc4ops_warning = 0; @@ -239,7 +239,7 @@ int do_proc_net_rpc_nfsd(int update_every, unsigned long long dt) { if(do_proc4ops == -1) do_proc4ops = config_get_boolean("plugin:proc:/proc/net/rpc/nfsd", "NFS v4 operations", 1); // if they are enabled, reset them to 1 - // later we do them =2 to avoid doing strcmp for all lines + // later we do them =2 to avoid doing strcmp() for all lines if(do_rc) do_rc = 1; if(do_fh) do_fh = 1; if(do_io) do_io = 1; @@ -252,8 +252,7 @@ int do_proc_net_rpc_nfsd(int update_every, unsigned long long dt) { if(do_proc4) do_proc4 = 1; if(do_proc4ops) do_proc4ops = 1; - uint32_t lines = procfile_lines(ff), l; - uint32_t words; + size_t lines = procfile_lines(ff), l; char *type; unsigned long long rc_hits = 0, rc_misses = 0, rc_nocache = 0; @@ -265,20 +264,20 @@ int do_proc_net_rpc_nfsd(int update_every, unsigned long long dt) { unsigned long long rpc_calls = 0, rpc_bad_format = 0, rpc_bad_auth = 0, rpc_bad_client = 0; for(l = 0; l < lines ;l++) { - words = procfile_linewords(ff, l); + size_t words = procfile_linewords(ff, l); if(!words) continue; type = procfile_lineword(ff, l, 0); if(do_rc == 1 && strcmp(type, "rc") == 0) { if(words < 4) { - error("%s line of /proc/net/rpc/nfsd has %u words, expected %d", type, words, 4); + error("%s line of /proc/net/rpc/nfsd has %zu words, expected %d", type, words, 4); continue; } - rc_hits = strtoull(procfile_lineword(ff, l, 1), NULL, 10); - rc_misses = strtoull(procfile_lineword(ff, l, 2), NULL, 10); - rc_nocache = strtoull(procfile_lineword(ff, l, 3), NULL, 10); + rc_hits = str2ull(procfile_lineword(ff, l, 1)); + rc_misses = str2ull(procfile_lineword(ff, l, 2)); + rc_nocache = str2ull(procfile_lineword(ff, l, 3)); unsigned long long sum = rc_hits + rc_misses + rc_nocache; if(sum == 0ULL) do_rc = -1; @@ -286,15 +285,15 @@ int do_proc_net_rpc_nfsd(int update_every, unsigned long long dt) { } else if(do_fh == 1 && strcmp(type, "fh") == 0) { if(words < 6) { - error("%s line of /proc/net/rpc/nfsd has %u words, expected %d", type, words, 6); + error("%s line of /proc/net/rpc/nfsd has %zu words, expected %d", type, words, 6); continue; } - fh_stale = strtoull(procfile_lineword(ff, l, 1), NULL, 10); - fh_total_lookups = strtoull(procfile_lineword(ff, l, 2), NULL, 10); - fh_anonymous_lookups = strtoull(procfile_lineword(ff, l, 3), NULL, 10); - fh_dir_not_in_dcache = strtoull(procfile_lineword(ff, l, 4), NULL, 10); - fh_non_dir_not_in_dcache = strtoull(procfile_lineword(ff, l, 5), NULL, 10); + fh_stale = str2ull(procfile_lineword(ff, l, 1)); + fh_total_lookups = str2ull(procfile_lineword(ff, l, 2)); + fh_anonymous_lookups = str2ull(procfile_lineword(ff, l, 3)); + fh_dir_not_in_dcache = str2ull(procfile_lineword(ff, l, 4)); + fh_non_dir_not_in_dcache = str2ull(procfile_lineword(ff, l, 5)); unsigned long long sum = fh_stale + fh_total_lookups + fh_anonymous_lookups + fh_dir_not_in_dcache + fh_non_dir_not_in_dcache; if(sum == 0ULL) do_fh = -1; @@ -302,12 +301,12 @@ int do_proc_net_rpc_nfsd(int update_every, unsigned long long dt) { } else if(do_io == 1 && strcmp(type, "io") == 0) { if(words < 3) { - error("%s line of /proc/net/rpc/nfsd has %u words, expected %d", type, words, 3); + error("%s line of /proc/net/rpc/nfsd has %zu words, expected %d", type, words, 3); continue; } - io_read = strtoull(procfile_lineword(ff, l, 1), NULL, 10); - io_write = strtoull(procfile_lineword(ff, l, 2), NULL, 10); + io_read = str2ull(procfile_lineword(ff, l, 1)); + io_write = str2ull(procfile_lineword(ff, l, 2)); unsigned long long sum = io_read + io_write; if(sum == 0ULL) do_io = -1; @@ -315,12 +314,12 @@ int do_proc_net_rpc_nfsd(int update_every, unsigned long long dt) { } else if(do_th == 1 && strcmp(type, "th") == 0) { if(words < 13) { - error("%s line of /proc/net/rpc/nfsd has %u words, expected %d", type, words, 13); + error("%s line of /proc/net/rpc/nfsd has %zu words, expected %d", type, words, 13); continue; } - th_threads = strtoull(procfile_lineword(ff, l, 1), NULL, 10); - th_fullcnt = strtoull(procfile_lineword(ff, l, 2), NULL, 10); + th_threads = str2ull(procfile_lineword(ff, l, 1)); + th_fullcnt = str2ull(procfile_lineword(ff, l, 2)); th_hist10 = (unsigned long long)(atof(procfile_lineword(ff, l, 3)) * 1000.0); th_hist20 = (unsigned long long)(atof(procfile_lineword(ff, l, 4)) * 1000.0); th_hist30 = (unsigned long long)(atof(procfile_lineword(ff, l, 5)) * 1000.0); @@ -346,22 +345,22 @@ int do_proc_net_rpc_nfsd(int update_every, unsigned long long dt) { } else if(do_ra == 1 && strcmp(type, "ra") == 0) { if(words < 13) { - error("%s line of /proc/net/rpc/nfsd has %u words, expected %d", type, words, 13); + error("%s line of /proc/net/rpc/nfsd has %zu words, expected %d", type, words, 13); continue; } - ra_size = strtoull(procfile_lineword(ff, l, 1), NULL, 10); - ra_hist10 = strtoull(procfile_lineword(ff, l, 2), NULL, 10); - ra_hist20 = strtoull(procfile_lineword(ff, l, 3), NULL, 10); - ra_hist30 = strtoull(procfile_lineword(ff, l, 4), NULL, 10); - ra_hist40 = strtoull(procfile_lineword(ff, l, 5), NULL, 10); - ra_hist50 = strtoull(procfile_lineword(ff, l, 6), NULL, 10); - ra_hist60 = strtoull(procfile_lineword(ff, l, 7), NULL, 10); - ra_hist70 = strtoull(procfile_lineword(ff, l, 8), NULL, 10); - ra_hist80 = strtoull(procfile_lineword(ff, l, 9), NULL, 10); - ra_hist90 = strtoull(procfile_lineword(ff, l, 10), NULL, 10); - ra_hist100 = strtoull(procfile_lineword(ff, l, 11), NULL, 10); - ra_none = strtoull(procfile_lineword(ff, l, 12), NULL, 10); + ra_size = str2ull(procfile_lineword(ff, l, 1)); + ra_hist10 = str2ull(procfile_lineword(ff, l, 2)); + ra_hist20 = str2ull(procfile_lineword(ff, l, 3)); + ra_hist30 = str2ull(procfile_lineword(ff, l, 4)); + ra_hist40 = str2ull(procfile_lineword(ff, l, 5)); + ra_hist50 = str2ull(procfile_lineword(ff, l, 6)); + ra_hist60 = str2ull(procfile_lineword(ff, l, 7)); + ra_hist70 = str2ull(procfile_lineword(ff, l, 8)); + ra_hist80 = str2ull(procfile_lineword(ff, l, 9)); + ra_hist90 = str2ull(procfile_lineword(ff, l, 10)); + ra_hist100 = str2ull(procfile_lineword(ff, l, 11)); + ra_none = str2ull(procfile_lineword(ff, l, 12)); unsigned long long sum = ra_hist10 + ra_hist20 + ra_hist30 + ra_hist40 + ra_hist50 + ra_hist60 + ra_hist70 + ra_hist80 + ra_hist90 + ra_hist100 + ra_none; if(sum == 0ULL) { @@ -375,14 +374,14 @@ int do_proc_net_rpc_nfsd(int update_every, unsigned long long dt) { } else if(do_net == 1 && strcmp(type, "net") == 0) { if(words < 5) { - error("%s line of /proc/net/rpc/nfsd has %u words, expected %d", type, words, 5); + error("%s line of /proc/net/rpc/nfsd has %zu words, expected %d", type, words, 5); continue; } - net_count = strtoull(procfile_lineword(ff, l, 1), NULL, 10); - net_udp_count = strtoull(procfile_lineword(ff, l, 2), NULL, 10); - net_tcp_count = strtoull(procfile_lineword(ff, l, 3), NULL, 10); - net_tcp_connections = strtoull(procfile_lineword(ff, l, 4), NULL, 10); + net_count = str2ull(procfile_lineword(ff, l, 1)); + net_udp_count = str2ull(procfile_lineword(ff, l, 2)); + net_tcp_count = str2ull(procfile_lineword(ff, l, 3)); + net_tcp_connections = str2ull(procfile_lineword(ff, l, 4)); unsigned long long sum = net_count + net_udp_count + net_tcp_count + net_tcp_connections; if(sum == 0ULL) do_net = -1; @@ -390,14 +389,14 @@ int do_proc_net_rpc_nfsd(int update_every, unsigned long long dt) { } else if(do_rpc == 1 && strcmp(type, "rpc") == 0) { if(words < 6) { - error("%s line of /proc/net/rpc/nfsd has %u words, expected %d", type, words, 6); + error("%s line of /proc/net/rpc/nfsd has %zu words, expected %d", type, words, 6); continue; } - rpc_calls = strtoull(procfile_lineword(ff, l, 1), NULL, 10); - rpc_bad_format = strtoull(procfile_lineword(ff, l, 2), NULL, 10); - rpc_bad_auth = strtoull(procfile_lineword(ff, l, 3), NULL, 10); - rpc_bad_client = strtoull(procfile_lineword(ff, l, 4), NULL, 10); + rpc_calls = str2ull(procfile_lineword(ff, l, 1)); + rpc_bad_format = str2ull(procfile_lineword(ff, l, 2)); + rpc_bad_auth = str2ull(procfile_lineword(ff, l, 3)); + rpc_bad_client = str2ull(procfile_lineword(ff, l, 4)); unsigned long long sum = rpc_calls + rpc_bad_format + rpc_bad_auth + rpc_bad_client; if(sum == 0ULL) do_rpc = -1; @@ -410,7 +409,7 @@ int do_proc_net_rpc_nfsd(int update_every, unsigned long long dt) { unsigned long long sum = 0; unsigned int i, j; for(i = 0, j = 2; j < words && nfsd_proc2_values[i].name[0] ; i++, j++) { - nfsd_proc2_values[i].value = strtoull(procfile_lineword(ff, l, j), NULL, 10); + nfsd_proc2_values[i].value = str2ull(procfile_lineword(ff, l, j)); nfsd_proc2_values[i].present = 1; sum += nfsd_proc2_values[i].value; } @@ -431,7 +430,7 @@ int do_proc_net_rpc_nfsd(int update_every, unsigned long long dt) { unsigned long long sum = 0; unsigned int i, j; for(i = 0, j = 2; j < words && nfsd_proc3_values[i].name[0] ; i++, j++) { - nfsd_proc3_values[i].value = strtoull(procfile_lineword(ff, l, j), NULL, 10); + nfsd_proc3_values[i].value = str2ull(procfile_lineword(ff, l, j)); nfsd_proc3_values[i].present = 1; sum += nfsd_proc3_values[i].value; } @@ -452,7 +451,7 @@ int do_proc_net_rpc_nfsd(int update_every, unsigned long long dt) { unsigned long long sum = 0; unsigned int i, j; for(i = 0, j = 2; j < words && nfsd_proc4_values[i].name[0] ; i++, j++) { - nfsd_proc4_values[i].value = strtoull(procfile_lineword(ff, l, j), NULL, 10); + nfsd_proc4_values[i].value = str2ull(procfile_lineword(ff, l, j)); nfsd_proc4_values[i].present = 1; sum += nfsd_proc4_values[i].value; } @@ -473,7 +472,7 @@ int do_proc_net_rpc_nfsd(int update_every, unsigned long long dt) { unsigned long long sum = 0; unsigned int i, j; for(i = 0, j = 2; j < words && nfsd4_ops_values[i].name[0] ; i++, j++) { - nfsd4_ops_values[i].value = strtoull(procfile_lineword(ff, l, j), NULL, 10); + nfsd4_ops_values[i].value = str2ull(procfile_lineword(ff, l, j)); nfsd4_ops_values[i].present = 1; sum += nfsd4_ops_values[i].value; } diff --git a/src/proc_net_snmp.c b/src/proc_net_snmp.c index a75c0a96a..cd5c250ae 100644 --- a/src/proc_net_snmp.c +++ b/src/proc_net_snmp.c @@ -1,9 +1,6 @@ #include "common.h" #define RRD_TYPE_NET_SNMP "ipv4" -#define RRD_TYPE_NET_SNMP_LEN strlen(RRD_TYPE_NET_SNMP) - -#define NETSTAT_PRESENT 0x00000001 struct netstat_columns { char *name; @@ -174,13 +171,13 @@ static unsigned long long *netstat_columns_find(struct netstat_columns *nc, cons fatal("Cannot find key '%s' in /proc/net/snmp internal array.", name); } -static void parse_line_pair(procfile *ff, struct netstat_columns *nc, uint32_t header_line, uint32_t values_line) { - uint32_t hwords = procfile_linewords(ff, header_line); - uint32_t vwords = procfile_linewords(ff, values_line); - uint32_t w, i; +static void parse_line_pair(procfile *ff, struct netstat_columns *nc, size_t header_line, size_t values_line) { + size_t hwords = procfile_linewords(ff, header_line); + size_t vwords = procfile_linewords(ff, values_line); + size_t w, i; if(unlikely(vwords > hwords)) { - error("File /proc/net/snmp on header line %u has %u words, but on value line %u has %u words.", header_line, hwords, values_line, vwords); + error("File /proc/net/snmp on header line %zu has %zu words, but on value line %zu has %zu words.", header_line, hwords, values_line, vwords); vwords = hwords; } @@ -190,14 +187,14 @@ static void parse_line_pair(procfile *ff, struct netstat_columns *nc, uint32_t h for(i = 0 ; nc[i].name ;i++) { if(unlikely(hash == nc[i].hash && !strcmp(key, nc[i].name))) { - nc[i].value = strtoull(procfile_lineword(ff, values_line, w), NULL, 10); + nc[i].value = str2ull(procfile_lineword(ff, values_line, w)); break; } } } } -int do_proc_net_snmp(int update_every, unsigned long long dt) { +int do_proc_net_snmp(int update_every, usec_t dt) { (void)dt; static procfile *ff = NULL; @@ -360,14 +357,14 @@ int do_proc_net_snmp(int update_every, unsigned long long dt) { char filename[FILENAME_MAX + 1]; snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/net/snmp"); ff = procfile_open(config_get("plugin:proc:/proc/net/snmp", "filename to monitor", filename), " \t:", PROCFILE_FLAG_DEFAULT); + if(unlikely(!ff)) return 1; } - if(unlikely(!ff)) return 1; ff = procfile_readall(ff); if(unlikely(!ff)) return 0; // we return 0, so that we will retry to open it next time - uint32_t lines = procfile_lines(ff), l; - uint32_t words; + size_t lines = procfile_lines(ff), l; + size_t words; RRDSET *st; @@ -376,7 +373,7 @@ int do_proc_net_snmp(int update_every, unsigned long long dt) { uint32_t hash = simple_hash(key); if(unlikely(hash == hash_ip && strcmp(key, "Ip") == 0)) { - uint32_t h = l++; + size_t h = l++; if(strcmp(procfile_lineword(ff, l, 0), "Ip") != 0) { error("Cannot read Ip line from /proc/net/snmp."); @@ -385,7 +382,7 @@ int do_proc_net_snmp(int update_every, unsigned long long dt) { words = procfile_linewords(ff, l); if(words < 3) { - error("Cannot read /proc/net/snmp Ip line. Expected 3+ params, read %u.", words); + error("Cannot read /proc/net/snmp Ip line. Expected 3+ params, read %zu.", words); continue; } @@ -482,7 +479,7 @@ int do_proc_net_snmp(int update_every, unsigned long long dt) { } } else if(unlikely(hash == hash_icmp && strcmp(key, "Icmp") == 0)) { - uint32_t h = l++; + size_t h = l++; if(strcmp(procfile_lineword(ff, l, 0), "Icmp") != 0) { error("Cannot read Icmp line from /proc/net/snmp."); @@ -491,7 +488,7 @@ int do_proc_net_snmp(int update_every, unsigned long long dt) { words = procfile_linewords(ff, l); if(words < 3) { - error("Cannot read /proc/net/snmp Icmp line. Expected 3+ params, read %u.", words); + error("Cannot read /proc/net/snmp Icmp line. Expected 3+ params, read %zu.", words); continue; } @@ -532,7 +529,7 @@ int do_proc_net_snmp(int update_every, unsigned long long dt) { } } else if(unlikely(hash == hash_icmpmsg && strcmp(key, "IcmpMsg") == 0)) { - uint32_t h = l++; + size_t h = l++; if(strcmp(procfile_lineword(ff, l, 0), "IcmpMsg") != 0) { error("Cannot read IcmpMsg line from /proc/net/snmp."); @@ -562,7 +559,7 @@ int do_proc_net_snmp(int update_every, unsigned long long dt) { } } else if(unlikely(hash == hash_tcp && strcmp(key, "Tcp") == 0)) { - uint32_t h = l++; + size_t h = l++; if(strcmp(procfile_lineword(ff, l, 0), "Tcp") != 0) { error("Cannot read Tcp line from /proc/net/snmp."); @@ -571,7 +568,7 @@ int do_proc_net_snmp(int update_every, unsigned long long dt) { words = procfile_linewords(ff, l); if(words < 3) { - error("Cannot read /proc/net/snmp Tcp line. Expected 3+ params, read %u.", words); + error("Cannot read /proc/net/snmp Tcp line. Expected 3+ params, read %zu.", words); continue; } @@ -655,7 +652,7 @@ int do_proc_net_snmp(int update_every, unsigned long long dt) { } } else if(unlikely(hash == hash_udp && strcmp(key, "Udp") == 0)) { - uint32_t h = l++; + size_t h = l++; if(strcmp(procfile_lineword(ff, l, 0), "Udp") != 0) { error("Cannot read Udp line from /proc/net/snmp."); @@ -664,7 +661,7 @@ int do_proc_net_snmp(int update_every, unsigned long long dt) { words = procfile_linewords(ff, l); if(words < 3) { - error("Cannot read /proc/net/snmp Udp line. Expected 3+ params, read %u.", words); + error("Cannot read /proc/net/snmp Udp line. Expected 3+ params, read %zu.", words); continue; } @@ -715,7 +712,7 @@ int do_proc_net_snmp(int update_every, unsigned long long dt) { } } else if(unlikely(hash == hash_udplite && strcmp(key, "UdpLite") == 0)) { - uint32_t h = l++; + size_t h = l++; if(strcmp(procfile_lineword(ff, l, 0), "UdpLite") != 0) { error("Cannot read UdpLite line from /proc/net/snmp."); @@ -724,7 +721,7 @@ int do_proc_net_snmp(int update_every, unsigned long long dt) { words = procfile_linewords(ff, l); if(words < 3) { - error("Cannot read /proc/net/snmp UdpLite line. Expected 3+ params, read %u.", words); + error("Cannot read /proc/net/snmp UdpLite line. Expected 3+ params, read %zu.", words); continue; } diff --git a/src/proc_net_snmp6.c b/src/proc_net_snmp6.c index 97dc20edd..51d7121a1 100644 --- a/src/proc_net_snmp6.c +++ b/src/proc_net_snmp6.c @@ -1,456 +1,275 @@ #include "common.h" #define RRD_TYPE_NET_SNMP6 "ipv6" -#define RRD_TYPE_NET_SNMP6_LEN strlen(RRD_TYPE_NET_SNMP6) -int do_proc_net_snmp6(int update_every, unsigned long long dt) { +int do_proc_net_snmp6(int update_every, usec_t dt) { + (void)dt; + static procfile *ff = NULL; - static int gen_hashes = -1; - - static int do_ip_packets = -1, do_ip_fragsout = -1, do_ip_fragsin = -1, do_ip_errors = -1, - do_udplite_packets = -1, do_udplite_errors = -1, - do_udp_packets = -1, do_udp_errors = -1, - do_bandwidth = -1, do_mcast = -1, do_bcast = -1, do_mcast_p = -1, - do_icmp = -1, do_icmp_redir = -1, do_icmp_errors = -1, do_icmp_echos = -1, do_icmp_groupmemb = -1, - do_icmp_router = -1, do_icmp_neighbor = -1, do_icmp_mldv2 = -1, do_icmp_types = -1, do_ect = -1; - - static uint32_t hash_Ip6InReceives = -1; - - static uint32_t hash_Ip6InHdrErrors = -1; - static uint32_t hash_Ip6InTooBigErrors = -1; - static uint32_t hash_Ip6InNoRoutes = -1; - static uint32_t hash_Ip6InAddrErrors = -1; - static uint32_t hash_Ip6InUnknownProtos = -1; - static uint32_t hash_Ip6InTruncatedPkts = -1; - static uint32_t hash_Ip6InDiscards = -1; - static uint32_t hash_Ip6InDelivers = -1; - - static uint32_t hash_Ip6OutForwDatagrams = -1; - static uint32_t hash_Ip6OutRequests = -1; - static uint32_t hash_Ip6OutDiscards = -1; - static uint32_t hash_Ip6OutNoRoutes = -1; - - static uint32_t hash_Ip6ReasmTimeout = -1; - static uint32_t hash_Ip6ReasmReqds = -1; - static uint32_t hash_Ip6ReasmOKs = -1; - static uint32_t hash_Ip6ReasmFails = -1; - - static uint32_t hash_Ip6FragOKs = -1; - static uint32_t hash_Ip6FragFails = -1; - static uint32_t hash_Ip6FragCreates = -1; - - static uint32_t hash_Ip6InMcastPkts = -1; - static uint32_t hash_Ip6OutMcastPkts = -1; - - static uint32_t hash_Ip6InOctets = -1; - static uint32_t hash_Ip6OutOctets = -1; - - static uint32_t hash_Ip6InMcastOctets = -1; - static uint32_t hash_Ip6OutMcastOctets = -1; - static uint32_t hash_Ip6InBcastOctets = -1; - static uint32_t hash_Ip6OutBcastOctets = -1; - - static uint32_t hash_Ip6InNoECTPkts = -1; - static uint32_t hash_Ip6InECT1Pkts = -1; - static uint32_t hash_Ip6InECT0Pkts = -1; - static uint32_t hash_Ip6InCEPkts = -1; - - static uint32_t hash_Icmp6InMsgs = -1; - static uint32_t hash_Icmp6InErrors = -1; - static uint32_t hash_Icmp6OutMsgs = -1; - static uint32_t hash_Icmp6OutErrors = -1; - static uint32_t hash_Icmp6InCsumErrors = -1; - static uint32_t hash_Icmp6InDestUnreachs = -1; - static uint32_t hash_Icmp6InPktTooBigs = -1; - static uint32_t hash_Icmp6InTimeExcds = -1; - static uint32_t hash_Icmp6InParmProblems = -1; - static uint32_t hash_Icmp6InEchos = -1; - static uint32_t hash_Icmp6InEchoReplies = -1; - static uint32_t hash_Icmp6InGroupMembQueries = -1; - static uint32_t hash_Icmp6InGroupMembResponses = -1; - static uint32_t hash_Icmp6InGroupMembReductions = -1; - static uint32_t hash_Icmp6InRouterSolicits = -1; - static uint32_t hash_Icmp6InRouterAdvertisements = -1; - static uint32_t hash_Icmp6InNeighborSolicits = -1; - static uint32_t hash_Icmp6InNeighborAdvertisements = -1; - static uint32_t hash_Icmp6InRedirects = -1; - static uint32_t hash_Icmp6InMLDv2Reports = -1; - static uint32_t hash_Icmp6OutDestUnreachs = -1; - static uint32_t hash_Icmp6OutPktTooBigs = -1; - static uint32_t hash_Icmp6OutTimeExcds = -1; - static uint32_t hash_Icmp6OutParmProblems = -1; - static uint32_t hash_Icmp6OutEchos = -1; - static uint32_t hash_Icmp6OutEchoReplies = -1; - static uint32_t hash_Icmp6OutGroupMembQueries = -1; - static uint32_t hash_Icmp6OutGroupMembResponses = -1; - static uint32_t hash_Icmp6OutGroupMembReductions = -1; - static uint32_t hash_Icmp6OutRouterSolicits = -1; - static uint32_t hash_Icmp6OutRouterAdvertisements = -1; - static uint32_t hash_Icmp6OutNeighborSolicits = -1; - static uint32_t hash_Icmp6OutNeighborAdvertisements = -1; - static uint32_t hash_Icmp6OutRedirects = -1; - static uint32_t hash_Icmp6OutMLDv2Reports = -1; - static uint32_t hash_Icmp6InType1 = -1; - static uint32_t hash_Icmp6InType128 = -1; - static uint32_t hash_Icmp6InType129 = -1; - static uint32_t hash_Icmp6InType136 = -1; - static uint32_t hash_Icmp6OutType1 = -1; - static uint32_t hash_Icmp6OutType128 = -1; - static uint32_t hash_Icmp6OutType129 = -1; - static uint32_t hash_Icmp6OutType133 = -1; - static uint32_t hash_Icmp6OutType135 = -1; - static uint32_t hash_Icmp6OutType143 = -1; - - static uint32_t hash_Udp6InDatagrams = -1; - static uint32_t hash_Udp6NoPorts = -1; - static uint32_t hash_Udp6InErrors = -1; - static uint32_t hash_Udp6OutDatagrams = -1; - static uint32_t hash_Udp6RcvbufErrors = -1; - static uint32_t hash_Udp6SndbufErrors = -1; - static uint32_t hash_Udp6InCsumErrors = -1; - static uint32_t hash_Udp6IgnoredMulti = -1; - - static uint32_t hash_UdpLite6InDatagrams = -1; - static uint32_t hash_UdpLite6NoPorts = -1; - static uint32_t hash_UdpLite6InErrors = -1; - static uint32_t hash_UdpLite6OutDatagrams = -1; - static uint32_t hash_UdpLite6RcvbufErrors = -1; - static uint32_t hash_UdpLite6SndbufErrors = -1; - static uint32_t hash_UdpLite6InCsumErrors = -1; - - if(gen_hashes != 1) { - gen_hashes = 1; - hash_Ip6InReceives = simple_hash("Ip6InReceives"); - hash_Ip6InHdrErrors = simple_hash("Ip6InHdrErrors"); - hash_Ip6InTooBigErrors = simple_hash("Ip6InTooBigErrors"); - hash_Ip6InNoRoutes = simple_hash("Ip6InNoRoutes"); - hash_Ip6InAddrErrors = simple_hash("Ip6InAddrErrors"); - hash_Ip6InUnknownProtos = simple_hash("Ip6InUnknownProtos"); - hash_Ip6InTruncatedPkts = simple_hash("Ip6InTruncatedPkts"); - hash_Ip6InDiscards = simple_hash("Ip6InDiscards"); - hash_Ip6InDelivers = simple_hash("Ip6InDelivers"); - hash_Ip6OutForwDatagrams = simple_hash("Ip6OutForwDatagrams"); - hash_Ip6OutRequests = simple_hash("Ip6OutRequests"); - hash_Ip6OutDiscards = simple_hash("Ip6OutDiscards"); - hash_Ip6OutNoRoutes = simple_hash("Ip6OutNoRoutes"); - hash_Ip6ReasmTimeout = simple_hash("Ip6ReasmTimeout"); - hash_Ip6ReasmReqds = simple_hash("Ip6ReasmReqds"); - hash_Ip6ReasmOKs = simple_hash("Ip6ReasmOKs"); - hash_Ip6ReasmFails = simple_hash("Ip6ReasmFails"); - hash_Ip6FragOKs = simple_hash("Ip6FragOKs"); - hash_Ip6FragFails = simple_hash("Ip6FragFails"); - hash_Ip6FragCreates = simple_hash("Ip6FragCreates"); - hash_Ip6InMcastPkts = simple_hash("Ip6InMcastPkts"); - hash_Ip6OutMcastPkts = simple_hash("Ip6OutMcastPkts"); - hash_Ip6InOctets = simple_hash("Ip6InOctets"); - hash_Ip6OutOctets = simple_hash("Ip6OutOctets"); - hash_Ip6InMcastOctets = simple_hash("Ip6InMcastOctets"); - hash_Ip6OutMcastOctets = simple_hash("Ip6OutMcastOctets"); - hash_Ip6InBcastOctets = simple_hash("Ip6InBcastOctets"); - hash_Ip6OutBcastOctets = simple_hash("Ip6OutBcastOctets"); - hash_Ip6InNoECTPkts = simple_hash("Ip6InNoECTPkts"); - hash_Ip6InECT1Pkts = simple_hash("Ip6InECT1Pkts"); - hash_Ip6InECT0Pkts = simple_hash("Ip6InECT0Pkts"); - hash_Ip6InCEPkts = simple_hash("Ip6InCEPkts"); - hash_Icmp6InMsgs = simple_hash("Icmp6InMsgs"); - hash_Icmp6InErrors = simple_hash("Icmp6InErrors"); - hash_Icmp6OutMsgs = simple_hash("Icmp6OutMsgs"); - hash_Icmp6OutErrors = simple_hash("Icmp6OutErrors"); - hash_Icmp6InCsumErrors = simple_hash("Icmp6InCsumErrors"); - hash_Icmp6InDestUnreachs = simple_hash("Icmp6InDestUnreachs"); - hash_Icmp6InPktTooBigs = simple_hash("Icmp6InPktTooBigs"); - hash_Icmp6InTimeExcds = simple_hash("Icmp6InTimeExcds"); - hash_Icmp6InParmProblems = simple_hash("Icmp6InParmProblems"); - hash_Icmp6InEchos = simple_hash("Icmp6InEchos"); - hash_Icmp6InEchoReplies = simple_hash("Icmp6InEchoReplies"); - hash_Icmp6InGroupMembQueries = simple_hash("Icmp6InGroupMembQueries"); - hash_Icmp6InGroupMembResponses = simple_hash("Icmp6InGroupMembResponses"); - hash_Icmp6InGroupMembReductions = simple_hash("Icmp6InGroupMembReductions"); - hash_Icmp6InRouterSolicits = simple_hash("Icmp6InRouterSolicits"); - hash_Icmp6InRouterAdvertisements = simple_hash("Icmp6InRouterAdvertisements"); - hash_Icmp6InNeighborSolicits = simple_hash("Icmp6InNeighborSolicits"); - hash_Icmp6InNeighborAdvertisements = simple_hash("Icmp6InNeighborAdvertisements"); - hash_Icmp6InRedirects = simple_hash("Icmp6InRedirects"); - hash_Icmp6InMLDv2Reports = simple_hash("Icmp6InMLDv2Reports"); - hash_Icmp6OutDestUnreachs = simple_hash("Icmp6OutDestUnreachs"); - hash_Icmp6OutPktTooBigs = simple_hash("Icmp6OutPktTooBigs"); - hash_Icmp6OutTimeExcds = simple_hash("Icmp6OutTimeExcds"); - hash_Icmp6OutParmProblems = simple_hash("Icmp6OutParmProblems"); - hash_Icmp6OutEchos = simple_hash("Icmp6OutEchos"); - hash_Icmp6OutEchoReplies = simple_hash("Icmp6OutEchoReplies"); - hash_Icmp6OutGroupMembQueries = simple_hash("Icmp6OutGroupMembQueries"); - hash_Icmp6OutGroupMembResponses = simple_hash("Icmp6OutGroupMembResponses"); - hash_Icmp6OutGroupMembReductions = simple_hash("Icmp6OutGroupMembReductions"); - hash_Icmp6OutRouterSolicits = simple_hash("Icmp6OutRouterSolicits"); - hash_Icmp6OutRouterAdvertisements = simple_hash("Icmp6OutRouterAdvertisements"); - hash_Icmp6OutNeighborSolicits = simple_hash("Icmp6OutNeighborSolicits"); - hash_Icmp6OutNeighborAdvertisements = simple_hash("Icmp6OutNeighborAdvertisements"); - hash_Icmp6OutRedirects = simple_hash("Icmp6OutRedirects"); - hash_Icmp6OutMLDv2Reports = simple_hash("Icmp6OutMLDv2Reports"); - hash_Icmp6InType1 = simple_hash("Icmp6InType1"); - hash_Icmp6InType128 = simple_hash("Icmp6InType128"); - hash_Icmp6InType129 = simple_hash("Icmp6InType129"); - hash_Icmp6InType136 = simple_hash("Icmp6InType136"); - hash_Icmp6OutType1 = simple_hash("Icmp6OutType1"); - hash_Icmp6OutType128 = simple_hash("Icmp6OutType128"); - hash_Icmp6OutType129 = simple_hash("Icmp6OutType129"); - hash_Icmp6OutType133 = simple_hash("Icmp6OutType133"); - hash_Icmp6OutType135 = simple_hash("Icmp6OutType135"); - hash_Icmp6OutType143 = simple_hash("Icmp6OutType143"); - hash_Udp6InDatagrams = simple_hash("Udp6InDatagrams"); - hash_Udp6NoPorts = simple_hash("Udp6NoPorts"); - hash_Udp6InErrors = simple_hash("Udp6InErrors"); - hash_Udp6OutDatagrams = simple_hash("Udp6OutDatagrams"); - hash_Udp6RcvbufErrors = simple_hash("Udp6RcvbufErrors"); - hash_Udp6SndbufErrors = simple_hash("Udp6SndbufErrors"); - hash_Udp6InCsumErrors = simple_hash("Udp6InCsumErrors"); - hash_Udp6IgnoredMulti = simple_hash("Udp6IgnoredMulti"); - hash_UdpLite6InDatagrams = simple_hash("UdpLite6InDatagrams"); - hash_UdpLite6NoPorts = simple_hash("UdpLite6NoPorts"); - hash_UdpLite6InErrors = simple_hash("UdpLite6InErrors"); - hash_UdpLite6OutDatagrams = simple_hash("UdpLite6OutDatagrams"); - hash_UdpLite6RcvbufErrors = simple_hash("UdpLite6RcvbufErrors"); - hash_UdpLite6SndbufErrors = simple_hash("UdpLite6SndbufErrors"); - hash_UdpLite6InCsumErrors = simple_hash("UdpLite6InCsumErrors"); + + static int do_ip_packets = -1, + do_ip_fragsout = -1, + do_ip_fragsin = -1, + do_ip_errors = -1, + do_udplite_packets = -1, + do_udplite_errors = -1, + do_udp_packets = -1, + do_udp_errors = -1, + do_bandwidth = -1, + do_mcast = -1, + do_bcast = -1, + do_mcast_p = -1, + do_icmp = -1, + do_icmp_redir = -1, + do_icmp_errors = -1, + do_icmp_echos = -1, + do_icmp_groupmemb = -1, + do_icmp_router = -1, + do_icmp_neighbor = -1, + do_icmp_mldv2 = -1, + do_icmp_types = -1, + do_ect = -1; + + static ARL_BASE *arl_base = NULL; + + static unsigned long long Ip6InReceives = 0ULL; + static unsigned long long Ip6InHdrErrors = 0ULL; + static unsigned long long Ip6InTooBigErrors = 0ULL; + static unsigned long long Ip6InNoRoutes = 0ULL; + static unsigned long long Ip6InAddrErrors = 0ULL; + static unsigned long long Ip6InUnknownProtos = 0ULL; + static unsigned long long Ip6InTruncatedPkts = 0ULL; + static unsigned long long Ip6InDiscards = 0ULL; + static unsigned long long Ip6InDelivers = 0ULL; + static unsigned long long Ip6OutForwDatagrams = 0ULL; + static unsigned long long Ip6OutRequests = 0ULL; + static unsigned long long Ip6OutDiscards = 0ULL; + static unsigned long long Ip6OutNoRoutes = 0ULL; + static unsigned long long Ip6ReasmTimeout = 0ULL; + static unsigned long long Ip6ReasmReqds = 0ULL; + static unsigned long long Ip6ReasmOKs = 0ULL; + static unsigned long long Ip6ReasmFails = 0ULL; + static unsigned long long Ip6FragOKs = 0ULL; + static unsigned long long Ip6FragFails = 0ULL; + static unsigned long long Ip6FragCreates = 0ULL; + static unsigned long long Ip6InMcastPkts = 0ULL; + static unsigned long long Ip6OutMcastPkts = 0ULL; + static unsigned long long Ip6InOctets = 0ULL; + static unsigned long long Ip6OutOctets = 0ULL; + static unsigned long long Ip6InMcastOctets = 0ULL; + static unsigned long long Ip6OutMcastOctets = 0ULL; + static unsigned long long Ip6InBcastOctets = 0ULL; + static unsigned long long Ip6OutBcastOctets = 0ULL; + static unsigned long long Ip6InNoECTPkts = 0ULL; + static unsigned long long Ip6InECT1Pkts = 0ULL; + static unsigned long long Ip6InECT0Pkts = 0ULL; + static unsigned long long Ip6InCEPkts = 0ULL; + static unsigned long long Icmp6InMsgs = 0ULL; + static unsigned long long Icmp6InErrors = 0ULL; + static unsigned long long Icmp6OutMsgs = 0ULL; + static unsigned long long Icmp6OutErrors = 0ULL; + static unsigned long long Icmp6InCsumErrors = 0ULL; + static unsigned long long Icmp6InDestUnreachs = 0ULL; + static unsigned long long Icmp6InPktTooBigs = 0ULL; + static unsigned long long Icmp6InTimeExcds = 0ULL; + static unsigned long long Icmp6InParmProblems = 0ULL; + static unsigned long long Icmp6InEchos = 0ULL; + static unsigned long long Icmp6InEchoReplies = 0ULL; + static unsigned long long Icmp6InGroupMembQueries = 0ULL; + static unsigned long long Icmp6InGroupMembResponses = 0ULL; + static unsigned long long Icmp6InGroupMembReductions = 0ULL; + static unsigned long long Icmp6InRouterSolicits = 0ULL; + static unsigned long long Icmp6InRouterAdvertisements = 0ULL; + static unsigned long long Icmp6InNeighborSolicits = 0ULL; + static unsigned long long Icmp6InNeighborAdvertisements = 0ULL; + static unsigned long long Icmp6InRedirects = 0ULL; + static unsigned long long Icmp6InMLDv2Reports = 0ULL; + static unsigned long long Icmp6OutDestUnreachs = 0ULL; + static unsigned long long Icmp6OutPktTooBigs = 0ULL; + static unsigned long long Icmp6OutTimeExcds = 0ULL; + static unsigned long long Icmp6OutParmProblems = 0ULL; + static unsigned long long Icmp6OutEchos = 0ULL; + static unsigned long long Icmp6OutEchoReplies = 0ULL; + static unsigned long long Icmp6OutGroupMembQueries = 0ULL; + static unsigned long long Icmp6OutGroupMembResponses = 0ULL; + static unsigned long long Icmp6OutGroupMembReductions = 0ULL; + static unsigned long long Icmp6OutRouterSolicits = 0ULL; + static unsigned long long Icmp6OutRouterAdvertisements = 0ULL; + static unsigned long long Icmp6OutNeighborSolicits = 0ULL; + static unsigned long long Icmp6OutNeighborAdvertisements = 0ULL; + static unsigned long long Icmp6OutRedirects = 0ULL; + static unsigned long long Icmp6OutMLDv2Reports = 0ULL; + static unsigned long long Icmp6InType1 = 0ULL; + static unsigned long long Icmp6InType128 = 0ULL; + static unsigned long long Icmp6InType129 = 0ULL; + static unsigned long long Icmp6InType136 = 0ULL; + static unsigned long long Icmp6OutType1 = 0ULL; + static unsigned long long Icmp6OutType128 = 0ULL; + static unsigned long long Icmp6OutType129 = 0ULL; + static unsigned long long Icmp6OutType133 = 0ULL; + static unsigned long long Icmp6OutType135 = 0ULL; + static unsigned long long Icmp6OutType143 = 0ULL; + static unsigned long long Udp6InDatagrams = 0ULL; + static unsigned long long Udp6NoPorts = 0ULL; + static unsigned long long Udp6InErrors = 0ULL; + static unsigned long long Udp6OutDatagrams = 0ULL; + static unsigned long long Udp6RcvbufErrors = 0ULL; + static unsigned long long Udp6SndbufErrors = 0ULL; + static unsigned long long Udp6InCsumErrors = 0ULL; + static unsigned long long Udp6IgnoredMulti = 0ULL; + static unsigned long long UdpLite6InDatagrams = 0ULL; + static unsigned long long UdpLite6NoPorts = 0ULL; + static unsigned long long UdpLite6InErrors = 0ULL; + static unsigned long long UdpLite6OutDatagrams = 0ULL; + static unsigned long long UdpLite6RcvbufErrors = 0ULL; + static unsigned long long UdpLite6SndbufErrors = 0ULL; + static unsigned long long UdpLite6InCsumErrors = 0ULL; + + if(unlikely(!arl_base)) { + do_ip_packets = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ipv6 packets", CONFIG_ONDEMAND_ONDEMAND); + do_ip_fragsout = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ipv6 fragments sent", CONFIG_ONDEMAND_ONDEMAND); + do_ip_fragsin = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ipv6 fragments assembly", CONFIG_ONDEMAND_ONDEMAND); + do_ip_errors = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ipv6 errors", CONFIG_ONDEMAND_ONDEMAND); + do_udp_packets = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ipv6 UDP packets", CONFIG_ONDEMAND_ONDEMAND); + do_udp_errors = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ipv6 UDP errors", CONFIG_ONDEMAND_ONDEMAND); + do_udplite_packets = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ipv6 UDPlite packets", CONFIG_ONDEMAND_ONDEMAND); + do_udplite_errors = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ipv6 UDPlite errors", CONFIG_ONDEMAND_ONDEMAND); + do_bandwidth = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "bandwidth", CONFIG_ONDEMAND_ONDEMAND); + do_mcast = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "multicast bandwidth", CONFIG_ONDEMAND_ONDEMAND); + do_bcast = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "broadcast bandwidth", CONFIG_ONDEMAND_ONDEMAND); + do_mcast_p = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "multicast packets", CONFIG_ONDEMAND_ONDEMAND); + do_icmp = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp", CONFIG_ONDEMAND_ONDEMAND); + do_icmp_redir = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp redirects", CONFIG_ONDEMAND_ONDEMAND); + do_icmp_errors = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp errors", CONFIG_ONDEMAND_ONDEMAND); + do_icmp_echos = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp echos", CONFIG_ONDEMAND_ONDEMAND); + do_icmp_groupmemb = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp group membership", CONFIG_ONDEMAND_ONDEMAND); + do_icmp_router = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp router", CONFIG_ONDEMAND_ONDEMAND); + do_icmp_neighbor = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp neighbor", CONFIG_ONDEMAND_ONDEMAND); + do_icmp_mldv2 = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp mldv2", CONFIG_ONDEMAND_ONDEMAND); + do_icmp_types = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp types", CONFIG_ONDEMAND_ONDEMAND); + do_ect = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ect", CONFIG_ONDEMAND_ONDEMAND); + + arl_base = arl_create("snmp6", NULL, 60); + arl_expect(arl_base, "Ip6InReceives", &Ip6InReceives); + arl_expect(arl_base, "Ip6InHdrErrors", &Ip6InHdrErrors); + arl_expect(arl_base, "Ip6InTooBigErrors", &Ip6InTooBigErrors); + arl_expect(arl_base, "Ip6InNoRoutes", &Ip6InNoRoutes); + arl_expect(arl_base, "Ip6InAddrErrors", &Ip6InAddrErrors); + arl_expect(arl_base, "Ip6InUnknownProtos", &Ip6InUnknownProtos); + arl_expect(arl_base, "Ip6InTruncatedPkts", &Ip6InTruncatedPkts); + arl_expect(arl_base, "Ip6InDiscards", &Ip6InDiscards); + arl_expect(arl_base, "Ip6InDelivers", &Ip6InDelivers); + arl_expect(arl_base, "Ip6OutForwDatagrams", &Ip6OutForwDatagrams); + arl_expect(arl_base, "Ip6OutRequests", &Ip6OutRequests); + arl_expect(arl_base, "Ip6OutDiscards", &Ip6OutDiscards); + arl_expect(arl_base, "Ip6OutNoRoutes", &Ip6OutNoRoutes); + arl_expect(arl_base, "Ip6ReasmTimeout", &Ip6ReasmTimeout); + arl_expect(arl_base, "Ip6ReasmReqds", &Ip6ReasmReqds); + arl_expect(arl_base, "Ip6ReasmOKs", &Ip6ReasmOKs); + arl_expect(arl_base, "Ip6ReasmFails", &Ip6ReasmFails); + arl_expect(arl_base, "Ip6FragOKs", &Ip6FragOKs); + arl_expect(arl_base, "Ip6FragFails", &Ip6FragFails); + arl_expect(arl_base, "Ip6FragCreates", &Ip6FragCreates); + arl_expect(arl_base, "Ip6InMcastPkts", &Ip6InMcastPkts); + arl_expect(arl_base, "Ip6OutMcastPkts", &Ip6OutMcastPkts); + arl_expect(arl_base, "Ip6InOctets", &Ip6InOctets); + arl_expect(arl_base, "Ip6OutOctets", &Ip6OutOctets); + arl_expect(arl_base, "Ip6InMcastOctets", &Ip6InMcastOctets); + arl_expect(arl_base, "Ip6OutMcastOctets", &Ip6OutMcastOctets); + arl_expect(arl_base, "Ip6InBcastOctets", &Ip6InBcastOctets); + arl_expect(arl_base, "Ip6OutBcastOctets", &Ip6OutBcastOctets); + arl_expect(arl_base, "Ip6InNoECTPkts", &Ip6InNoECTPkts); + arl_expect(arl_base, "Ip6InECT1Pkts", &Ip6InECT1Pkts); + arl_expect(arl_base, "Ip6InECT0Pkts", &Ip6InECT0Pkts); + arl_expect(arl_base, "Ip6InCEPkts", &Ip6InCEPkts); + arl_expect(arl_base, "Icmp6InMsgs", &Icmp6InMsgs); + arl_expect(arl_base, "Icmp6InErrors", &Icmp6InErrors); + arl_expect(arl_base, "Icmp6OutMsgs", &Icmp6OutMsgs); + arl_expect(arl_base, "Icmp6OutErrors", &Icmp6OutErrors); + arl_expect(arl_base, "Icmp6InCsumErrors", &Icmp6InCsumErrors); + arl_expect(arl_base, "Icmp6InDestUnreachs", &Icmp6InDestUnreachs); + arl_expect(arl_base, "Icmp6InPktTooBigs", &Icmp6InPktTooBigs); + arl_expect(arl_base, "Icmp6InTimeExcds", &Icmp6InTimeExcds); + arl_expect(arl_base, "Icmp6InParmProblems", &Icmp6InParmProblems); + arl_expect(arl_base, "Icmp6InEchos", &Icmp6InEchos); + arl_expect(arl_base, "Icmp6InEchoReplies", &Icmp6InEchoReplies); + arl_expect(arl_base, "Icmp6InGroupMembQueries", &Icmp6InGroupMembQueries); + arl_expect(arl_base, "Icmp6InGroupMembResponses", &Icmp6InGroupMembResponses); + arl_expect(arl_base, "Icmp6InGroupMembReductions", &Icmp6InGroupMembReductions); + arl_expect(arl_base, "Icmp6InRouterSolicits", &Icmp6InRouterSolicits); + arl_expect(arl_base, "Icmp6InRouterAdvertisements", &Icmp6InRouterAdvertisements); + arl_expect(arl_base, "Icmp6InNeighborSolicits", &Icmp6InNeighborSolicits); + arl_expect(arl_base, "Icmp6InNeighborAdvertisements", &Icmp6InNeighborAdvertisements); + arl_expect(arl_base, "Icmp6InRedirects", &Icmp6InRedirects); + arl_expect(arl_base, "Icmp6InMLDv2Reports", &Icmp6InMLDv2Reports); + arl_expect(arl_base, "Icmp6OutDestUnreachs", &Icmp6OutDestUnreachs); + arl_expect(arl_base, "Icmp6OutPktTooBigs", &Icmp6OutPktTooBigs); + arl_expect(arl_base, "Icmp6OutTimeExcds", &Icmp6OutTimeExcds); + arl_expect(arl_base, "Icmp6OutParmProblems", &Icmp6OutParmProblems); + arl_expect(arl_base, "Icmp6OutEchos", &Icmp6OutEchos); + arl_expect(arl_base, "Icmp6OutEchoReplies", &Icmp6OutEchoReplies); + arl_expect(arl_base, "Icmp6OutGroupMembQueries", &Icmp6OutGroupMembQueries); + arl_expect(arl_base, "Icmp6OutGroupMembResponses", &Icmp6OutGroupMembResponses); + arl_expect(arl_base, "Icmp6OutGroupMembReductions", &Icmp6OutGroupMembReductions); + arl_expect(arl_base, "Icmp6OutRouterSolicits", &Icmp6OutRouterSolicits); + arl_expect(arl_base, "Icmp6OutRouterAdvertisements", &Icmp6OutRouterAdvertisements); + arl_expect(arl_base, "Icmp6OutNeighborSolicits", &Icmp6OutNeighborSolicits); + arl_expect(arl_base, "Icmp6OutNeighborAdvertisements", &Icmp6OutNeighborAdvertisements); + arl_expect(arl_base, "Icmp6OutRedirects", &Icmp6OutRedirects); + arl_expect(arl_base, "Icmp6OutMLDv2Reports", &Icmp6OutMLDv2Reports); + arl_expect(arl_base, "Icmp6InType1", &Icmp6InType1); + arl_expect(arl_base, "Icmp6InType128", &Icmp6InType128); + arl_expect(arl_base, "Icmp6InType129", &Icmp6InType129); + arl_expect(arl_base, "Icmp6InType136", &Icmp6InType136); + arl_expect(arl_base, "Icmp6OutType1", &Icmp6OutType1); + arl_expect(arl_base, "Icmp6OutType128", &Icmp6OutType128); + arl_expect(arl_base, "Icmp6OutType129", &Icmp6OutType129); + arl_expect(arl_base, "Icmp6OutType133", &Icmp6OutType133); + arl_expect(arl_base, "Icmp6OutType135", &Icmp6OutType135); + arl_expect(arl_base, "Icmp6OutType143", &Icmp6OutType143); + arl_expect(arl_base, "Udp6InDatagrams", &Udp6InDatagrams); + arl_expect(arl_base, "Udp6NoPorts", &Udp6NoPorts); + arl_expect(arl_base, "Udp6InErrors", &Udp6InErrors); + arl_expect(arl_base, "Udp6OutDatagrams", &Udp6OutDatagrams); + arl_expect(arl_base, "Udp6RcvbufErrors", &Udp6RcvbufErrors); + arl_expect(arl_base, "Udp6SndbufErrors", &Udp6SndbufErrors); + arl_expect(arl_base, "Udp6InCsumErrors", &Udp6InCsumErrors); + arl_expect(arl_base, "Udp6IgnoredMulti", &Udp6IgnoredMulti); + arl_expect(arl_base, "UdpLite6InDatagrams", &UdpLite6InDatagrams); + arl_expect(arl_base, "UdpLite6NoPorts", &UdpLite6NoPorts); + arl_expect(arl_base, "UdpLite6InErrors", &UdpLite6InErrors); + arl_expect(arl_base, "UdpLite6OutDatagrams", &UdpLite6OutDatagrams); + arl_expect(arl_base, "UdpLite6RcvbufErrors", &UdpLite6RcvbufErrors); + arl_expect(arl_base, "UdpLite6SndbufErrors", &UdpLite6SndbufErrors); + arl_expect(arl_base, "UdpLite6InCsumErrors", &UdpLite6InCsumErrors); } - if(do_ip_packets == -1) do_ip_packets = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ipv6 packets", CONFIG_ONDEMAND_ONDEMAND); - if(do_ip_fragsout == -1) do_ip_fragsout = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ipv6 fragments sent", CONFIG_ONDEMAND_ONDEMAND); - if(do_ip_fragsin == -1) do_ip_fragsin = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ipv6 fragments assembly", CONFIG_ONDEMAND_ONDEMAND); - if(do_ip_errors == -1) do_ip_errors = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ipv6 errors", CONFIG_ONDEMAND_ONDEMAND); - if(do_udp_packets == -1) do_udp_packets = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ipv6 UDP packets", CONFIG_ONDEMAND_ONDEMAND); - if(do_udp_errors == -1) do_udp_errors = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ipv6 UDP errors", CONFIG_ONDEMAND_ONDEMAND); - if(do_udplite_packets == -1) do_udplite_packets = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ipv6 UDPlite packets", CONFIG_ONDEMAND_ONDEMAND); - if(do_udplite_errors == -1) do_udplite_errors = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ipv6 UDPlite errors", CONFIG_ONDEMAND_ONDEMAND); - if(do_bandwidth == -1) do_bandwidth = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "bandwidth", CONFIG_ONDEMAND_ONDEMAND); - if(do_mcast == -1) do_mcast = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "multicast bandwidth", CONFIG_ONDEMAND_ONDEMAND); - if(do_bcast == -1) do_bcast = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "broadcast bandwidth", CONFIG_ONDEMAND_ONDEMAND); - if(do_mcast_p == -1) do_mcast_p = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "multicast packets", CONFIG_ONDEMAND_ONDEMAND); - if(do_icmp == -1) do_icmp = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp", CONFIG_ONDEMAND_ONDEMAND); - if(do_icmp_redir == -1) do_icmp_redir = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp redirects", CONFIG_ONDEMAND_ONDEMAND); - if(do_icmp_errors == -1) do_icmp_errors = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp errors", CONFIG_ONDEMAND_ONDEMAND); - if(do_icmp_echos == -1) do_icmp_echos = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp echos", CONFIG_ONDEMAND_ONDEMAND); - if(do_icmp_groupmemb == -1) do_icmp_groupmemb = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp group membership", CONFIG_ONDEMAND_ONDEMAND); - if(do_icmp_router == -1) do_icmp_router = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp router", CONFIG_ONDEMAND_ONDEMAND); - if(do_icmp_neighbor == -1) do_icmp_neighbor = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp neighbor", CONFIG_ONDEMAND_ONDEMAND); - if(do_icmp_mldv2 == -1) do_icmp_mldv2 = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp mldv2", CONFIG_ONDEMAND_ONDEMAND); - if(do_icmp_types == -1) do_icmp_types = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp types", CONFIG_ONDEMAND_ONDEMAND); - if(do_ect == -1) do_ect = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ect", CONFIG_ONDEMAND_ONDEMAND); - - if(dt) {}; - - if(!ff) { + if(unlikely(!ff)) { char filename[FILENAME_MAX + 1]; snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/net/snmp6"); ff = procfile_open(config_get("plugin:proc:/proc/net/snmp6", "filename to monitor", filename), " \t:", PROCFILE_FLAG_DEFAULT); + if(unlikely(!ff)) + return 1; } - if(!ff) return 1; ff = procfile_readall(ff); - if(!ff) return 0; // we return 0, so that we will retry to open it next time - - uint32_t lines = procfile_lines(ff), l; - uint32_t words; - - unsigned long long Ip6InReceives = 0ULL; - unsigned long long Ip6InHdrErrors = 0ULL; - unsigned long long Ip6InTooBigErrors = 0ULL; - unsigned long long Ip6InNoRoutes = 0ULL; - unsigned long long Ip6InAddrErrors = 0ULL; - unsigned long long Ip6InUnknownProtos = 0ULL; - unsigned long long Ip6InTruncatedPkts = 0ULL; - unsigned long long Ip6InDiscards = 0ULL; - unsigned long long Ip6InDelivers = 0ULL; - unsigned long long Ip6OutForwDatagrams = 0ULL; - unsigned long long Ip6OutRequests = 0ULL; - unsigned long long Ip6OutDiscards = 0ULL; - unsigned long long Ip6OutNoRoutes = 0ULL; - unsigned long long Ip6ReasmTimeout = 0ULL; - unsigned long long Ip6ReasmReqds = 0ULL; - unsigned long long Ip6ReasmOKs = 0ULL; - unsigned long long Ip6ReasmFails = 0ULL; - unsigned long long Ip6FragOKs = 0ULL; - unsigned long long Ip6FragFails = 0ULL; - unsigned long long Ip6FragCreates = 0ULL; - unsigned long long Ip6InMcastPkts = 0ULL; - unsigned long long Ip6OutMcastPkts = 0ULL; - unsigned long long Ip6InOctets = 0ULL; - unsigned long long Ip6OutOctets = 0ULL; - unsigned long long Ip6InMcastOctets = 0ULL; - unsigned long long Ip6OutMcastOctets = 0ULL; - unsigned long long Ip6InBcastOctets = 0ULL; - unsigned long long Ip6OutBcastOctets = 0ULL; - unsigned long long Ip6InNoECTPkts = 0ULL; - unsigned long long Ip6InECT1Pkts = 0ULL; - unsigned long long Ip6InECT0Pkts = 0ULL; - unsigned long long Ip6InCEPkts = 0ULL; - unsigned long long Icmp6InMsgs = 0ULL; - unsigned long long Icmp6InErrors = 0ULL; - unsigned long long Icmp6OutMsgs = 0ULL; - unsigned long long Icmp6OutErrors = 0ULL; - unsigned long long Icmp6InCsumErrors = 0ULL; - unsigned long long Icmp6InDestUnreachs = 0ULL; - unsigned long long Icmp6InPktTooBigs = 0ULL; - unsigned long long Icmp6InTimeExcds = 0ULL; - unsigned long long Icmp6InParmProblems = 0ULL; - unsigned long long Icmp6InEchos = 0ULL; - unsigned long long Icmp6InEchoReplies = 0ULL; - unsigned long long Icmp6InGroupMembQueries = 0ULL; - unsigned long long Icmp6InGroupMembResponses = 0ULL; - unsigned long long Icmp6InGroupMembReductions = 0ULL; - unsigned long long Icmp6InRouterSolicits = 0ULL; - unsigned long long Icmp6InRouterAdvertisements = 0ULL; - unsigned long long Icmp6InNeighborSolicits = 0ULL; - unsigned long long Icmp6InNeighborAdvertisements = 0ULL; - unsigned long long Icmp6InRedirects = 0ULL; - unsigned long long Icmp6InMLDv2Reports = 0ULL; - unsigned long long Icmp6OutDestUnreachs = 0ULL; - unsigned long long Icmp6OutPktTooBigs = 0ULL; - unsigned long long Icmp6OutTimeExcds = 0ULL; - unsigned long long Icmp6OutParmProblems = 0ULL; - unsigned long long Icmp6OutEchos = 0ULL; - unsigned long long Icmp6OutEchoReplies = 0ULL; - unsigned long long Icmp6OutGroupMembQueries = 0ULL; - unsigned long long Icmp6OutGroupMembResponses = 0ULL; - unsigned long long Icmp6OutGroupMembReductions = 0ULL; - unsigned long long Icmp6OutRouterSolicits = 0ULL; - unsigned long long Icmp6OutRouterAdvertisements = 0ULL; - unsigned long long Icmp6OutNeighborSolicits = 0ULL; - unsigned long long Icmp6OutNeighborAdvertisements = 0ULL; - unsigned long long Icmp6OutRedirects = 0ULL; - unsigned long long Icmp6OutMLDv2Reports = 0ULL; - unsigned long long Icmp6InType1 = 0ULL; - unsigned long long Icmp6InType128 = 0ULL; - unsigned long long Icmp6InType129 = 0ULL; - unsigned long long Icmp6InType136 = 0ULL; - unsigned long long Icmp6OutType1 = 0ULL; - unsigned long long Icmp6OutType128 = 0ULL; - unsigned long long Icmp6OutType129 = 0ULL; - unsigned long long Icmp6OutType133 = 0ULL; - unsigned long long Icmp6OutType135 = 0ULL; - unsigned long long Icmp6OutType143 = 0ULL; - unsigned long long Udp6InDatagrams = 0ULL; - unsigned long long Udp6NoPorts = 0ULL; - unsigned long long Udp6InErrors = 0ULL; - unsigned long long Udp6OutDatagrams = 0ULL; - unsigned long long Udp6RcvbufErrors = 0ULL; - unsigned long long Udp6SndbufErrors = 0ULL; - unsigned long long Udp6InCsumErrors = 0ULL; - unsigned long long Udp6IgnoredMulti = 0ULL; - unsigned long long UdpLite6InDatagrams = 0ULL; - unsigned long long UdpLite6NoPorts = 0ULL; - unsigned long long UdpLite6InErrors = 0ULL; - unsigned long long UdpLite6OutDatagrams = 0ULL; - unsigned long long UdpLite6RcvbufErrors = 0ULL; - unsigned long long UdpLite6SndbufErrors = 0ULL; - unsigned long long UdpLite6InCsumErrors = 0ULL; + if(unlikely(!ff)) + return 0; // we return 0, so that we will retry to open it next time + + size_t lines = procfile_lines(ff), l; + + arl_begin(arl_base); for(l = 0; l < lines ;l++) { - words = procfile_linewords(ff, l); - if(words < 2) { - if(words) error("Cannot read /proc/net/snmp6 line %u. Expected 2 params, read %u.", l, words); + size_t words = procfile_linewords(ff, l); + if(unlikely(words < 2)) { + if(unlikely(words)) error("Cannot read /proc/net/snmp6 line %zu. Expected 2 params, read %zu.", l, words); continue; } - char *name = procfile_lineword(ff, l, 0); - char * value = procfile_lineword(ff, l, 1); - if(!name || !*name || !value || !*value) continue; - - uint32_t hash = simple_hash(name); - - if(0) ; - else if(hash == hash_Ip6InReceives && strcmp(name, "Ip6InReceives") == 0) Ip6InReceives = strtoull(value, NULL, 10); - else if(hash == hash_Ip6InHdrErrors && strcmp(name, "Ip6InHdrErrors") == 0) Ip6InHdrErrors = strtoull(value, NULL, 10); - else if(hash == hash_Ip6InTooBigErrors && strcmp(name, "Ip6InTooBigErrors") == 0) Ip6InTooBigErrors = strtoull(value, NULL, 10); - else if(hash == hash_Ip6InNoRoutes && strcmp(name, "Ip6InNoRoutes") == 0) Ip6InNoRoutes = strtoull(value, NULL, 10); - else if(hash == hash_Ip6InAddrErrors && strcmp(name, "Ip6InAddrErrors") == 0) Ip6InAddrErrors = strtoull(value, NULL, 10); - else if(hash == hash_Ip6InUnknownProtos && strcmp(name, "Ip6InUnknownProtos") == 0) Ip6InUnknownProtos = strtoull(value, NULL, 10); - else if(hash == hash_Ip6InTruncatedPkts && strcmp(name, "Ip6InTruncatedPkts") == 0) Ip6InTruncatedPkts = strtoull(value, NULL, 10); - else if(hash == hash_Ip6InDiscards && strcmp(name, "Ip6InDiscards") == 0) Ip6InDiscards = strtoull(value, NULL, 10); - else if(hash == hash_Ip6InDelivers && strcmp(name, "Ip6InDelivers") == 0) Ip6InDelivers = strtoull(value, NULL, 10); - else if(hash == hash_Ip6OutForwDatagrams && strcmp(name, "Ip6OutForwDatagrams") == 0) Ip6OutForwDatagrams = strtoull(value, NULL, 10); - else if(hash == hash_Ip6OutRequests && strcmp(name, "Ip6OutRequests") == 0) Ip6OutRequests = strtoull(value, NULL, 10); - else if(hash == hash_Ip6OutDiscards && strcmp(name, "Ip6OutDiscards") == 0) Ip6OutDiscards = strtoull(value, NULL, 10); - else if(hash == hash_Ip6OutNoRoutes && strcmp(name, "Ip6OutNoRoutes") == 0) Ip6OutNoRoutes = strtoull(value, NULL, 10); - else if(hash == hash_Ip6ReasmTimeout && strcmp(name, "Ip6ReasmTimeout") == 0) Ip6ReasmTimeout = strtoull(value, NULL, 10); - else if(hash == hash_Ip6ReasmReqds && strcmp(name, "Ip6ReasmReqds") == 0) Ip6ReasmReqds = strtoull(value, NULL, 10); - else if(hash == hash_Ip6ReasmOKs && strcmp(name, "Ip6ReasmOKs") == 0) Ip6ReasmOKs = strtoull(value, NULL, 10); - else if(hash == hash_Ip6ReasmFails && strcmp(name, "Ip6ReasmFails") == 0) Ip6ReasmFails = strtoull(value, NULL, 10); - else if(hash == hash_Ip6FragOKs && strcmp(name, "Ip6FragOKs") == 0) Ip6FragOKs = strtoull(value, NULL, 10); - else if(hash == hash_Ip6FragFails && strcmp(name, "Ip6FragFails") == 0) Ip6FragFails = strtoull(value, NULL, 10); - else if(hash == hash_Ip6FragCreates && strcmp(name, "Ip6FragCreates") == 0) Ip6FragCreates = strtoull(value, NULL, 10); - else if(hash == hash_Ip6InMcastPkts && strcmp(name, "Ip6InMcastPkts") == 0) Ip6InMcastPkts = strtoull(value, NULL, 10); - else if(hash == hash_Ip6OutMcastPkts && strcmp(name, "Ip6OutMcastPkts") == 0) Ip6OutMcastPkts = strtoull(value, NULL, 10); - else if(hash == hash_Ip6InOctets && strcmp(name, "Ip6InOctets") == 0) Ip6InOctets = strtoull(value, NULL, 10); - else if(hash == hash_Ip6OutOctets && strcmp(name, "Ip6OutOctets") == 0) Ip6OutOctets = strtoull(value, NULL, 10); - else if(hash == hash_Ip6InMcastOctets && strcmp(name, "Ip6InMcastOctets") == 0) Ip6InMcastOctets = strtoull(value, NULL, 10); - else if(hash == hash_Ip6OutMcastOctets && strcmp(name, "Ip6OutMcastOctets") == 0) Ip6OutMcastOctets = strtoull(value, NULL, 10); - else if(hash == hash_Ip6InBcastOctets && strcmp(name, "Ip6InBcastOctets") == 0) Ip6InBcastOctets = strtoull(value, NULL, 10); - else if(hash == hash_Ip6OutBcastOctets && strcmp(name, "Ip6OutBcastOctets") == 0) Ip6OutBcastOctets = strtoull(value, NULL, 10); - else if(hash == hash_Ip6InNoECTPkts && strcmp(name, "Ip6InNoECTPkts") == 0) Ip6InNoECTPkts = strtoull(value, NULL, 10); - else if(hash == hash_Ip6InECT1Pkts && strcmp(name, "Ip6InECT1Pkts") == 0) Ip6InECT1Pkts = strtoull(value, NULL, 10); - else if(hash == hash_Ip6InECT0Pkts && strcmp(name, "Ip6InECT0Pkts") == 0) Ip6InECT0Pkts = strtoull(value, NULL, 10); - else if(hash == hash_Ip6InCEPkts && strcmp(name, "Ip6InCEPkts") == 0) Ip6InCEPkts = strtoull(value, NULL, 10); - else if(hash == hash_Icmp6InMsgs && strcmp(name, "Icmp6InMsgs") == 0) Icmp6InMsgs = strtoull(value, NULL, 10); - else if(hash == hash_Icmp6InErrors && strcmp(name, "Icmp6InErrors") == 0) Icmp6InErrors = strtoull(value, NULL, 10); - else if(hash == hash_Icmp6OutMsgs && strcmp(name, "Icmp6OutMsgs") == 0) Icmp6OutMsgs = strtoull(value, NULL, 10); - else if(hash == hash_Icmp6OutErrors && strcmp(name, "Icmp6OutErrors") == 0) Icmp6OutErrors = strtoull(value, NULL, 10); - else if(hash == hash_Icmp6InCsumErrors && strcmp(name, "Icmp6InCsumErrors") == 0) Icmp6InCsumErrors = strtoull(value, NULL, 10); - else if(hash == hash_Icmp6InDestUnreachs && strcmp(name, "Icmp6InDestUnreachs") == 0) Icmp6InDestUnreachs = strtoull(value, NULL, 10); - else if(hash == hash_Icmp6InPktTooBigs && strcmp(name, "Icmp6InPktTooBigs") == 0) Icmp6InPktTooBigs = strtoull(value, NULL, 10); - else if(hash == hash_Icmp6InTimeExcds && strcmp(name, "Icmp6InTimeExcds") == 0) Icmp6InTimeExcds = strtoull(value, NULL, 10); - else if(hash == hash_Icmp6InParmProblems && strcmp(name, "Icmp6InParmProblems") == 0) Icmp6InParmProblems = strtoull(value, NULL, 10); - else if(hash == hash_Icmp6InEchos && strcmp(name, "Icmp6InEchos") == 0) Icmp6InEchos = strtoull(value, NULL, 10); - else if(hash == hash_Icmp6InEchoReplies && strcmp(name, "Icmp6InEchoReplies") == 0) Icmp6InEchoReplies = strtoull(value, NULL, 10); - else if(hash == hash_Icmp6InGroupMembQueries && strcmp(name, "Icmp6InGroupMembQueries") == 0) Icmp6InGroupMembQueries = strtoull(value, NULL, 10); - else if(hash == hash_Icmp6InGroupMembResponses && strcmp(name, "Icmp6InGroupMembResponses") == 0) Icmp6InGroupMembResponses = strtoull(value, NULL, 10); - else if(hash == hash_Icmp6InGroupMembReductions && strcmp(name, "Icmp6InGroupMembReductions") == 0) Icmp6InGroupMembReductions = strtoull(value, NULL, 10); - else if(hash == hash_Icmp6InRouterSolicits && strcmp(name, "Icmp6InRouterSolicits") == 0) Icmp6InRouterSolicits = strtoull(value, NULL, 10); - else if(hash == hash_Icmp6InRouterAdvertisements && strcmp(name, "Icmp6InRouterAdvertisements") == 0) Icmp6InRouterAdvertisements = strtoull(value, NULL, 10); - else if(hash == hash_Icmp6InNeighborSolicits && strcmp(name, "Icmp6InNeighborSolicits") == 0) Icmp6InNeighborSolicits = strtoull(value, NULL, 10); - else if(hash == hash_Icmp6InNeighborAdvertisements && strcmp(name, "Icmp6InNeighborAdvertisements") == 0) Icmp6InNeighborAdvertisements = strtoull(value, NULL, 10); - else if(hash == hash_Icmp6InRedirects && strcmp(name, "Icmp6InRedirects") == 0) Icmp6InRedirects = strtoull(value, NULL, 10); - else if(hash == hash_Icmp6InMLDv2Reports && strcmp(name, "Icmp6InMLDv2Reports") == 0) Icmp6InMLDv2Reports = strtoull(value, NULL, 10); - else if(hash == hash_Icmp6OutDestUnreachs && strcmp(name, "Icmp6OutDestUnreachs") == 0) Icmp6OutDestUnreachs = strtoull(value, NULL, 10); - else if(hash == hash_Icmp6OutPktTooBigs && strcmp(name, "Icmp6OutPktTooBigs") == 0) Icmp6OutPktTooBigs = strtoull(value, NULL, 10); - else if(hash == hash_Icmp6OutTimeExcds && strcmp(name, "Icmp6OutTimeExcds") == 0) Icmp6OutTimeExcds = strtoull(value, NULL, 10); - else if(hash == hash_Icmp6OutParmProblems && strcmp(name, "Icmp6OutParmProblems") == 0) Icmp6OutParmProblems = strtoull(value, NULL, 10); - else if(hash == hash_Icmp6OutEchos && strcmp(name, "Icmp6OutEchos") == 0) Icmp6OutEchos = strtoull(value, NULL, 10); - else if(hash == hash_Icmp6OutEchoReplies && strcmp(name, "Icmp6OutEchoReplies") == 0) Icmp6OutEchoReplies = strtoull(value, NULL, 10); - else if(hash == hash_Icmp6OutGroupMembQueries && strcmp(name, "Icmp6OutGroupMembQueries") == 0) Icmp6OutGroupMembQueries = strtoull(value, NULL, 10); - else if(hash == hash_Icmp6OutGroupMembResponses && strcmp(name, "Icmp6OutGroupMembResponses") == 0) Icmp6OutGroupMembResponses = strtoull(value, NULL, 10); - else if(hash == hash_Icmp6OutGroupMembReductions && strcmp(name, "Icmp6OutGroupMembReductions") == 0) Icmp6OutGroupMembReductions = strtoull(value, NULL, 10); - else if(hash == hash_Icmp6OutRouterSolicits && strcmp(name, "Icmp6OutRouterSolicits") == 0) Icmp6OutRouterSolicits = strtoull(value, NULL, 10); - else if(hash == hash_Icmp6OutRouterAdvertisements && strcmp(name, "Icmp6OutRouterAdvertisements") == 0) Icmp6OutRouterAdvertisements = strtoull(value, NULL, 10); - else if(hash == hash_Icmp6OutNeighborSolicits && strcmp(name, "Icmp6OutNeighborSolicits") == 0) Icmp6OutNeighborSolicits = strtoull(value, NULL, 10); - else if(hash == hash_Icmp6OutNeighborAdvertisements && strcmp(name, "Icmp6OutNeighborAdvertisements") == 0) Icmp6OutNeighborAdvertisements = strtoull(value, NULL, 10); - else if(hash == hash_Icmp6OutRedirects && strcmp(name, "Icmp6OutRedirects") == 0) Icmp6OutRedirects = strtoull(value, NULL, 10); - else if(hash == hash_Icmp6OutMLDv2Reports && strcmp(name, "Icmp6OutMLDv2Reports") == 0) Icmp6OutMLDv2Reports = strtoull(value, NULL, 10); - else if(hash == hash_Icmp6InType1 && strcmp(name, "Icmp6InType1") == 0) Icmp6InType1 = strtoull(value, NULL, 10); - else if(hash == hash_Icmp6InType128 && strcmp(name, "Icmp6InType128") == 0) Icmp6InType128 = strtoull(value, NULL, 10); - else if(hash == hash_Icmp6InType129 && strcmp(name, "Icmp6InType129") == 0) Icmp6InType129 = strtoull(value, NULL, 10); - else if(hash == hash_Icmp6InType136 && strcmp(name, "Icmp6InType136") == 0) Icmp6InType136 = strtoull(value, NULL, 10); - else if(hash == hash_Icmp6OutType1 && strcmp(name, "Icmp6OutType1") == 0) Icmp6OutType1 = strtoull(value, NULL, 10); - else if(hash == hash_Icmp6OutType128 && strcmp(name, "Icmp6OutType128") == 0) Icmp6OutType128 = strtoull(value, NULL, 10); - else if(hash == hash_Icmp6OutType129 && strcmp(name, "Icmp6OutType129") == 0) Icmp6OutType129 = strtoull(value, NULL, 10); - else if(hash == hash_Icmp6OutType133 && strcmp(name, "Icmp6OutType133") == 0) Icmp6OutType133 = strtoull(value, NULL, 10); - else if(hash == hash_Icmp6OutType135 && strcmp(name, "Icmp6OutType135") == 0) Icmp6OutType135 = strtoull(value, NULL, 10); - else if(hash == hash_Icmp6OutType143 && strcmp(name, "Icmp6OutType143") == 0) Icmp6OutType143 = strtoull(value, NULL, 10); - else if(hash == hash_Udp6InDatagrams && strcmp(name, "Udp6InDatagrams") == 0) Udp6InDatagrams = strtoull(value, NULL, 10); - else if(hash == hash_Udp6NoPorts && strcmp(name, "Udp6NoPorts") == 0) Udp6NoPorts = strtoull(value, NULL, 10); - else if(hash == hash_Udp6InErrors && strcmp(name, "Udp6InErrors") == 0) Udp6InErrors = strtoull(value, NULL, 10); - else if(hash == hash_Udp6OutDatagrams && strcmp(name, "Udp6OutDatagrams") == 0) Udp6OutDatagrams = strtoull(value, NULL, 10); - else if(hash == hash_Udp6RcvbufErrors && strcmp(name, "Udp6RcvbufErrors") == 0) Udp6RcvbufErrors = strtoull(value, NULL, 10); - else if(hash == hash_Udp6SndbufErrors && strcmp(name, "Udp6SndbufErrors") == 0) Udp6SndbufErrors = strtoull(value, NULL, 10); - else if(hash == hash_Udp6InCsumErrors && strcmp(name, "Udp6InCsumErrors") == 0) Udp6InCsumErrors = strtoull(value, NULL, 10); - else if(hash == hash_Udp6IgnoredMulti && strcmp(name, "Udp6IgnoredMulti") == 0) Udp6IgnoredMulti = strtoull(value, NULL, 10); - else if(hash == hash_UdpLite6InDatagrams && strcmp(name, "UdpLite6InDatagrams") == 0) UdpLite6InDatagrams = strtoull(value, NULL, 10); - else if(hash == hash_UdpLite6NoPorts && strcmp(name, "UdpLite6NoPorts") == 0) UdpLite6NoPorts = strtoull(value, NULL, 10); - else if(hash == hash_UdpLite6InErrors && strcmp(name, "UdpLite6InErrors") == 0) UdpLite6InErrors = strtoull(value, NULL, 10); - else if(hash == hash_UdpLite6OutDatagrams && strcmp(name, "UdpLite6OutDatagrams") == 0) UdpLite6OutDatagrams = strtoull(value, NULL, 10); - else if(hash == hash_UdpLite6RcvbufErrors && strcmp(name, "UdpLite6RcvbufErrors") == 0) UdpLite6RcvbufErrors = strtoull(value, NULL, 10); - else if(hash == hash_UdpLite6SndbufErrors && strcmp(name, "UdpLite6SndbufErrors") == 0) UdpLite6SndbufErrors = strtoull(value, NULL, 10); - else if(hash == hash_UdpLite6InCsumErrors && strcmp(name, "UdpLite6InCsumErrors") == 0) UdpLite6InCsumErrors = strtoull(value, NULL, 10); + if(unlikely(arl_check(arl_base, + procfile_lineword(ff, l, 0), + procfile_lineword(ff, l, 1)))) break; } RRDSET *st; @@ -460,7 +279,7 @@ int do_proc_net_snmp6(int update_every, unsigned long long dt) { if(do_bandwidth == CONFIG_ONDEMAND_YES || (do_bandwidth == CONFIG_ONDEMAND_ONDEMAND && (Ip6InOctets || Ip6OutOctets))) { do_bandwidth = CONFIG_ONDEMAND_YES; st = rrdset_find("system.ipv6"); - if(!st) { + if(unlikely(!st)) { st = rrdset_create("system", "ipv6", NULL, "network", NULL, "IPv6 Bandwidth", "kilobits/s", 500, update_every, RRDSET_TYPE_AREA); rrddim_add(st, "received", NULL, 8, 1024, RRDDIM_INCREMENTAL); @@ -478,7 +297,7 @@ int do_proc_net_snmp6(int update_every, unsigned long long dt) { if(do_ip_packets == CONFIG_ONDEMAND_YES || (do_ip_packets == CONFIG_ONDEMAND_ONDEMAND && (Ip6InReceives || Ip6OutRequests || Ip6InDelivers || Ip6OutForwDatagrams))) { do_ip_packets = CONFIG_ONDEMAND_YES; st = rrdset_find(RRD_TYPE_NET_SNMP6 ".packets"); - if(!st) { + if(unlikely(!st)) { st = rrdset_create(RRD_TYPE_NET_SNMP6, "packets", NULL, "packets", NULL, "IPv6 Packets", "packets/s", 3000, update_every, RRDSET_TYPE_LINE); rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL); @@ -500,7 +319,7 @@ int do_proc_net_snmp6(int update_every, unsigned long long dt) { if(do_ip_fragsout == CONFIG_ONDEMAND_YES || (do_ip_fragsout == CONFIG_ONDEMAND_ONDEMAND && (Ip6FragOKs || Ip6FragFails || Ip6FragCreates))) { do_ip_fragsout = CONFIG_ONDEMAND_YES; st = rrdset_find(RRD_TYPE_NET_SNMP6 ".fragsout"); - if(!st) { + if(unlikely(!st)) { st = rrdset_create(RRD_TYPE_NET_SNMP6, "fragsout", NULL, "fragments", NULL, "IPv6 Fragments Sent", "packets/s", 3010, update_every, RRDSET_TYPE_LINE); st->isdetail = 1; @@ -527,7 +346,7 @@ int do_proc_net_snmp6(int update_every, unsigned long long dt) { ))) { do_ip_fragsin = CONFIG_ONDEMAND_YES; st = rrdset_find(RRD_TYPE_NET_SNMP6 ".fragsin"); - if(!st) { + if(unlikely(!st)) { st = rrdset_create(RRD_TYPE_NET_SNMP6, "fragsin", NULL, "fragments", NULL, "IPv6 Fragments Reassembly", "packets/s", 3011, update_every, RRDSET_TYPE_LINE); st->isdetail = 1; @@ -560,7 +379,7 @@ int do_proc_net_snmp6(int update_every, unsigned long long dt) { ))) { do_ip_errors = CONFIG_ONDEMAND_YES; st = rrdset_find(RRD_TYPE_NET_SNMP6 ".errors"); - if(!st) { + if(unlikely(!st)) { st = rrdset_create(RRD_TYPE_NET_SNMP6, "errors", NULL, "errors", NULL, "IPv6 Errors", "packets/s", 3002, update_every, RRDSET_TYPE_LINE); st->isdetail = 1; @@ -597,7 +416,7 @@ int do_proc_net_snmp6(int update_every, unsigned long long dt) { if(do_udp_packets == CONFIG_ONDEMAND_YES || (do_udp_packets == CONFIG_ONDEMAND_ONDEMAND && (Udp6InDatagrams || Udp6OutDatagrams))) { do_udp_packets = CONFIG_ONDEMAND_YES; st = rrdset_find(RRD_TYPE_NET_SNMP6 ".udppackets"); - if(!st) { + if(unlikely(!st)) { st = rrdset_create(RRD_TYPE_NET_SNMP6, "udppackets", NULL, "udp", NULL, "IPv6 UDP Packets", "packets/s", 3601, update_every, RRDSET_TYPE_LINE); rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL); @@ -623,7 +442,7 @@ int do_proc_net_snmp6(int update_every, unsigned long long dt) { ))) { do_udp_errors = CONFIG_ONDEMAND_YES; st = rrdset_find(RRD_TYPE_NET_SNMP6 ".udperrors"); - if(!st) { + if(unlikely(!st)) { st = rrdset_create(RRD_TYPE_NET_SNMP6, "udperrors", NULL, "udp", NULL, "IPv6 UDP Errors", "events/s", 3701, update_every, RRDSET_TYPE_LINE); st->isdetail = 1; @@ -650,7 +469,7 @@ int do_proc_net_snmp6(int update_every, unsigned long long dt) { if(do_udplite_packets == CONFIG_ONDEMAND_YES || (do_udplite_packets == CONFIG_ONDEMAND_ONDEMAND && (UdpLite6InDatagrams || UdpLite6OutDatagrams))) { do_udplite_packets = CONFIG_ONDEMAND_YES; st = rrdset_find(RRD_TYPE_NET_SNMP6 ".udplitepackets"); - if(!st) { + if(unlikely(!st)) { st = rrdset_create(RRD_TYPE_NET_SNMP6, "udplitepackets", NULL, "udplite", NULL, "IPv6 UDPlite Packets", "packets/s", 3601, update_every, RRDSET_TYPE_LINE); rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL); @@ -676,7 +495,7 @@ int do_proc_net_snmp6(int update_every, unsigned long long dt) { ))) { do_udplite_errors = CONFIG_ONDEMAND_YES; st = rrdset_find(RRD_TYPE_NET_SNMP6 ".udpliteerrors"); - if(!st) { + if(unlikely(!st)) { st = rrdset_create(RRD_TYPE_NET_SNMP6, "udpliteerrors", NULL, "udplite", NULL, "IPv6 UDP Lite Errors", "events/s", 3701, update_every, RRDSET_TYPE_LINE); st->isdetail = 1; @@ -701,7 +520,7 @@ int do_proc_net_snmp6(int update_every, unsigned long long dt) { if(do_mcast == CONFIG_ONDEMAND_YES || (do_mcast == CONFIG_ONDEMAND_ONDEMAND && (Ip6OutMcastOctets || Ip6InMcastOctets))) { do_mcast = CONFIG_ONDEMAND_YES; st = rrdset_find(RRD_TYPE_NET_SNMP6 ".mcast"); - if(!st) { + if(unlikely(!st)) { st = rrdset_create(RRD_TYPE_NET_SNMP6, "mcast", NULL, "multicast", NULL, "IPv6 Multicast Bandwidth", "kilobits/s", 9000, update_every, RRDSET_TYPE_AREA); st->isdetail = 1; @@ -720,7 +539,7 @@ int do_proc_net_snmp6(int update_every, unsigned long long dt) { if(do_bcast == CONFIG_ONDEMAND_YES || (do_bcast == CONFIG_ONDEMAND_ONDEMAND && (Ip6OutBcastOctets || Ip6InBcastOctets))) { do_bcast = CONFIG_ONDEMAND_YES; st = rrdset_find(RRD_TYPE_NET_SNMP6 ".bcast"); - if(!st) { + if(unlikely(!st)) { st = rrdset_create(RRD_TYPE_NET_SNMP6, "bcast", NULL, "broadcast", NULL, "IPv6 Broadcast Bandwidth", "kilobits/s", 8000, update_every, RRDSET_TYPE_AREA); st->isdetail = 1; @@ -739,7 +558,7 @@ int do_proc_net_snmp6(int update_every, unsigned long long dt) { if(do_mcast_p == CONFIG_ONDEMAND_YES || (do_mcast_p == CONFIG_ONDEMAND_ONDEMAND && (Ip6OutMcastPkts || Ip6InMcastPkts))) { do_mcast_p = CONFIG_ONDEMAND_YES; st = rrdset_find(RRD_TYPE_NET_SNMP6 ".mcastpkts"); - if(!st) { + if(unlikely(!st)) { st = rrdset_create(RRD_TYPE_NET_SNMP6, "mcastpkts", NULL, "multicast", NULL, "IPv6 Multicast Packets", "packets/s", 9500, update_every, RRDSET_TYPE_LINE); st->isdetail = 1; @@ -758,7 +577,7 @@ int do_proc_net_snmp6(int update_every, unsigned long long dt) { if(do_icmp == CONFIG_ONDEMAND_YES || (do_icmp == CONFIG_ONDEMAND_ONDEMAND && (Icmp6InMsgs || Icmp6OutMsgs))) { do_icmp = CONFIG_ONDEMAND_YES; st = rrdset_find(RRD_TYPE_NET_SNMP6 ".icmp"); - if(!st) { + if(unlikely(!st)) { st = rrdset_create(RRD_TYPE_NET_SNMP6, "icmp", NULL, "icmp", NULL, "IPv6 ICMP Messages", "messages/s", 10000, update_every, RRDSET_TYPE_LINE); rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL); @@ -776,7 +595,7 @@ int do_proc_net_snmp6(int update_every, unsigned long long dt) { if(do_icmp_redir == CONFIG_ONDEMAND_YES || (do_icmp_redir == CONFIG_ONDEMAND_ONDEMAND && (Icmp6InRedirects || Icmp6OutRedirects))) { do_icmp_redir = CONFIG_ONDEMAND_YES; st = rrdset_find(RRD_TYPE_NET_SNMP6 ".icmpredir"); - if(!st) { + if(unlikely(!st)) { st = rrdset_create(RRD_TYPE_NET_SNMP6, "icmpredir", NULL, "icmp", NULL, "IPv6 ICMP Redirects", "redirects/s", 10050, update_every, RRDSET_TYPE_LINE); rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL); @@ -807,7 +626,7 @@ int do_proc_net_snmp6(int update_every, unsigned long long dt) { ))) { do_icmp_errors = CONFIG_ONDEMAND_YES; st = rrdset_find(RRD_TYPE_NET_SNMP6 ".icmperrors"); - if(!st) { + if(unlikely(!st)) { st = rrdset_create(RRD_TYPE_NET_SNMP6, "icmperrors", NULL, "icmp", NULL, "IPv6 ICMP Errors", "errors/s", 10100, update_every, RRDSET_TYPE_LINE); rrddim_add(st, "InErrors", NULL, 1, 1, RRDDIM_INCREMENTAL); @@ -850,7 +669,7 @@ int do_proc_net_snmp6(int update_every, unsigned long long dt) { ))) { do_icmp_echos = CONFIG_ONDEMAND_YES; st = rrdset_find(RRD_TYPE_NET_SNMP6 ".icmpechos"); - if(!st) { + if(unlikely(!st)) { st = rrdset_create(RRD_TYPE_NET_SNMP6, "icmpechos", NULL, "icmp", NULL, "IPv6 ICMP Echo", "messages/s", 10200, update_every, RRDSET_TYPE_LINE); rrddim_add(st, "InEchos", NULL, 1, 1, RRDDIM_INCREMENTAL); @@ -880,7 +699,7 @@ int do_proc_net_snmp6(int update_every, unsigned long long dt) { ))) { do_icmp_groupmemb = CONFIG_ONDEMAND_YES; st = rrdset_find(RRD_TYPE_NET_SNMP6 ".groupmemb"); - if(!st) { + if(unlikely(!st)) { st = rrdset_create(RRD_TYPE_NET_SNMP6, "groupmemb", NULL, "icmp", NULL, "IPv6 ICMP Group Membership", "messages/s", 10300, update_every, RRDSET_TYPE_LINE); rrddim_add(st, "InQueries", NULL, 1, 1, RRDDIM_INCREMENTAL); @@ -912,7 +731,7 @@ int do_proc_net_snmp6(int update_every, unsigned long long dt) { ))) { do_icmp_router = CONFIG_ONDEMAND_YES; st = rrdset_find(RRD_TYPE_NET_SNMP6 ".icmprouter"); - if(!st) { + if(unlikely(!st)) { st = rrdset_create(RRD_TYPE_NET_SNMP6, "icmprouter", NULL, "icmp", NULL, "IPv6 Router Messages", "messages/s", 10400, update_every, RRDSET_TYPE_LINE); rrddim_add(st, "InSolicits", NULL, 1, 1, RRDDIM_INCREMENTAL); @@ -940,7 +759,7 @@ int do_proc_net_snmp6(int update_every, unsigned long long dt) { ))) { do_icmp_neighbor = CONFIG_ONDEMAND_YES; st = rrdset_find(RRD_TYPE_NET_SNMP6 ".icmpneighbor"); - if(!st) { + if(unlikely(!st)) { st = rrdset_create(RRD_TYPE_NET_SNMP6, "icmpneighbor", NULL, "icmp", NULL, "IPv6 Neighbor Messages", "messages/s", 10500, update_every, RRDSET_TYPE_LINE); rrddim_add(st, "InSolicits", NULL, 1, 1, RRDDIM_INCREMENTAL); @@ -962,7 +781,7 @@ int do_proc_net_snmp6(int update_every, unsigned long long dt) { if(do_icmp_mldv2 == CONFIG_ONDEMAND_YES || (do_icmp_mldv2 == CONFIG_ONDEMAND_ONDEMAND && (Icmp6InMLDv2Reports || Icmp6OutMLDv2Reports))) { do_icmp_mldv2 = CONFIG_ONDEMAND_YES; st = rrdset_find(RRD_TYPE_NET_SNMP6 ".icmpmldv2"); - if(!st) { + if(unlikely(!st)) { st = rrdset_create(RRD_TYPE_NET_SNMP6, "icmpmldv2", NULL, "icmp", NULL, "IPv6 ICMP MLDv2 Reports", "reports/s", 10600, update_every, RRDSET_TYPE_LINE); rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL); @@ -992,7 +811,7 @@ int do_proc_net_snmp6(int update_every, unsigned long long dt) { ))) { do_icmp_types = CONFIG_ONDEMAND_YES; st = rrdset_find(RRD_TYPE_NET_SNMP6 ".icmptypes"); - if(!st) { + if(unlikely(!st)) { st = rrdset_create(RRD_TYPE_NET_SNMP6, "icmptypes", NULL, "icmp", NULL, "IPv6 ICMP Types", "messages/s", 10700, update_every, RRDSET_TYPE_LINE); rrddim_add(st, "InType1", NULL, 1, 1, RRDDIM_INCREMENTAL); @@ -1032,7 +851,7 @@ int do_proc_net_snmp6(int update_every, unsigned long long dt) { ))) { do_ect = CONFIG_ONDEMAND_YES; st = rrdset_find(RRD_TYPE_NET_SNMP6 ".ect"); - if(!st) { + if(unlikely(!st)) { st = rrdset_create(RRD_TYPE_NET_SNMP6, "ect", NULL, "packets", NULL, "IPv6 ECT Packets", "packets/s", 10800, update_every, RRDSET_TYPE_LINE); rrddim_add(st, "InNoECTPkts", NULL, 1, 1, RRDDIM_INCREMENTAL); diff --git a/src/proc_net_softnet_stat.c b/src/proc_net_softnet_stat.c index b01315863..2f4eb3e66 100644 --- a/src/proc_net_softnet_stat.c +++ b/src/proc_net_softnet_stat.c @@ -1,6 +1,6 @@ #include "common.h" -static inline char *softnet_column_name(uint32_t column) { +static inline char *softnet_column_name(size_t column) { switch(column) { // https://github.com/torvalds/linux/blob/a7fd20d1c476af4563e66865213474a2f9f473a4/net/core/net-procfs.c#L161-L166 case 0: return "processed"; @@ -12,35 +12,36 @@ static inline char *softnet_column_name(uint32_t column) { } } -int do_proc_net_softnet_stat(int update_every, unsigned long long dt) { +int do_proc_net_softnet_stat(int update_every, usec_t dt) { (void)dt; static procfile *ff = NULL; static int do_per_core = -1; - static uint32_t allocated_lines = 0, allocated_columns = 0, *data = NULL; + static size_t allocated_lines = 0, allocated_columns = 0; + static uint32_t *data = NULL; - if(do_per_core == -1) do_per_core = config_get_boolean("plugin:proc:/proc/net/softnet_stat", "softnet_stat per core", 1); + if(unlikely(do_per_core == -1)) do_per_core = config_get_boolean("plugin:proc:/proc/net/softnet_stat", "softnet_stat per core", 1); - if(!ff) { + if(unlikely(!ff)) { char filename[FILENAME_MAX + 1]; snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/net/softnet_stat"); ff = procfile_open(config_get("plugin:proc:/proc/net/softnet_stat", "filename to monitor", filename), " \t", PROCFILE_FLAG_DEFAULT); + if(unlikely(!ff)) return 1; } - if(!ff) return 1; ff = procfile_readall(ff); - if(!ff) return 0; // we return 0, so that we will retry to open it next time + if(unlikely(!ff)) return 0; // we return 0, so that we will retry to open it next time - uint32_t lines = procfile_lines(ff), l; - uint32_t words = procfile_linewords(ff, 0), w; + size_t lines = procfile_lines(ff), l; + size_t words = procfile_linewords(ff, 0), w; - if(!lines || !words) { - error("Cannot read /proc/net/softnet_stat, %u lines and %u columns reported.", lines, words); + if(unlikely(!lines || !words)) { + error("Cannot read /proc/net/softnet_stat, %zu lines and %zu columns reported.", lines, words); return 1; } - if(lines > 200) lines = 200; - if(words > 50) words = 50; + if(unlikely(lines > 200)) lines = 200; + if(unlikely(words > 50)) words = 50; if(unlikely(!data || lines > allocated_lines || words > allocated_columns)) { freez(data); @@ -55,20 +56,21 @@ int do_proc_net_softnet_stat(int update_every, unsigned long long dt) { // parse the values for(l = 0; l < lines ;l++) { words = procfile_linewords(ff, l); - if(!words) continue; + if(unlikely(!words)) continue; - if(words > allocated_columns) words = allocated_columns; + if(unlikely(words > allocated_columns)) + words = allocated_columns; for(w = 0; w < words ; w++) { if(unlikely(softnet_column_name(w))) { - uint32_t t = strtoul(procfile_lineword(ff, l, w), NULL, 16); + uint32_t t = (uint32_t)strtoul(procfile_lineword(ff, l, w), NULL, 16); data[w] += t; data[((l + 1) * allocated_columns) + w] = t; } } } - if(data[(lines * allocated_columns)] == 0) + if(unlikely(data[(lines * allocated_columns)] == 0)) lines--; RRDSET *st; @@ -76,7 +78,7 @@ int do_proc_net_softnet_stat(int update_every, unsigned long long dt) { // -------------------------------------------------------------------- st = rrdset_find_bytype("system", "softnet_stat"); - if(!st) { + if(unlikely(!st)) { st = rrdset_create("system", "softnet_stat", NULL, "softnet_stat", NULL, "System softnet_stat", "events/s", 955, update_every, RRDSET_TYPE_LINE); for(w = 0; w < allocated_columns ;w++) if(unlikely(softnet_column_name(w))) @@ -93,12 +95,12 @@ int do_proc_net_softnet_stat(int update_every, unsigned long long dt) { if(do_per_core) { for(l = 0; l < lines ;l++) { char id[50+1]; - snprintfz(id, 50, "cpu%u_softnet_stat", l); + snprintfz(id, 50, "cpu%zu_softnet_stat", l); st = rrdset_find_bytype("cpu", id); - if(!st) { + if(unlikely(!st)) { char title[100+1]; - snprintfz(title, 100, "CPU%u softnet_stat", l); + snprintfz(title, 100, "CPU%zu softnet_stat", l); st = rrdset_create("cpu", id, NULL, "softnet_stat", NULL, title, "events/s", 4101 + l, update_every, RRDSET_TYPE_LINE); for(w = 0; w < allocated_columns ;w++) diff --git a/src/proc_net_stat_conntrack.c b/src/proc_net_stat_conntrack.c index 54e250bf2..b9c724983 100644 --- a/src/proc_net_stat_conntrack.c +++ b/src/proc_net_stat_conntrack.c @@ -2,83 +2,127 @@ #define RRD_TYPE_NET_STAT_NETFILTER "netfilter" #define RRD_TYPE_NET_STAT_CONNTRACK "conntrack" -#define RRD_TYPE_NET_STAT_CONNTRACK_LEN strlen(RRD_TYPE_NET_STAT_CONNTRACK) -int do_proc_net_stat_conntrack(int update_every, unsigned long long dt) { +int do_proc_net_stat_conntrack(int update_every, usec_t dt) { static procfile *ff = NULL; static int do_sockets = -1, do_new = -1, do_changes = -1, do_expect = -1, do_search = -1, do_errors = -1; + static usec_t get_max_every = 10 * USEC_PER_SEC, usec_since_last_max = 0; + static int read_full = 1; + static char *nf_conntrack_filename, *nf_conntrack_count_filename, *nf_conntrack_max_filename; + static RRDVAR *rrdvar_max = NULL; - if(do_sockets == -1) do_sockets = config_get_boolean("plugin:proc:/proc/net/stat/nf_conntrack", "netfilter connections", 1); - if(do_new == -1) do_new = config_get_boolean("plugin:proc:/proc/net/stat/nf_conntrack", "netfilter new connections", 1); - if(do_changes == -1) do_changes = config_get_boolean("plugin:proc:/proc/net/stat/nf_conntrack", "netfilter connection changes", 1); - if(do_expect == -1) do_expect = config_get_boolean("plugin:proc:/proc/net/stat/nf_conntrack", "netfilter connection expectations", 1); - if(do_search == -1) do_search = config_get_boolean("plugin:proc:/proc/net/stat/nf_conntrack", "netfilter connection searches", 1); - if(do_errors == -1) do_errors = config_get_boolean("plugin:proc:/proc/net/stat/nf_conntrack", "netfilter errors", 1); - - if(dt) {}; + unsigned long long aentries = 0, asearched = 0, afound = 0, anew = 0, ainvalid = 0, aignore = 0, adelete = 0, adelete_list = 0, + ainsert = 0, ainsert_failed = 0, adrop = 0, aearly_drop = 0, aicmp_error = 0, aexpect_new = 0, aexpect_create = 0, aexpect_delete = 0, asearch_restart = 0; - if(!ff) { + if(unlikely(do_sockets == -1)) { char filename[FILENAME_MAX + 1]; snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/net/stat/nf_conntrack"); - ff = procfile_open(config_get("plugin:proc:/proc/net/stat/nf_conntrack", "filename to monitor", filename), " \t:", PROCFILE_FLAG_DEFAULT); - } - if(!ff) return 1; + nf_conntrack_filename = config_get("plugin:proc:/proc/net/stat/nf_conntrack", "filename to monitor", filename); - ff = procfile_readall(ff); - if(!ff) return 0; // we return 0, so that we will retry to open it next time + snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/sys/net/netfilter/nf_conntrack_max"); + nf_conntrack_max_filename = config_get("plugin:proc:/proc/sys/net/netfilter/nf_conntrack_max", "filename to monitor", filename); + usec_since_last_max = get_max_every = config_get_number("plugin:proc:/proc/sys/net/netfilter/nf_conntrack_max", "read every seconds", 10) * USEC_PER_SEC; - uint32_t lines = procfile_lines(ff), l; - uint32_t words; + read_full = 1; + ff = procfile_open(nf_conntrack_filename, " \t:", PROCFILE_FLAG_DEFAULT); + if(!ff) read_full = 0; - unsigned long long aentries = 0, asearched = 0, afound = 0, anew = 0, ainvalid = 0, aignore = 0, adelete = 0, adelete_list = 0, - ainsert = 0, ainsert_failed = 0, adrop = 0, aearly_drop = 0, aicmp_error = 0, aexpect_new = 0, aexpect_create = 0, aexpect_delete = 0, asearch_restart = 0; + do_new = config_get_boolean("plugin:proc:/proc/net/stat/nf_conntrack", "netfilter new connections", read_full); + do_changes = config_get_boolean("plugin:proc:/proc/net/stat/nf_conntrack", "netfilter connection changes", read_full); + do_expect = config_get_boolean("plugin:proc:/proc/net/stat/nf_conntrack", "netfilter connection expectations", read_full); + do_search = config_get_boolean("plugin:proc:/proc/net/stat/nf_conntrack", "netfilter connection searches", read_full); + do_errors = config_get_boolean("plugin:proc:/proc/net/stat/nf_conntrack", "netfilter errors", read_full); + + do_sockets = 1; + if(!read_full) { + snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/sys/net/netfilter/nf_conntrack_count"); + nf_conntrack_count_filename = config_get("plugin:proc:/proc/sys/net/netfilter/nf_conntrack_count", "filename to monitor", filename); + + if(read_single_number_file(nf_conntrack_count_filename, &aentries)) + do_sockets = 0; + } + + do_sockets = config_get_boolean("plugin:proc:/proc/net/stat/nf_conntrack", "netfilter connections", do_sockets); - for(l = 1; l < lines ;l++) { - words = procfile_linewords(ff, l); - if(words < 17) { - if(words) error("Cannot read /proc/net/stat/nf_conntrack line. Expected 17 params, read %u.", words); - continue; + if(!do_sockets && !read_full) + return 1; + + rrdvar_max = rrdvar_custom_host_variable_create(&localhost, "netfilter.conntrack.max"); + } + + if(likely(read_full)) { + if(unlikely(!ff)) { + ff = procfile_open(nf_conntrack_filename, " \t:", PROCFILE_FLAG_DEFAULT); + if(unlikely(!ff)) + return 0; // we return 0, so that we will retry to open it next time + } + + ff = procfile_readall(ff); + if(unlikely(!ff)) + return 0; // we return 0, so that we will retry to open it next time + + size_t lines = procfile_lines(ff), l; + + for(l = 1; l < lines ;l++) { + size_t words = procfile_linewords(ff, l); + if(unlikely(words < 17)) { + if(unlikely(words)) error("Cannot read /proc/net/stat/nf_conntrack line. Expected 17 params, read %zu.", words); + continue; + } + + unsigned long long tentries = 0, tsearched = 0, tfound = 0, tnew = 0, tinvalid = 0, tignore = 0, tdelete = 0, tdelete_list = 0, tinsert = 0, tinsert_failed = 0, tdrop = 0, tearly_drop = 0, ticmp_error = 0, texpect_new = 0, texpect_create = 0, texpect_delete = 0, tsearch_restart = 0; + + tentries = strtoull(procfile_lineword(ff, l, 0), NULL, 16); + tsearched = strtoull(procfile_lineword(ff, l, 1), NULL, 16); + tfound = strtoull(procfile_lineword(ff, l, 2), NULL, 16); + tnew = strtoull(procfile_lineword(ff, l, 3), NULL, 16); + tinvalid = strtoull(procfile_lineword(ff, l, 4), NULL, 16); + tignore = strtoull(procfile_lineword(ff, l, 5), NULL, 16); + tdelete = strtoull(procfile_lineword(ff, l, 6), NULL, 16); + tdelete_list = strtoull(procfile_lineword(ff, l, 7), NULL, 16); + tinsert = strtoull(procfile_lineword(ff, l, 8), NULL, 16); + tinsert_failed = strtoull(procfile_lineword(ff, l, 9), NULL, 16); + tdrop = strtoull(procfile_lineword(ff, l, 10), NULL, 16); + tearly_drop = strtoull(procfile_lineword(ff, l, 11), NULL, 16); + ticmp_error = strtoull(procfile_lineword(ff, l, 12), NULL, 16); + texpect_new = strtoull(procfile_lineword(ff, l, 13), NULL, 16); + texpect_create = strtoull(procfile_lineword(ff, l, 14), NULL, 16); + texpect_delete = strtoull(procfile_lineword(ff, l, 15), NULL, 16); + tsearch_restart = strtoull(procfile_lineword(ff, l, 16), NULL, 16); + + if(unlikely(!aentries)) aentries = tentries; + + // sum all the cpus together + asearched += tsearched; // conntrack.search + afound += tfound; // conntrack.search + anew += tnew; // conntrack.new + ainvalid += tinvalid; // conntrack.new + aignore += tignore; // conntrack.new + adelete += tdelete; // conntrack.changes + adelete_list += tdelete_list; // conntrack.changes + ainsert += tinsert; // conntrack.changes + ainsert_failed += tinsert_failed; // conntrack.errors + adrop += tdrop; // conntrack.errors + aearly_drop += tearly_drop; // conntrack.errors + aicmp_error += ticmp_error; // conntrack.errors + aexpect_new += texpect_new; // conntrack.expect + aexpect_create += texpect_create; // conntrack.expect + aexpect_delete += texpect_delete; // conntrack.expect + asearch_restart += tsearch_restart; // conntrack.search } + } + else { + if(unlikely(read_single_number_file(nf_conntrack_count_filename, &aentries))) + return 0; // we return 0, so that we will retry to open it next time + } + + usec_since_last_max += dt; + if(unlikely(rrdvar_max && usec_since_last_max >= get_max_every)) { + usec_since_last_max = 0; - unsigned long long tentries = 0, tsearched = 0, tfound = 0, tnew = 0, tinvalid = 0, tignore = 0, tdelete = 0, tdelete_list = 0, tinsert = 0, tinsert_failed = 0, tdrop = 0, tearly_drop = 0, ticmp_error = 0, texpect_new = 0, texpect_create = 0, texpect_delete = 0, tsearch_restart = 0; - - tentries = strtoull(procfile_lineword(ff, l, 0), NULL, 16); - tsearched = strtoull(procfile_lineword(ff, l, 1), NULL, 16); - tfound = strtoull(procfile_lineword(ff, l, 2), NULL, 16); - tnew = strtoull(procfile_lineword(ff, l, 3), NULL, 16); - tinvalid = strtoull(procfile_lineword(ff, l, 4), NULL, 16); - tignore = strtoull(procfile_lineword(ff, l, 5), NULL, 16); - tdelete = strtoull(procfile_lineword(ff, l, 6), NULL, 16); - tdelete_list = strtoull(procfile_lineword(ff, l, 7), NULL, 16); - tinsert = strtoull(procfile_lineword(ff, l, 8), NULL, 16); - tinsert_failed = strtoull(procfile_lineword(ff, l, 9), NULL, 16); - tdrop = strtoull(procfile_lineword(ff, l, 10), NULL, 16); - tearly_drop = strtoull(procfile_lineword(ff, l, 11), NULL, 16); - ticmp_error = strtoull(procfile_lineword(ff, l, 12), NULL, 16); - texpect_new = strtoull(procfile_lineword(ff, l, 13), NULL, 16); - texpect_create = strtoull(procfile_lineword(ff, l, 14), NULL, 16); - texpect_delete = strtoull(procfile_lineword(ff, l, 15), NULL, 16); - tsearch_restart = strtoull(procfile_lineword(ff, l, 16), NULL, 16); - - if(!aentries) aentries = tentries; - - // sum all the cpus together - asearched += tsearched; // conntrack.search - afound += tfound; // conntrack.search - anew += tnew; // conntrack.new - ainvalid += tinvalid; // conntrack.new - aignore += tignore; // conntrack.new - adelete += tdelete; // conntrack.changes - adelete_list += tdelete_list; // conntrack.changes - ainsert += tinsert; // conntrack.changes - ainsert_failed += tinsert_failed; // conntrack.errors - adrop += tdrop; // conntrack.errors - aearly_drop += tearly_drop; // conntrack.errors - aicmp_error += ticmp_error; // conntrack.errors - aexpect_new += texpect_new; // conntrack.expect - aexpect_create += texpect_create; // conntrack.expect - aexpect_delete += texpect_delete; // conntrack.expect - asearch_restart += tsearch_restart; // conntrack.search + unsigned long long max; + if(likely(!read_single_number_file(nf_conntrack_max_filename, &max))) + rrdvar_custom_host_variable_set(rrdvar_max, max); } RRDSET *st; @@ -87,7 +131,7 @@ int do_proc_net_stat_conntrack(int update_every, unsigned long long dt) { if(do_sockets) { st = rrdset_find(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_CONNTRACK "_sockets"); - if(!st) { + if(unlikely(!st)) { st = rrdset_create(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_CONNTRACK "_sockets", NULL, RRD_TYPE_NET_STAT_CONNTRACK, NULL, "Connection Tracker Connections", "active connections", 3000, update_every, RRDSET_TYPE_LINE); rrddim_add(st, "connections", NULL, 1, 1, RRDDIM_ABSOLUTE); @@ -102,7 +146,7 @@ int do_proc_net_stat_conntrack(int update_every, unsigned long long dt) { if(do_new) { st = rrdset_find(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_CONNTRACK "_new"); - if(!st) { + if(unlikely(!st)) { st = rrdset_create(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_CONNTRACK "_new", NULL, RRD_TYPE_NET_STAT_CONNTRACK, NULL, "Connection Tracker New Connections", "connections/s", 3001, update_every, RRDSET_TYPE_LINE); rrddim_add(st, "new", NULL, 1, 1, RRDDIM_INCREMENTAL); @@ -121,7 +165,7 @@ int do_proc_net_stat_conntrack(int update_every, unsigned long long dt) { if(do_changes) { st = rrdset_find(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_CONNTRACK "_changes"); - if(!st) { + if(unlikely(!st)) { st = rrdset_create(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_CONNTRACK "_changes", NULL, RRD_TYPE_NET_STAT_CONNTRACK, NULL, "Connection Tracker Changes", "changes/s", 3002, update_every, RRDSET_TYPE_LINE); st->isdetail = 1; @@ -141,7 +185,7 @@ int do_proc_net_stat_conntrack(int update_every, unsigned long long dt) { if(do_expect) { st = rrdset_find(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_CONNTRACK "_expect"); - if(!st) { + if(unlikely(!st)) { st = rrdset_create(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_CONNTRACK "_expect", NULL, RRD_TYPE_NET_STAT_CONNTRACK, NULL, "Connection Tracker Expectations", "expectations/s", 3003, update_every, RRDSET_TYPE_LINE); st->isdetail = 1; @@ -161,7 +205,7 @@ int do_proc_net_stat_conntrack(int update_every, unsigned long long dt) { if(do_search) { st = rrdset_find(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_CONNTRACK "_search"); - if(!st) { + if(unlikely(!st)) { st = rrdset_create(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_CONNTRACK "_search", NULL, RRD_TYPE_NET_STAT_CONNTRACK, NULL, "Connection Tracker Searches", "searches/s", 3010, update_every, RRDSET_TYPE_LINE); st->isdetail = 1; @@ -181,7 +225,7 @@ int do_proc_net_stat_conntrack(int update_every, unsigned long long dt) { if(do_errors) { st = rrdset_find(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_CONNTRACK "_errors"); - if(!st) { + if(unlikely(!st)) { st = rrdset_create(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_CONNTRACK "_errors", NULL, RRD_TYPE_NET_STAT_CONNTRACK, NULL, "Connection Tracker Errors", "events/s", 3005, update_every, RRDSET_TYPE_LINE); st->isdetail = 1; diff --git a/src/proc_net_stat_synproxy.c b/src/proc_net_stat_synproxy.c index 102805f70..6bb0a3c69 100644 --- a/src/proc_net_stat_synproxy.c +++ b/src/proc_net_stat_synproxy.c @@ -2,32 +2,35 @@ #define RRD_TYPE_NET_STAT_NETFILTER "netfilter" #define RRD_TYPE_NET_STAT_SYNPROXY "synproxy" -#define RRD_TYPE_NET_STAT_SYNPROXY_LEN strlen(RRD_TYPE_NET_STAT_SYNPROXY) -int do_proc_net_stat_synproxy(int update_every, unsigned long long dt) { +int do_proc_net_stat_synproxy(int update_every, usec_t dt) { + (void)dt; + static int do_entries = -1, do_cookies = -1, do_syns = -1, do_reopened = -1; static procfile *ff = NULL; - if(do_entries == -1) do_entries = config_get_boolean_ondemand("plugin:proc:/proc/net/stat/synproxy", "SYNPROXY entries", CONFIG_ONDEMAND_ONDEMAND); - if(do_cookies == -1) do_cookies = config_get_boolean_ondemand("plugin:proc:/proc/net/stat/synproxy", "SYNPROXY cookies", CONFIG_ONDEMAND_ONDEMAND); - if(do_syns == -1) do_syns = config_get_boolean_ondemand("plugin:proc:/proc/net/stat/synproxy", "SYNPROXY SYN received", CONFIG_ONDEMAND_ONDEMAND); - if(do_reopened == -1) do_reopened = config_get_boolean_ondemand("plugin:proc:/proc/net/stat/synproxy", "SYNPROXY connections reopened", CONFIG_ONDEMAND_ONDEMAND); - - if(dt) {}; + if(unlikely(do_entries == -1)) { + do_entries = config_get_boolean_ondemand("plugin:proc:/proc/net/stat/synproxy", "SYNPROXY entries", CONFIG_ONDEMAND_ONDEMAND); + do_cookies = config_get_boolean_ondemand("plugin:proc:/proc/net/stat/synproxy", "SYNPROXY cookies", CONFIG_ONDEMAND_ONDEMAND); + do_syns = config_get_boolean_ondemand("plugin:proc:/proc/net/stat/synproxy", "SYNPROXY SYN received", CONFIG_ONDEMAND_ONDEMAND); + do_reopened = config_get_boolean_ondemand("plugin:proc:/proc/net/stat/synproxy", "SYNPROXY connections reopened", CONFIG_ONDEMAND_ONDEMAND); + } - if(!ff) { + if(unlikely(!ff)) { char filename[FILENAME_MAX + 1]; snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/net/stat/synproxy"); ff = procfile_open(config_get("plugin:proc:/proc/net/stat/synproxy", "filename to monitor", filename), " \t,:|", PROCFILE_FLAG_DEFAULT); + if(unlikely(!ff)) + return 1; } - if(!ff) return 1; ff = procfile_readall(ff); - if(!ff) return 0; // we return 0, so that we will retry to open it next time + if(unlikely(!ff)) + return 0; // we return 0, so that we will retry to open it next time // make sure we have 3 lines size_t lines = procfile_lines(ff), l; - if(lines < 2) { + if(unlikely(lines < 2)) { error("/proc/net/stat/synproxy has %zu lines, expected no less than 2. Disabling it.", lines); return 1; } @@ -36,8 +39,9 @@ int do_proc_net_stat_synproxy(int update_every, unsigned long long dt) { // synproxy gives its values per CPU for(l = 1; l < lines ;l++) { - int words = procfile_linewords(ff, l); - if(words < 6) continue; + size_t words = procfile_linewords(ff, l); + if(unlikely(words < 6)) + continue; entries += strtoull(procfile_lineword(ff, l, 0), NULL, 16); syn_received += strtoull(procfile_lineword(ff, l, 1), NULL, 16); @@ -57,7 +61,7 @@ int do_proc_net_stat_synproxy(int update_every, unsigned long long dt) { do_entries = CONFIG_ONDEMAND_YES; st = rrdset_find(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_SYNPROXY "_entries"); - if(!st) { + if(unlikely(!st)) { st = rrdset_create(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_SYNPROXY "_entries", NULL, RRD_TYPE_NET_STAT_SYNPROXY, NULL, "SYNPROXY Entries Used", "entries", 3304, update_every, RRDSET_TYPE_LINE); rrddim_add(st, "entries", NULL, 1, 1, RRDDIM_ABSOLUTE); @@ -74,7 +78,7 @@ int do_proc_net_stat_synproxy(int update_every, unsigned long long dt) { do_syns = CONFIG_ONDEMAND_YES; st = rrdset_find(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_SYNPROXY "_syn_received"); - if(!st) { + if(unlikely(!st)) { st = rrdset_create(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_SYNPROXY "_syn_received", NULL, RRD_TYPE_NET_STAT_SYNPROXY, NULL, "SYNPROXY SYN Packets received", "SYN/s", 3301, update_every, RRDSET_TYPE_LINE); rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL); @@ -91,7 +95,7 @@ int do_proc_net_stat_synproxy(int update_every, unsigned long long dt) { do_reopened = CONFIG_ONDEMAND_YES; st = rrdset_find(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_SYNPROXY "_conn_reopened"); - if(!st) { + if(unlikely(!st)) { st = rrdset_create(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_SYNPROXY "_conn_reopened", NULL, RRD_TYPE_NET_STAT_SYNPROXY, NULL, "SYNPROXY Connections Reopened", "connections/s", 3303, update_every, RRDSET_TYPE_LINE); rrddim_add(st, "reopened", NULL, 1, 1, RRDDIM_INCREMENTAL); @@ -108,7 +112,7 @@ int do_proc_net_stat_synproxy(int update_every, unsigned long long dt) { do_cookies = CONFIG_ONDEMAND_YES; st = rrdset_find(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_SYNPROXY "_cookies"); - if(!st) { + if(unlikely(!st)) { st = rrdset_create(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_SYNPROXY "_cookies", NULL, RRD_TYPE_NET_STAT_SYNPROXY, NULL, "SYNPROXY TCP Cookies", "cookies/s", 3302, update_every, RRDSET_TYPE_LINE); rrddim_add(st, "valid", NULL, 1, 1, RRDDIM_INCREMENTAL); diff --git a/src/proc_self_mountinfo.c b/src/proc_self_mountinfo.c index 51aea7aee..d07f22510 100644 --- a/src/proc_self_mountinfo.c +++ b/src/proc_self_mountinfo.c @@ -1,12 +1,55 @@ #include "common.h" +// ---------------------------------------------------------------------------- +// taken from gnulib/mountlist.c + +#ifndef ME_REMOTE +/* A file system is "remote" if its Fs_name contains a ':' + or if (it is of type (smbfs or cifs) and its Fs_name starts with '//') + or Fs_name is equal to "-hosts" (used by autofs to mount remote fs). */ +# define ME_REMOTE(Fs_name, Fs_type) \ + (strchr (Fs_name, ':') != NULL \ + || ((Fs_name)[0] == '/' \ + && (Fs_name)[1] == '/' \ + && (strcmp (Fs_type, "smbfs") == 0 \ + || strcmp (Fs_type, "cifs") == 0)) \ + || (strcmp("-hosts", Fs_name) == 0)) +#endif + +#define ME_DUMMY_0(Fs_name, Fs_type) \ + (strcmp (Fs_type, "autofs") == 0 \ + || strcmp (Fs_type, "proc") == 0 \ + || strcmp (Fs_type, "subfs") == 0 \ + /* for Linux 2.6/3.x */ \ + || strcmp (Fs_type, "debugfs") == 0 \ + || strcmp (Fs_type, "devpts") == 0 \ + || strcmp (Fs_type, "fusectl") == 0 \ + || strcmp (Fs_type, "mqueue") == 0 \ + || strcmp (Fs_type, "rpc_pipefs") == 0 \ + || strcmp (Fs_type, "sysfs") == 0 \ + /* FreeBSD, Linux 2.4 */ \ + || strcmp (Fs_type, "devfs") == 0 \ + /* for NetBSD 3.0 */ \ + || strcmp (Fs_type, "kernfs") == 0 \ + /* for Irix 6.5 */ \ + || strcmp (Fs_type, "ignore") == 0) + +/* Historically, we have marked as "dummy" any file system of type "none", + but now that programs like du need to know about bind-mounted directories, + we grant an exception to any with "bind" in its list of mount options. + I.e., those are *not* dummy entries. */ +# define ME_DUMMY(Fs_name, Fs_type) \ + (ME_DUMMY_0 (Fs_name, Fs_type) || strcmp (Fs_type, "none") == 0) + +// ---------------------------------------------------------------------------- + // find the mount info with the given major:minor // in the supplied linked list of mountinfo structures struct mountinfo *mountinfo_find(struct mountinfo *root, unsigned long major, unsigned long minor) { struct mountinfo *mi; for(mi = root; mi ; mi = mi->next) - if(mi->major == major && mi->minor == minor) + if(unlikely(mi->major == major && mi->minor == minor)) return mi; return NULL; @@ -19,12 +62,12 @@ struct mountinfo *mountinfo_find_by_filesystem_mount_source(struct mountinfo *ro uint32_t filesystem_hash = simple_hash(filesystem), mount_source_hash = simple_hash(mount_source); for(mi = root; mi ; mi = mi->next) - if(mi->filesystem + if(unlikely(mi->filesystem && mi->mount_source && mi->filesystem_hash == filesystem_hash && mi->mount_source_hash == mount_source_hash && !strcmp(mi->filesystem, filesystem) - && !strcmp(mi->mount_source, mount_source)) + && !strcmp(mi->mount_source, mount_source))) return mi; return NULL; @@ -37,10 +80,10 @@ struct mountinfo *mountinfo_find_by_filesystem_super_option(struct mountinfo *ro size_t solen = strlen(super_options); for(mi = root; mi ; mi = mi->next) - if(mi->filesystem + if(unlikely(mi->filesystem && mi->super_options && mi->filesystem_hash == filesystem_hash - && !strcmp(mi->filesystem, filesystem)) { + && !strcmp(mi->filesystem, filesystem))) { // super_options is a comma separated list char *s = mi->super_options, *e; @@ -49,7 +92,7 @@ struct mountinfo *mountinfo_find_by_filesystem_super_option(struct mountinfo *ro while(*e && *e != ',') e++; size_t len = e - s; - if(len == solen && !strncmp(s, super_options, len)) + if(unlikely(len == solen && !strncmp(s, super_options, len))) return mi; if(*e == ',') s = ++e; @@ -72,6 +115,7 @@ void mountinfo_free(struct mountinfo *mi) { freez(mi->root); freez(mi->mount_point); freez(mi->mount_options); + freez(mi->persistent_id); /* if(mi->optional_fields_count) { @@ -113,56 +157,65 @@ static char *strdupz_decoding_octal(const char *string) { return buffer; } -// read the whole mountinfo into a linked list -struct mountinfo *mountinfo_read() { - procfile *ff = NULL; +static inline int is_read_only(const char *s) { + if(!s) return 0; + + size_t len = strlen(s); + if(len < 2) return 0; + if(len == 2) { + if(!strcmp(s, "ro")) return 1; + return 0; + } + if(!strncmp(s, "ro,", 3)) return 1; + if(!strncmp(&s[len - 3], ",ro", 3)) return 1; + if(strstr(s, ",ro,")) return 1; + return 0; +} +// read the whole mountinfo into a linked list +struct mountinfo *mountinfo_read(int do_statvfs) { char filename[FILENAME_MAX + 1]; snprintfz(filename, FILENAME_MAX, "%s/proc/self/mountinfo", global_host_prefix); - ff = procfile_open(filename, " \t", PROCFILE_FLAG_DEFAULT); - if(!ff) { + procfile *ff = procfile_open(filename, " \t", PROCFILE_FLAG_DEFAULT); + if(unlikely(!ff)) { snprintfz(filename, FILENAME_MAX, "%s/proc/1/mountinfo", global_host_prefix); ff = procfile_open(filename, " \t", PROCFILE_FLAG_DEFAULT); - if(!ff) return NULL; + if(unlikely(!ff)) return NULL; } ff = procfile_readall(ff); - if(!ff) return NULL; + if(unlikely(!ff)) + return NULL; struct mountinfo *root = NULL, *last = NULL, *mi = NULL; unsigned long l, lines = procfile_lines(ff); for(l = 0; l < lines ;l++) { - if(procfile_linewords(ff, l) < 5) + if(unlikely(procfile_linewords(ff, l) < 5)) continue; mi = mallocz(sizeof(struct mountinfo)); - if(unlikely(!root)) - root = last = mi; - else - last->next = mi; - - last = mi; - mi->next = NULL; - unsigned long w = 0; - mi->id = strtoul(procfile_lineword(ff, l, w), NULL, 10); w++; - mi->parentid = strtoul(procfile_lineword(ff, l, w), NULL, 10); w++; + mi->id = str2ul(procfile_lineword(ff, l, w)); w++; + mi->parentid = str2ul(procfile_lineword(ff, l, w)); w++; char *major = procfile_lineword(ff, l, w), *minor; w++; for(minor = major; *minor && *minor != ':' ;minor++) ; - if(!*minor) { + if(unlikely(!*minor)) { error("Cannot parse major:minor on '%s' at line %lu of '%s'", major, l + 1, filename); + freez(mi); continue; } *minor = '\0'; minor++; - mi->major = strtoul(major, NULL, 10); - mi->minor = strtoul(minor, NULL, 10); + mi->flags = 0; + + mi->major = str2ul(major); + mi->minor = str2ul(minor); mi->root = strdupz(procfile_lineword(ff, l, w)); w++; mi->root_hash = simple_hash(mi->root); @@ -170,8 +223,15 @@ struct mountinfo *mountinfo_read() { mi->mount_point = strdupz_decoding_octal(procfile_lineword(ff, l, w)); w++; mi->mount_point_hash = simple_hash(mi->mount_point); + mi->persistent_id = strdupz(mi->mount_point); + netdata_fix_chart_id(mi->persistent_id); + mi->persistent_id_hash = simple_hash(mi->persistent_id); + mi->mount_options = strdupz(procfile_lineword(ff, l, w)); w++; + if(unlikely(is_read_only(mi->mount_options))) + mi->flags |= MOUNTINFO_READONLY; + // count the optional fields /* unsigned long wo = w; @@ -189,14 +249,11 @@ struct mountinfo *mountinfo_read() { // we have some optional fields // read them into a new array of pointers; - mi->optional_fields = malloc(mi->optional_fields_count * sizeof(char *)); - if(unlikely(!mi->optional_fields)) - fatal("Cannot allocate memory for %d mountinfo optional fields", mi->optional_fields_count); + mi->optional_fields = mallocz(mi->optional_fields_count * sizeof(char *)); int i; for(i = 0; i < mi->optional_fields_count ; i++) { - *mi->optional_fields[wo] = strdup(procfile_lineword(ff, l, w)); - if(!mi->optional_fields[wo]) fatal("Cannot allocate memory"); + *mi->optional_fields[wo] = strdupz(procfile_lineword(ff, l, w)); wo++; } } @@ -210,33 +267,134 @@ struct mountinfo *mountinfo_read() { mi->filesystem = strdupz(procfile_lineword(ff, l, w)); w++; mi->filesystem_hash = simple_hash(mi->filesystem); - mi->mount_source = strdupz(procfile_lineword(ff, l, w)); w++; + mi->mount_source = strdupz_decoding_octal(procfile_lineword(ff, l, w)); w++; mi->mount_source_hash = simple_hash(mi->mount_source); mi->super_options = strdupz(procfile_lineword(ff, l, w)); w++; + + if(unlikely(is_read_only(mi->super_options))) + mi->flags |= MOUNTINFO_READONLY; + + if(unlikely(ME_DUMMY(mi->mount_source, mi->filesystem))) + mi->flags |= MOUNTINFO_IS_DUMMY; + + if(unlikely(ME_REMOTE(mi->mount_source, mi->filesystem))) + mi->flags |= MOUNTINFO_IS_REMOTE; + + // mark as BIND the duplicates (i.e. same filesystem + same source) + if(do_statvfs) { + struct stat buf; + if(unlikely(stat(mi->mount_point, &buf) == -1)) { + mi->st_dev = 0; + mi->flags |= MOUNTINFO_NO_STAT; + } + else { + mi->st_dev = buf.st_dev; + + struct mountinfo *mt; + for(mt = root; mt; mt = mt->next) { + if(unlikely(mt->st_dev == mi->st_dev && !(mi->flags & MOUNTINFO_NO_STAT))) { + if(strlen(mi->mount_point) < strlen(mt->mount_point)) + mt->flags |= MOUNTINFO_IS_SAME_DEV; + else + mi->flags |= MOUNTINFO_IS_SAME_DEV; + } + } + } + } + else { + mi->st_dev = 0; + } } else { mi->filesystem = NULL; + mi->filesystem_hash = 0; + mi->mount_source = NULL; + mi->mount_source_hash = 0; + mi->super_options = NULL; + + mi->st_dev = 0; + } + + // check if it has size + if(do_statvfs) { + struct statvfs buff_statvfs; + if(unlikely(statvfs(mi->mount_point, &buff_statvfs) < 0)) { + mi->flags |= MOUNTINFO_NO_STAT; + } + else if(unlikely(!buff_statvfs.f_blocks /* || !buff_statvfs.f_files */)) { + mi->flags |= MOUNTINFO_NO_SIZE; + } } + // link it + if(unlikely(!root)) + root = mi; + else + last->next = mi; + + last = mi; + mi->next = NULL; + /* - info("MOUNTINFO: %u %u %u:%u root '%s', mount point '%s', mount options '%s', filesystem '%s', mount source '%s', super options '%s'", +#ifdef NETDATA_INTERNAL_CHECKS + fprintf(stderr, "MOUNTINFO: %ld %ld %lu:%lu root '%s', persistent id '%s', mount point '%s', mount options '%s', filesystem '%s', mount source '%s', super options '%s'%s%s%s%s%s%s\n", mi->id, mi->parentid, mi->major, mi->minor, mi->root, - mi->mount_point, - mi->mount_options, - mi->filesystem, - mi->mount_source, - mi->super_options + mi->persistent_id, + (mi->mount_point)?mi->mount_point:"", + (mi->mount_options)?mi->mount_options:"", + (mi->filesystem)?mi->filesystem:"", + (mi->mount_source)?mi->mount_source:"", + (mi->super_options)?mi->super_options:"", + (mi->flags & MOUNTINFO_IS_DUMMY)?" DUMMY":"", + (mi->flags & MOUNTINFO_IS_BIND)?" BIND":"", + (mi->flags & MOUNTINFO_IS_REMOTE)?" REMOTE":"", + (mi->flags & MOUNTINFO_NO_STAT)?" NOSTAT":"", + (mi->flags & MOUNTINFO_NO_SIZE)?" NOSIZE":"", + (mi->flags & MOUNTINFO_IS_SAME_DEV)?" SAMEDEV":"" ); +#endif */ } +/* find if the mount options have "bind" in them + { + FILE *fp = setmntent(MOUNTED, "r"); + if (fp != NULL) { + struct mntent mntbuf; + struct mntent *mnt; + char buf[4096 + 1]; + + while ((mnt = getmntent_r(fp, &mntbuf, buf, 4096))) { + char *bind = hasmntopt(mnt, "bind"); + if(unlikely(bind)) { + struct mountinfo *mi; + for(mi = root; mi ; mi = mi->next) { + if(unlikely(strcmp(mnt->mnt_dir, mi->mount_point) == 0)) { + fprintf(stderr, "Mount point '%s' is BIND\n", mi->mount_point); + mi->flags |= MOUNTINFO_IS_BIND; + break; + } + } + +#ifdef NETDATA_INTERNAL_CHECKS + if(unlikely(!mi)) { + error("Mount point '%s' not found in /proc/self/mountinfo", mnt->mnt_dir); + } +#endif + } + } + endmntent(fp); + } + } +*/ + procfile_close(ff); return root; } diff --git a/src/proc_self_mountinfo.h b/src/proc_self_mountinfo.h index c2d9688c1..00cf699ab 100644 --- a/src/proc_self_mountinfo.h +++ b/src/proc_self_mountinfo.h @@ -1,12 +1,23 @@ #ifndef NETDATA_PROC_SELF_MOUNTINFO_H #define NETDATA_PROC_SELF_MOUNTINFO_H 1 +#define MOUNTINFO_IS_DUMMY 0x00000001 +#define MOUNTINFO_IS_REMOTE 0x00000002 +#define MOUNTINFO_IS_BIND 0x00000004 +#define MOUNTINFO_IS_SAME_DEV 0x00000008 +#define MOUNTINFO_NO_STAT 0x00000010 +#define MOUNTINFO_NO_SIZE 0x00000020 +#define MOUNTINFO_READONLY 0x00000040 + struct mountinfo { long id; // mount ID: unique identifier of the mount (may be reused after umount(2)). long parentid; // parent ID: ID of parent mount (or of self for the top of the mount tree). unsigned long major; // major:minor: value of st_dev for files on filesystem (see stat(2)). unsigned long minor; + char *persistent_id; // a calculated persistent id for the mount point + uint32_t persistent_id_hash; + char *root; // root: root of the mount within the filesystem. uint32_t root_hash; @@ -27,6 +38,10 @@ struct mountinfo { char *super_options; // super options: per-superblock options. + uint32_t flags; + + dev_t st_dev; // id of device as given by stat() + struct mountinfo *next; }; @@ -35,6 +50,6 @@ extern struct mountinfo *mountinfo_find_by_filesystem_mount_source(struct mounti extern struct mountinfo *mountinfo_find_by_filesystem_super_option(struct mountinfo *root, const char *filesystem, const char *super_options); extern void mountinfo_free(struct mountinfo *mi); -extern struct mountinfo *mountinfo_read(); +extern struct mountinfo *mountinfo_read(int do_statvfs); #endif /* NETDATA_PROC_SELF_MOUNTINFO_H */
\ No newline at end of file diff --git a/src/proc_softirqs.c b/src/proc_softirqs.c index ebbbf2aeb..c7b10d70d 100644 --- a/src/proc_softirqs.c +++ b/src/proc_softirqs.c @@ -2,71 +2,89 @@ #define MAX_INTERRUPT_NAME 50 +struct cpu_interrupt { + unsigned long long value; + RRDDIM *rd; +}; + struct interrupt { int used; char *id; char name[MAX_INTERRUPT_NAME + 1]; + RRDDIM *rd; unsigned long long total; - unsigned long long value[]; + struct cpu_interrupt cpu[]; }; // since each interrupt is variable in size // we use this to calculate its record size -#define recordsize(cpus) (sizeof(struct interrupt) + (cpus * sizeof(unsigned long long))) +#define recordsize(cpus) (sizeof(struct interrupt) + (cpus * sizeof(struct cpu_interrupt))) // given a base, get a pointer to each record #define irrindex(base, line, cpus) ((struct interrupt *)&((char *)(base))[line * recordsize(cpus)]) -static inline struct interrupt *get_interrupts_array(int lines, int cpus) { +static inline struct interrupt *get_interrupts_array(size_t lines, int cpus) { static struct interrupt *irrs = NULL; - static int allocated = 0; + static size_t allocated = 0; + + if(unlikely(lines != allocated)) { + uint32_t l; + int c; - if(lines > allocated) { irrs = (struct interrupt *)reallocz(irrs, lines * recordsize(cpus)); + + // reset all interrupt RRDDIM pointers as any line could have shifted + for(l = 0; l < lines ;l++) { + struct interrupt *irr = irrindex(irrs, l, cpus); + irr->rd = NULL; + irr->name[0] = '\0'; + for(c = 0; c < cpus ;c++) + irr->cpu[c].rd = NULL; + } + allocated = lines; } return irrs; } -int do_proc_softirqs(int update_every, unsigned long long dt) { +int do_proc_softirqs(int update_every, usec_t dt) { + (void)dt; static procfile *ff = NULL; static int cpus = -1, do_per_core = -1; - struct interrupt *irrs = NULL; - if(dt) {}; + if(unlikely(do_per_core == -1)) do_per_core = config_get_boolean("plugin:proc:/proc/softirqs", "interrupts per core", 1); - if(do_per_core == -1) do_per_core = config_get_boolean("plugin:proc:/proc/softirqs", "interrupts per core", 1); - - if(!ff) { + if(unlikely(!ff)) { char filename[FILENAME_MAX + 1]; snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/softirqs"); ff = procfile_open(config_get("plugin:proc:/proc/softirqs", "filename to monitor", filename), " \t", PROCFILE_FLAG_DEFAULT); + if(unlikely(!ff)) return 1; } - if(!ff) return 1; ff = procfile_readall(ff); - if(!ff) return 0; // we return 0, so that we will retry to open it next time + if(unlikely(!ff)) return 0; // we return 0, so that we will retry to open it next time - uint32_t lines = procfile_lines(ff), l; - uint32_t words = procfile_linewords(ff, 0), w; + size_t lines = procfile_lines(ff), l; + size_t words = procfile_linewords(ff, 0); - if(!lines) { + if(unlikely(!lines)) { error("Cannot read /proc/softirqs, zero lines reported."); return 1; } // find how many CPUs are there - if(cpus == -1) { + if(unlikely(cpus == -1)) { + uint32_t w; cpus = 0; for(w = 0; w < words ; w++) { - if(strncmp(procfile_lineword(ff, 0, w), "CPU", 3) == 0) + if(likely(strncmp(procfile_lineword(ff, 0, w), "CPU", 3) == 0)) cpus++; } } - if(!cpus) { + if(unlikely(!cpus)) { error("PLUGIN: PROC_SOFTIRQS: Cannot find the number of CPUs in /proc/softirqs"); return 1; } @@ -82,23 +100,23 @@ int do_proc_softirqs(int update_every, unsigned long long dt) { irr->total = 0; words = procfile_linewords(ff, l); - if(!words) continue; + if(unlikely(!words)) continue; irr->id = procfile_lineword(ff, l, 0); - if(!irr->id || !irr->id[0]) continue; + if(unlikely(!irr->id || !irr->id[0])) continue; - int idlen = strlen(irr->id); - if(irr->id[idlen - 1] == ':') + size_t idlen = strlen(irr->id); + if(unlikely(idlen && irr->id[idlen - 1] == ':')) irr->id[idlen - 1] = '\0'; int c; for(c = 0; c < cpus ;c++) { - if((c + 1) < (int)words) - irr->value[c] = strtoull(procfile_lineword(ff, l, (uint32_t)(c + 1)), NULL, 10); + if(likely((c + 1) < (int)words)) + irr->cpu[c].value = str2ull(procfile_lineword(ff, l, (uint32_t)(c + 1))); else - irr->value[c] = 0; + irr->cpu[c].value = 0; - irr->total += irr->value[c]; + irr->total += irr->cpu[c].value; } strncpyz(irr->name, irr->id, MAX_INTERRUPT_NAME); @@ -111,21 +129,30 @@ int do_proc_softirqs(int update_every, unsigned long long dt) { // -------------------------------------------------------------------- st = rrdset_find_bytype("system", "softirqs"); - if(!st) { - st = rrdset_create("system", "softirqs", NULL, "softirqs", NULL, "System softirqs", "softirqs/s", 950, update_every, RRDSET_TYPE_STACKED); - - for(l = 0; l < lines ;l++) { - struct interrupt *irr = irrindex(irrs, l, cpus); - if(!irr->used) continue; - rrddim_add(st, irr->id, irr->name, 1, 1, RRDDIM_INCREMENTAL); - } - } + if(unlikely(!st)) st = rrdset_create("system", "softirqs", NULL, "softirqs", NULL, "System softirqs", "softirqs/s", 950, update_every, RRDSET_TYPE_STACKED); else rrdset_next(st); for(l = 0; l < lines ;l++) { struct interrupt *irr = irrindex(irrs, l, cpus); - if(!irr->used) continue; - rrddim_set(st, irr->id, irr->total); + if(unlikely(!irr->used)) continue; + // some interrupt may have changed without changing the total number of lines + // if the same number of interrupts have been added and removed between two + // calls of this function. + if(unlikely(!irr->rd || strncmp(irr->name, irr->rd->name, MAX_INTERRUPT_NAME) != 0)) { + irr->rd = rrddim_find(st, irr->id); + if(unlikely(!irr->rd)) + irr->rd = rrddim_add(st, irr->id, irr->name, 1, 1, RRDDIM_INCREMENTAL); + else + rrddim_set_name(st, irr->rd, irr->name); + + // also reset per cpu RRDDIMs to avoid repeating strncmp() in the per core loop + if(likely(do_per_core)) { + int c; + for (c = 0; c < cpus ;c++) + irr->cpu[c].rd = NULL; + } + } + rrddim_set_by_pointer(st, irr->rd, irr->total); } rrdset_done(st); @@ -137,32 +164,33 @@ int do_proc_softirqs(int update_every, unsigned long long dt) { snprintfz(id, 50, "cpu%d_softirqs", c); st = rrdset_find_bytype("cpu", id); - if(!st) { + if(unlikely(!st)) { // find if everything is zero unsigned long long core_sum = 0 ; for(l = 0; l < lines ;l++) { struct interrupt *irr = irrindex(irrs, l, cpus); - if(!irr->used) continue; - core_sum += irr->value[c]; + if(unlikely(!irr->used)) continue; + core_sum += irr->cpu[c].value; } - if(core_sum == 0) continue; // try next core + if(unlikely(core_sum == 0)) continue; // try next core char title[100+1]; snprintfz(title, 100, "CPU%d softirqs", c); st = rrdset_create("cpu", id, NULL, "softirqs", "cpu.softirqs", title, "softirqs/s", 3000 + c, update_every, RRDSET_TYPE_STACKED); - - for(l = 0; l < lines ;l++) { - struct interrupt *irr = irrindex(irrs, l, cpus); - if(!irr->used) continue; - rrddim_add(st, irr->id, irr->name, 1, 1, RRDDIM_INCREMENTAL); - } } else rrdset_next(st); for(l = 0; l < lines ;l++) { struct interrupt *irr = irrindex(irrs, l, cpus); - if(!irr->used) continue; - rrddim_set(st, irr->id, irr->value[c]); + if(unlikely(!irr->used)) continue; + if(unlikely(!irr->cpu[c].rd)) { + irr->cpu[c].rd = rrddim_find(st, irr->id); + if(unlikely(!irr->cpu[c].rd)) + irr->cpu[c].rd = rrddim_add(st, irr->id, irr->name, 1, 1, RRDDIM_INCREMENTAL); + else + rrddim_set_name(st, irr->cpu[c].rd, irr->name); + } + rrddim_set_by_pointer(st, irr->cpu[c].rd, irr->cpu[c].value); } rrdset_done(st); } diff --git a/src/proc_stat.c b/src/proc_stat.c index 88cb820b3..f7e6d5bc0 100644 --- a/src/proc_stat.c +++ b/src/proc_stat.c @@ -1,6 +1,6 @@ #include "common.h" -int do_proc_stat(int update_every, unsigned long long dt) { +int do_proc_stat(int update_every, usec_t dt) { (void)dt; static procfile *ff = NULL; @@ -32,8 +32,8 @@ int do_proc_stat(int update_every, unsigned long long dt) { ff = procfile_readall(ff); if(unlikely(!ff)) return 0; // we return 0, so that we will retry to open it next time - uint32_t lines = procfile_lines(ff), l; - uint32_t words; + size_t lines = procfile_lines(ff), l; + size_t words; unsigned long long processes = 0, running = 0 , blocked = 0; RRDSET *st; @@ -46,7 +46,7 @@ int do_proc_stat(int update_every, unsigned long long dt) { if(likely(row_key[0] == 'c' && row_key[1] == 'p' && row_key[2] == 'u')) { words = procfile_linewords(ff, l); if(unlikely(words < 9)) { - error("Cannot read /proc/stat cpu line. Expected 9 params, read %u.", words); + error("Cannot read /proc/stat cpu line. Expected 9 params, read %zu.", words); continue; } @@ -54,19 +54,19 @@ int do_proc_stat(int update_every, unsigned long long dt) { unsigned long long user = 0, nice = 0, system = 0, idle = 0, iowait = 0, irq = 0, softirq = 0, steal = 0, guest = 0, guest_nice = 0; id = row_key; - user = strtoull(procfile_lineword(ff, l, 1), NULL, 10); - nice = strtoull(procfile_lineword(ff, l, 2), NULL, 10); - system = strtoull(procfile_lineword(ff, l, 3), NULL, 10); - idle = strtoull(procfile_lineword(ff, l, 4), NULL, 10); - iowait = strtoull(procfile_lineword(ff, l, 5), NULL, 10); - irq = strtoull(procfile_lineword(ff, l, 6), NULL, 10); - softirq = strtoull(procfile_lineword(ff, l, 7), NULL, 10); - steal = strtoull(procfile_lineword(ff, l, 8), NULL, 10); - - guest = strtoull(procfile_lineword(ff, l, 9), NULL, 10); + user = str2ull(procfile_lineword(ff, l, 1)); + nice = str2ull(procfile_lineword(ff, l, 2)); + system = str2ull(procfile_lineword(ff, l, 3)); + idle = str2ull(procfile_lineword(ff, l, 4)); + iowait = str2ull(procfile_lineword(ff, l, 5)); + irq = str2ull(procfile_lineword(ff, l, 6)); + softirq = str2ull(procfile_lineword(ff, l, 7)); + steal = str2ull(procfile_lineword(ff, l, 8)); + + guest = str2ull(procfile_lineword(ff, l, 9)); user -= guest; - guest_nice = strtoull(procfile_lineword(ff, l, 10), NULL, 10); + guest_nice = str2ull(procfile_lineword(ff, l, 10)); nice -= guest_nice; char *title, *type, *context, *family; @@ -126,8 +126,8 @@ int do_proc_stat(int update_every, unsigned long long dt) { rrdset_done(st); } } - else if(hash == hash_intr && strcmp(row_key, "intr") == 0) { - unsigned long long value = strtoull(procfile_lineword(ff, l, 1), NULL, 10); + else if(unlikely(hash == hash_intr && strcmp(row_key, "intr") == 0)) { + unsigned long long value = str2ull(procfile_lineword(ff, l, 1)); // -------------------------------------------------------------------- @@ -145,8 +145,8 @@ int do_proc_stat(int update_every, unsigned long long dt) { rrdset_done(st); } } - else if(hash == hash_ctxt && strcmp(row_key, "ctxt") == 0) { - unsigned long long value = strtoull(procfile_lineword(ff, l, 1), NULL, 10); + else if(unlikely(hash == hash_ctxt && strcmp(row_key, "ctxt") == 0)) { + unsigned long long value = str2ull(procfile_lineword(ff, l, 1)); // -------------------------------------------------------------------- @@ -163,14 +163,14 @@ int do_proc_stat(int update_every, unsigned long long dt) { rrdset_done(st); } } - else if(hash == hash_processes && !processes && strcmp(row_key, "processes") == 0) { - processes = strtoull(procfile_lineword(ff, l, 1), NULL, 10); + else if(unlikely(hash == hash_processes && !processes && strcmp(row_key, "processes") == 0)) { + processes = str2ull(procfile_lineword(ff, l, 1)); } - else if(hash == hash_procs_running && !running && strcmp(row_key, "procs_running") == 0) { - running = strtoull(procfile_lineword(ff, l, 1), NULL, 10); + else if(unlikely(hash == hash_procs_running && !running && strcmp(row_key, "procs_running") == 0)) { + running = str2ull(procfile_lineword(ff, l, 1)); } - else if(hash == hash_procs_blocked && !blocked && strcmp(row_key, "procs_blocked") == 0) { - blocked = strtoull(procfile_lineword(ff, l, 1), NULL, 10); + else if(unlikely(hash == hash_procs_blocked && !blocked && strcmp(row_key, "procs_blocked") == 0)) { + blocked = str2ull(procfile_lineword(ff, l, 1)); } } diff --git a/src/proc_sys_kernel_random_entropy_avail.c b/src/proc_sys_kernel_random_entropy_avail.c index 9515dad61..388406e0b 100644 --- a/src/proc_sys_kernel_random_entropy_avail.c +++ b/src/proc_sys_kernel_random_entropy_avail.c @@ -1,24 +1,24 @@ #include "common.h" -int do_proc_sys_kernel_random_entropy_avail(int update_every, unsigned long long dt) { - static procfile *ff = NULL; +int do_proc_sys_kernel_random_entropy_avail(int update_every, usec_t dt) { + (void)dt; - if(dt) {} ; + static procfile *ff = NULL; - if(!ff) { + if(unlikely(!ff)) { char filename[FILENAME_MAX + 1]; snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/sys/kernel/random/entropy_avail"); ff = procfile_open(config_get("plugin:proc:/proc/sys/kernel/random/entropy_avail", "filename to monitor", filename), "", PROCFILE_FLAG_DEFAULT); + if(unlikely(!ff)) return 1; } - if(!ff) return 1; ff = procfile_readall(ff); - if(!ff) return 0; // we return 0, so that we will retry to open it next time + if(unlikely(!ff)) return 0; // we return 0, so that we will retry to open it next time - unsigned long long entropy = strtoull(procfile_lineword(ff, 0, 0), NULL, 10); + unsigned long long entropy = str2ull(procfile_lineword(ff, 0, 0)); RRDSET *st = rrdset_find_bytype("system", "entropy"); - if(!st) { + if(unlikely(!st)) { st = rrdset_create("system", "entropy", NULL, "entropy", NULL, "Available Entropy", "entropy", 1000, update_every, RRDSET_TYPE_LINE); rrddim_add(st, "entropy", NULL, 1, 1, RRDDIM_ABSOLUTE); } diff --git a/src/proc_uptime.c b/src/proc_uptime.c new file mode 100644 index 000000000..9f341a33f --- /dev/null +++ b/src/proc_uptime.c @@ -0,0 +1,54 @@ +#include "common.h" + +int do_proc_uptime(int update_every, usec_t dt) { + (void)dt; + + static RRDSET *st = NULL; + collected_number uptime = 0; + +#ifdef CLOCK_BOOTTIME_IS_AVAILABLE + uptime = now_boottime_usec() / 1000; +#else + static procfile *ff = NULL; + + if(unlikely(!ff)) { + char filename[FILENAME_MAX + 1]; + snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/uptime"); + + ff = procfile_open(config_get("plugin:proc:/proc/uptime", "filename to monitor", filename), " \t", PROCFILE_FLAG_DEFAULT); + if(unlikely(!ff)) + return 1; + } + + ff = procfile_readall(ff); + if(unlikely(!ff)) + return 0; // we return 0, so that we will retry to open it next time + + if(unlikely(procfile_lines(ff) < 1)) { + error("/proc/uptime has no lines."); + return 1; + } + if(unlikely(procfile_linewords(ff, 0) < 1)) { + error("/proc/uptime has less than 1 word in it."); + return 1; + } + + uptime = (collected_number)(strtold(procfile_lineword(ff, 0, 0), NULL) * 1000.0); +#endif + + // -------------------------------------------------------------------- + + if(unlikely(!st)) + st = rrdset_find("system.uptime"); + + if(unlikely(!st)) { + st = rrdset_create("system", "uptime", NULL, "uptime", NULL, "System Uptime", "seconds", 1000, update_every, RRDSET_TYPE_LINE); + rrddim_add(st, "uptime", NULL, 1, 1000, RRDDIM_ABSOLUTE); + } + else rrdset_next(st); + + rrddim_set(st, "uptime", uptime); + rrdset_done(st); + + return 0; +} diff --git a/src/proc_vmstat.c b/src/proc_vmstat.c index 5f4e5aadd..ea917b989 100644 --- a/src/proc_vmstat.c +++ b/src/proc_vmstat.c @@ -1,416 +1,88 @@ #include "common.h" -int do_proc_vmstat(int update_every, unsigned long long dt) { +int do_proc_vmstat(int update_every, usec_t dt) { + (void)dt; + static procfile *ff = NULL; - static int do_swapio = -1, do_io = -1, do_pgfaults = -1, gen_hashes = -1; - - // static uint32_t hash_allocstall = -1; - // static uint32_t hash_compact_blocks_moved = -1; - // static uint32_t hash_compact_fail = -1; - // static uint32_t hash_compact_pagemigrate_failed = -1; - // static uint32_t hash_compact_pages_moved = -1; - // static uint32_t hash_compact_stall = -1; - // static uint32_t hash_compact_success = -1; - // static uint32_t hash_htlb_buddy_alloc_fail = -1; - // static uint32_t hash_htlb_buddy_alloc_success = -1; - // static uint32_t hash_kswapd_high_wmark_hit_quickly = -1; - // static uint32_t hash_kswapd_inodesteal = -1; - // static uint32_t hash_kswapd_low_wmark_hit_quickly = -1; - // static uint32_t hash_kswapd_skip_congestion_wait = -1; - // static uint32_t hash_nr_active_anon = -1; - // static uint32_t hash_nr_active_file = -1; - // static uint32_t hash_nr_anon_pages = -1; - // static uint32_t hash_nr_anon_transparent_hugepages = -1; - // static uint32_t hash_nr_bounce = -1; - // static uint32_t hash_nr_dirtied = -1; - // static uint32_t hash_nr_dirty = -1; - // static uint32_t hash_nr_dirty_background_threshold = -1; - // static uint32_t hash_nr_dirty_threshold = -1; - // static uint32_t hash_nr_file_pages = -1; - // static uint32_t hash_nr_free_pages = -1; - // static uint32_t hash_nr_inactive_anon = -1; - // static uint32_t hash_nr_inactive_file = -1; - // static uint32_t hash_nr_isolated_anon = -1; - // static uint32_t hash_nr_isolated_file = -1; - // static uint32_t hash_nr_kernel_stack = -1; - // static uint32_t hash_nr_mapped = -1; - // static uint32_t hash_nr_mlock = -1; - // static uint32_t hash_nr_page_table_pages = -1; - // static uint32_t hash_nr_shmem = -1; - // static uint32_t hash_nr_slab_reclaimable = -1; - // static uint32_t hash_nr_slab_unreclaimable = -1; - // static uint32_t hash_nr_unevictable = -1; - // static uint32_t hash_nr_unstable = -1; - // static uint32_t hash_nr_vmscan_immediate_reclaim = -1; - // static uint32_t hash_nr_vmscan_write = -1; - // static uint32_t hash_nr_writeback = -1; - // static uint32_t hash_nr_writeback_temp = -1; - // static uint32_t hash_nr_written = -1; - // static uint32_t hash_pageoutrun = -1; - // static uint32_t hash_pgactivate = -1; - // static uint32_t hash_pgalloc_dma = -1; - // static uint32_t hash_pgalloc_dma32 = -1; - // static uint32_t hash_pgalloc_movable = -1; - // static uint32_t hash_pgalloc_normal = -1; - // static uint32_t hash_pgdeactivate = -1; - static uint32_t hash_pgfault = -1; - // static uint32_t hash_pgfree = -1; - // static uint32_t hash_pginodesteal = -1; - static uint32_t hash_pgmajfault = -1; - static uint32_t hash_pgpgin = -1; - static uint32_t hash_pgpgout = -1; - // static uint32_t hash_pgrefill_dma = -1; - // static uint32_t hash_pgrefill_dma32 = -1; - // static uint32_t hash_pgrefill_movable = -1; - // static uint32_t hash_pgrefill_normal = -1; - // static uint32_t hash_pgrotated = -1; - // static uint32_t hash_pgscan_direct_dma = -1; - // static uint32_t hash_pgscan_direct_dma32 = -1; - // static uint32_t hash_pgscan_direct_movable = -1; - // static uint32_t hash_pgscan_direct_normal = -1; - // static uint32_t hash_pgscan_kswapd_dma = -1; - // static uint32_t hash_pgscan_kswapd_dma32 = -1; - // static uint32_t hash_pgscan_kswapd_movable = -1; - // static uint32_t hash_pgscan_kswapd_normal = -1; - // static uint32_t hash_pgsteal_direct_dma = -1; - // static uint32_t hash_pgsteal_direct_dma32 = -1; - // static uint32_t hash_pgsteal_direct_movable = -1; - // static uint32_t hash_pgsteal_direct_normal = -1; - // static uint32_t hash_pgsteal_kswapd_dma = -1; - // static uint32_t hash_pgsteal_kswapd_dma32 = -1; - // static uint32_t hash_pgsteal_kswapd_movable = -1; - // static uint32_t hash_pgsteal_kswapd_normal = -1; - static uint32_t hash_pswpin = -1; - static uint32_t hash_pswpout = -1; - // static uint32_t hash_slabs_scanned = -1; - // static uint32_t hash_thp_collapse_alloc = -1; - // static uint32_t hash_thp_collapse_alloc_failed = -1; - // static uint32_t hash_thp_fault_alloc = -1; - // static uint32_t hash_thp_fault_fallback = -1; - // static uint32_t hash_thp_split = -1; - // static uint32_t hash_unevictable_pgs_cleared = -1; - // static uint32_t hash_unevictable_pgs_culled = -1; - // static uint32_t hash_unevictable_pgs_mlocked = -1; - // static uint32_t hash_unevictable_pgs_mlockfreed = -1; - // static uint32_t hash_unevictable_pgs_munlocked = -1; - // static uint32_t hash_unevictable_pgs_rescued = -1; - // static uint32_t hash_unevictable_pgs_scanned = -1; - // static uint32_t hash_unevictable_pgs_stranded = -1; - - if(gen_hashes != 1) { - gen_hashes = 1; - // hash_allocstall = simple_hash("allocstall"); - // hash_compact_blocks_moved = simple_hash("compact_blocks_moved"); - // hash_compact_fail = simple_hash("compact_fail"); - // hash_compact_pagemigrate_failed = simple_hash("compact_pagemigrate_failed"); - // hash_compact_pages_moved = simple_hash("compact_pages_moved"); - // hash_compact_stall = simple_hash("compact_stall"); - // hash_compact_success = simple_hash("compact_success"); - // hash_htlb_buddy_alloc_fail = simple_hash("htlb_buddy_alloc_fail"); - // hash_htlb_buddy_alloc_success = simple_hash("htlb_buddy_alloc_success"); - // hash_kswapd_high_wmark_hit_quickly = simple_hash("kswapd_high_wmark_hit_quickly"); - // hash_kswapd_inodesteal = simple_hash("kswapd_inodesteal"); - // hash_kswapd_low_wmark_hit_quickly = simple_hash("kswapd_low_wmark_hit_quickly"); - // hash_kswapd_skip_congestion_wait = simple_hash("kswapd_skip_congestion_wait"); - // hash_nr_active_anon = simple_hash("nr_active_anon"); - // hash_nr_active_file = simple_hash("nr_active_file"); - // hash_nr_anon_pages = simple_hash("nr_anon_pages"); - // hash_nr_anon_transparent_hugepages = simple_hash("nr_anon_transparent_hugepages"); - // hash_nr_bounce = simple_hash("nr_bounce"); - // hash_nr_dirtied = simple_hash("nr_dirtied"); - // hash_nr_dirty = simple_hash("nr_dirty"); - // hash_nr_dirty_background_threshold = simple_hash("nr_dirty_background_threshold"); - // hash_nr_dirty_threshold = simple_hash("nr_dirty_threshold"); - // hash_nr_file_pages = simple_hash("nr_file_pages"); - // hash_nr_free_pages = simple_hash("nr_free_pages"); - // hash_nr_inactive_anon = simple_hash("nr_inactive_anon"); - // hash_nr_inactive_file = simple_hash("nr_inactive_file"); - // hash_nr_isolated_anon = simple_hash("nr_isolated_anon"); - // hash_nr_isolated_file = simple_hash("nr_isolated_file"); - // hash_nr_kernel_stack = simple_hash("nr_kernel_stack"); - // hash_nr_mapped = simple_hash("nr_mapped"); - // hash_nr_mlock = simple_hash("nr_mlock"); - // hash_nr_page_table_pages = simple_hash("nr_page_table_pages"); - // hash_nr_shmem = simple_hash("nr_shmem"); - // hash_nr_slab_reclaimable = simple_hash("nr_slab_reclaimable"); - // hash_nr_slab_unreclaimable = simple_hash("nr_slab_unreclaimable"); - // hash_nr_unevictable = simple_hash("nr_unevictable"); - // hash_nr_unstable = simple_hash("nr_unstable"); - // hash_nr_vmscan_immediate_reclaim = simple_hash("nr_vmscan_immediate_reclaim"); - // hash_nr_vmscan_write = simple_hash("nr_vmscan_write"); - // hash_nr_writeback = simple_hash("nr_writeback"); - // hash_nr_writeback_temp = simple_hash("nr_writeback_temp"); - // hash_nr_written = simple_hash("nr_written"); - // hash_pageoutrun = simple_hash("pageoutrun"); - // hash_pgactivate = simple_hash("pgactivate"); - // hash_pgalloc_dma = simple_hash("pgalloc_dma"); - // hash_pgalloc_dma32 = simple_hash("pgalloc_dma32"); - // hash_pgalloc_movable = simple_hash("pgalloc_movable"); - // hash_pgalloc_normal = simple_hash("pgalloc_normal"); - // hash_pgdeactivate = simple_hash("pgdeactivate"); - hash_pgfault = simple_hash("pgfault"); - // hash_pgfree = simple_hash("pgfree"); - // hash_pginodesteal = simple_hash("pginodesteal"); - hash_pgmajfault = simple_hash("pgmajfault"); - hash_pgpgin = simple_hash("pgpgin"); - hash_pgpgout = simple_hash("pgpgout"); - // hash_pgrefill_dma = simple_hash("pgrefill_dma"); - // hash_pgrefill_dma32 = simple_hash("pgrefill_dma32"); - // hash_pgrefill_movable = simple_hash("pgrefill_movable"); - // hash_pgrefill_normal = simple_hash("pgrefill_normal"); - // hash_pgrotated = simple_hash("pgrotated"); - // hash_pgscan_direct_dma = simple_hash("pgscan_direct_dma"); - // hash_pgscan_direct_dma32 = simple_hash("pgscan_direct_dma32"); - // hash_pgscan_direct_movable = simple_hash("pgscan_direct_movable"); - // hash_pgscan_direct_normal = simple_hash("pgscan_direct_normal"); - // hash_pgscan_kswapd_dma = simple_hash("pgscan_kswapd_dma"); - // hash_pgscan_kswapd_dma32 = simple_hash("pgscan_kswapd_dma32"); - // hash_pgscan_kswapd_movable = simple_hash("pgscan_kswapd_movable"); - // hash_pgscan_kswapd_normal = simple_hash("pgscan_kswapd_normal"); - // hash_pgsteal_direct_dma = simple_hash("pgsteal_direct_dma"); - // hash_pgsteal_direct_dma32 = simple_hash("pgsteal_direct_dma32"); - // hash_pgsteal_direct_movable = simple_hash("pgsteal_direct_movable"); - // hash_pgsteal_direct_normal = simple_hash("pgsteal_direct_normal"); - // hash_pgsteal_kswapd_dma = simple_hash("pgsteal_kswapd_dma"); - // hash_pgsteal_kswapd_dma32 = simple_hash("pgsteal_kswapd_dma32"); - // hash_pgsteal_kswapd_movable = simple_hash("pgsteal_kswapd_movable"); - // hash_pgsteal_kswapd_normal = simple_hash("pgsteal_kswapd_normal"); - hash_pswpin = simple_hash("pswpin"); - hash_pswpout = simple_hash("pswpout"); - // hash_slabs_scanned = simple_hash("slabs_scanned"); - // hash_thp_collapse_alloc = simple_hash("thp_collapse_alloc"); - // hash_thp_collapse_alloc_failed = simple_hash("thp_collapse_alloc_failed"); - // hash_thp_fault_alloc = simple_hash("thp_fault_alloc"); - // hash_thp_fault_fallback = simple_hash("thp_fault_fallback"); - // hash_thp_split = simple_hash("thp_split"); - // hash_unevictable_pgs_cleared = simple_hash("unevictable_pgs_cleared"); - // hash_unevictable_pgs_culled = simple_hash("unevictable_pgs_culled"); - // hash_unevictable_pgs_mlocked = simple_hash("unevictable_pgs_mlocked"); - // hash_unevictable_pgs_mlockfreed = simple_hash("unevictable_pgs_mlockfreed"); - // hash_unevictable_pgs_munlocked = simple_hash("unevictable_pgs_munlocked"); - // hash_unevictable_pgs_rescued = simple_hash("unevictable_pgs_rescued"); - // hash_unevictable_pgs_scanned = simple_hash("unevictable_pgs_scanned"); - // hash_unevictable_pgs_stranded = simple_hash("unevictable_pgs_stranded"); + static int do_swapio = -1, do_io = -1, do_pgfaults = -1, do_numa = -1; + static int has_numa = -1; + + static ARL_BASE *arl_base = NULL; + static unsigned long long numa_foreign = 0ULL; + static unsigned long long numa_hint_faults = 0ULL; + static unsigned long long numa_hint_faults_local = 0ULL; + static unsigned long long numa_huge_pte_updates = 0ULL; + static unsigned long long numa_interleave = 0ULL; + static unsigned long long numa_local = 0ULL; + static unsigned long long numa_other = 0ULL; + static unsigned long long numa_pages_migrated = 0ULL; + static unsigned long long numa_pte_updates = 0ULL; + static unsigned long long pgfault = 0ULL; + static unsigned long long pgmajfault = 0ULL; + static unsigned long long pgpgin = 0ULL; + static unsigned long long pgpgout = 0ULL; + static unsigned long long pswpin = 0ULL; + static unsigned long long pswpout = 0ULL; + + if(unlikely(!arl_base)) { + do_swapio = config_get_boolean_ondemand("plugin:proc:/proc/vmstat", "swap i/o", CONFIG_ONDEMAND_ONDEMAND); + do_io = config_get_boolean("plugin:proc:/proc/vmstat", "disk i/o", 1); + do_pgfaults = config_get_boolean("plugin:proc:/proc/vmstat", "memory page faults", 1); + do_numa = config_get_boolean_ondemand("plugin:proc:/proc/vmstat", "system-wide numa metric summary", CONFIG_ONDEMAND_ONDEMAND); + + + arl_base = arl_create("vmstat", NULL, 60); + arl_expect(arl_base, "pgfault", &pgfault); + arl_expect(arl_base, "pgmajfault", &pgmajfault); + arl_expect(arl_base, "pgpgin", &pgpgin); + arl_expect(arl_base, "pgpgout", &pgpgout); + arl_expect(arl_base, "pswpin", &pswpin); + arl_expect(arl_base, "pswpout", &pswpout); + + if(do_numa == CONFIG_ONDEMAND_YES || (do_numa == CONFIG_ONDEMAND_ONDEMAND && get_numa_node_count() >= 2)) { + arl_expect(arl_base, "numa_foreign", &numa_foreign); + arl_expect(arl_base, "numa_hint_faults_local", &numa_hint_faults_local); + arl_expect(arl_base, "numa_hint_faults", &numa_hint_faults); + arl_expect(arl_base, "numa_huge_pte_updates", &numa_huge_pte_updates); + arl_expect(arl_base, "numa_interleave", &numa_interleave); + arl_expect(arl_base, "numa_local", &numa_local); + arl_expect(arl_base, "numa_other", &numa_other); + arl_expect(arl_base, "numa_pages_migrated", &numa_pages_migrated); + arl_expect(arl_base, "numa_pte_updates", &numa_pte_updates); + } + else { + // Do not expect numa metrics when they are not needed. + // By not adding them, the ARL will stop processing the file + // when all the expected metrics are collected. + // Also ARL will not parse their values. + has_numa = 0; + do_numa = CONFIG_ONDEMAND_NO; + } } - if(do_swapio == -1) do_swapio = config_get_boolean_ondemand("plugin:proc:/proc/vmstat", "swap i/o", CONFIG_ONDEMAND_ONDEMAND); - if(do_io == -1) do_io = config_get_boolean("plugin:proc:/proc/vmstat", "disk i/o", 1); - if(do_pgfaults == -1) do_pgfaults = config_get_boolean("plugin:proc:/proc/vmstat", "memory page faults", 1); - - (void)dt; - - if(!ff) { + if(unlikely(!ff)) { char filename[FILENAME_MAX + 1]; snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/vmstat"); ff = procfile_open(config_get("plugin:proc:/proc/vmstat", "filename to monitor", filename), " \t:", PROCFILE_FLAG_DEFAULT); + if(unlikely(!ff)) return 1; } - if(!ff) return 1; ff = procfile_readall(ff); - if(!ff) return 0; // we return 0, so that we will retry to open it next time - - uint32_t lines = procfile_lines(ff), l; - uint32_t words; - - // unsigned long long allocstall = 0ULL; - // unsigned long long compact_blocks_moved = 0ULL; - // unsigned long long compact_fail = 0ULL; - // unsigned long long compact_pagemigrate_failed = 0ULL; - // unsigned long long compact_pages_moved = 0ULL; - // unsigned long long compact_stall = 0ULL; - // unsigned long long compact_success = 0ULL; - // unsigned long long htlb_buddy_alloc_fail = 0ULL; - // unsigned long long htlb_buddy_alloc_success = 0ULL; - // unsigned long long kswapd_high_wmark_hit_quickly = 0ULL; - // unsigned long long kswapd_inodesteal = 0ULL; - // unsigned long long kswapd_low_wmark_hit_quickly = 0ULL; - // unsigned long long kswapd_skip_congestion_wait = 0ULL; - // unsigned long long nr_active_anon = 0ULL; - // unsigned long long nr_active_file = 0ULL; - // unsigned long long nr_anon_pages = 0ULL; - // unsigned long long nr_anon_transparent_hugepages = 0ULL; - // unsigned long long nr_bounce = 0ULL; - // unsigned long long nr_dirtied = 0ULL; - // unsigned long long nr_dirty = 0ULL; - // unsigned long long nr_dirty_background_threshold = 0ULL; - // unsigned long long nr_dirty_threshold = 0ULL; - // unsigned long long nr_file_pages = 0ULL; - // unsigned long long nr_free_pages = 0ULL; - // unsigned long long nr_inactive_anon = 0ULL; - // unsigned long long nr_inactive_file = 0ULL; - // unsigned long long nr_isolated_anon = 0ULL; - // unsigned long long nr_isolated_file = 0ULL; - // unsigned long long nr_kernel_stack = 0ULL; - // unsigned long long nr_mapped = 0ULL; - // unsigned long long nr_mlock = 0ULL; - // unsigned long long nr_page_table_pages = 0ULL; - // unsigned long long nr_shmem = 0ULL; - // unsigned long long nr_slab_reclaimable = 0ULL; - // unsigned long long nr_slab_unreclaimable = 0ULL; - // unsigned long long nr_unevictable = 0ULL; - // unsigned long long nr_unstable = 0ULL; - // unsigned long long nr_vmscan_immediate_reclaim = 0ULL; - // unsigned long long nr_vmscan_write = 0ULL; - // unsigned long long nr_writeback = 0ULL; - // unsigned long long nr_writeback_temp = 0ULL; - // unsigned long long nr_written = 0ULL; - // unsigned long long pageoutrun = 0ULL; - // unsigned long long pgactivate = 0ULL; - // unsigned long long pgalloc_dma = 0ULL; - // unsigned long long pgalloc_dma32 = 0ULL; - // unsigned long long pgalloc_movable = 0ULL; - // unsigned long long pgalloc_normal = 0ULL; - // unsigned long long pgdeactivate = 0ULL; - unsigned long long pgfault = 0ULL; - // unsigned long long pgfree = 0ULL; - // unsigned long long pginodesteal = 0ULL; - unsigned long long pgmajfault = 0ULL; - unsigned long long pgpgin = 0ULL; - unsigned long long pgpgout = 0ULL; - // unsigned long long pgrefill_dma = 0ULL; - // unsigned long long pgrefill_dma32 = 0ULL; - // unsigned long long pgrefill_movable = 0ULL; - // unsigned long long pgrefill_normal = 0ULL; - // unsigned long long pgrotated = 0ULL; - // unsigned long long pgscan_direct_dma = 0ULL; - // unsigned long long pgscan_direct_dma32 = 0ULL; - // unsigned long long pgscan_direct_movable = 0ULL; - // unsigned long long pgscan_direct_normal = 0ULL; - // unsigned long long pgscan_kswapd_dma = 0ULL; - // unsigned long long pgscan_kswapd_dma32 = 0ULL; - // unsigned long long pgscan_kswapd_movable = 0ULL; - // unsigned long long pgscan_kswapd_normal = 0ULL; - // unsigned long long pgsteal_direct_dma = 0ULL; - // unsigned long long pgsteal_direct_dma32 = 0ULL; - // unsigned long long pgsteal_direct_movable = 0ULL; - // unsigned long long pgsteal_direct_normal = 0ULL; - // unsigned long long pgsteal_kswapd_dma = 0ULL; - // unsigned long long pgsteal_kswapd_dma32 = 0ULL; - // unsigned long long pgsteal_kswapd_movable = 0ULL; - // unsigned long long pgsteal_kswapd_normal = 0ULL; - unsigned long long pswpin = 0ULL; - unsigned long long pswpout = 0ULL; - // unsigned long long slabs_scanned = 0ULL; - // unsigned long long thp_collapse_alloc = 0ULL; - // unsigned long long thp_collapse_alloc_failed = 0ULL; - // unsigned long long thp_fault_alloc = 0ULL; - // unsigned long long thp_fault_fallback = 0ULL; - // unsigned long long thp_split = 0ULL; - // unsigned long long unevictable_pgs_cleared = 0ULL; - // unsigned long long unevictable_pgs_culled = 0ULL; - // unsigned long long unevictable_pgs_mlocked = 0ULL; - // unsigned long long unevictable_pgs_mlockfreed = 0ULL; - // unsigned long long unevictable_pgs_munlocked = 0ULL; - // unsigned long long unevictable_pgs_rescued = 0ULL; - // unsigned long long unevictable_pgs_scanned = 0ULL; - // unsigned long long unevictable_pgs_stranded = 0ULL; + if(unlikely(!ff)) return 0; // we return 0, so that we will retry to open it next time + + size_t lines = procfile_lines(ff), l; + arl_begin(arl_base); for(l = 0; l < lines ;l++) { - words = procfile_linewords(ff, l); - if(words < 2) { - if(words) error("Cannot read /proc/vmstat line %u. Expected 2 params, read %u.", l, words); + size_t words = procfile_linewords(ff, l); + if(unlikely(words < 2)) { + if(unlikely(words)) error("Cannot read /proc/vmstat line %zu. Expected 2 params, read %zu.", l, words); continue; } - char *name = procfile_lineword(ff, l, 0); - char * value = procfile_lineword(ff, l, 1); - if(!name || !*name || !value || !*value) continue; - - uint32_t hash = simple_hash(name); - - if(0) ; - // else if(hash == hash_allocstall && strcmp(name, "allocstall") == 0) allocstall = strtoull(value, NULL, 10); - // else if(hash == hash_compact_blocks_moved && strcmp(name, "compact_blocks_moved") == 0) compact_blocks_moved = strtoull(value, NULL, 10); - // else if(hash == hash_compact_fail && strcmp(name, "compact_fail") == 0) compact_fail = strtoull(value, NULL, 10); - // else if(hash == hash_compact_pagemigrate_failed && strcmp(name, "compact_pagemigrate_failed") == 0) compact_pagemigrate_failed = strtoull(value, NULL, 10); - // else if(hash == hash_compact_pages_moved && strcmp(name, "compact_pages_moved") == 0) compact_pages_moved = strtoull(value, NULL, 10); - // else if(hash == hash_compact_stall && strcmp(name, "compact_stall") == 0) compact_stall = strtoull(value, NULL, 10); - // else if(hash == hash_compact_success && strcmp(name, "compact_success") == 0) compact_success = strtoull(value, NULL, 10); - // else if(hash == hash_htlb_buddy_alloc_fail && strcmp(name, "htlb_buddy_alloc_fail") == 0) htlb_buddy_alloc_fail = strtoull(value, NULL, 10); - // else if(hash == hash_htlb_buddy_alloc_success && strcmp(name, "htlb_buddy_alloc_success") == 0) htlb_buddy_alloc_success = strtoull(value, NULL, 10); - // else if(hash == hash_kswapd_high_wmark_hit_quickly && strcmp(name, "kswapd_high_wmark_hit_quickly") == 0) kswapd_high_wmark_hit_quickly = strtoull(value, NULL, 10); - // else if(hash == hash_kswapd_inodesteal && strcmp(name, "kswapd_inodesteal") == 0) kswapd_inodesteal = strtoull(value, NULL, 10); - // else if(hash == hash_kswapd_low_wmark_hit_quickly && strcmp(name, "kswapd_low_wmark_hit_quickly") == 0) kswapd_low_wmark_hit_quickly = strtoull(value, NULL, 10); - // else if(hash == hash_kswapd_skip_congestion_wait && strcmp(name, "kswapd_skip_congestion_wait") == 0) kswapd_skip_congestion_wait = strtoull(value, NULL, 10); - // else if(hash == hash_nr_active_anon && strcmp(name, "nr_active_anon") == 0) nr_active_anon = strtoull(value, NULL, 10); - // else if(hash == hash_nr_active_file && strcmp(name, "nr_active_file") == 0) nr_active_file = strtoull(value, NULL, 10); - // else if(hash == hash_nr_anon_pages && strcmp(name, "nr_anon_pages") == 0) nr_anon_pages = strtoull(value, NULL, 10); - // else if(hash == hash_nr_anon_transparent_hugepages && strcmp(name, "nr_anon_transparent_hugepages") == 0) nr_anon_transparent_hugepages = strtoull(value, NULL, 10); - // else if(hash == hash_nr_bounce && strcmp(name, "nr_bounce") == 0) nr_bounce = strtoull(value, NULL, 10); - // else if(hash == hash_nr_dirtied && strcmp(name, "nr_dirtied") == 0) nr_dirtied = strtoull(value, NULL, 10); - // else if(hash == hash_nr_dirty && strcmp(name, "nr_dirty") == 0) nr_dirty = strtoull(value, NULL, 10); - // else if(hash == hash_nr_dirty_background_threshold && strcmp(name, "nr_dirty_background_threshold") == 0) nr_dirty_background_threshold = strtoull(value, NULL, 10); - // else if(hash == hash_nr_dirty_threshold && strcmp(name, "nr_dirty_threshold") == 0) nr_dirty_threshold = strtoull(value, NULL, 10); - // else if(hash == hash_nr_file_pages && strcmp(name, "nr_file_pages") == 0) nr_file_pages = strtoull(value, NULL, 10); - // else if(hash == hash_nr_free_pages && strcmp(name, "nr_free_pages") == 0) nr_free_pages = strtoull(value, NULL, 10); - // else if(hash == hash_nr_inactive_anon && strcmp(name, "nr_inactive_anon") == 0) nr_inactive_anon = strtoull(value, NULL, 10); - // else if(hash == hash_nr_inactive_file && strcmp(name, "nr_inactive_file") == 0) nr_inactive_file = strtoull(value, NULL, 10); - // else if(hash == hash_nr_isolated_anon && strcmp(name, "nr_isolated_anon") == 0) nr_isolated_anon = strtoull(value, NULL, 10); - // else if(hash == hash_nr_isolated_file && strcmp(name, "nr_isolated_file") == 0) nr_isolated_file = strtoull(value, NULL, 10); - // else if(hash == hash_nr_kernel_stack && strcmp(name, "nr_kernel_stack") == 0) nr_kernel_stack = strtoull(value, NULL, 10); - // else if(hash == hash_nr_mapped && strcmp(name, "nr_mapped") == 0) nr_mapped = strtoull(value, NULL, 10); - // else if(hash == hash_nr_mlock && strcmp(name, "nr_mlock") == 0) nr_mlock = strtoull(value, NULL, 10); - // else if(hash == hash_nr_page_table_pages && strcmp(name, "nr_page_table_pages") == 0) nr_page_table_pages = strtoull(value, NULL, 10); - // else if(hash == hash_nr_shmem && strcmp(name, "nr_shmem") == 0) nr_shmem = strtoull(value, NULL, 10); - // else if(hash == hash_nr_slab_reclaimable && strcmp(name, "nr_slab_reclaimable") == 0) nr_slab_reclaimable = strtoull(value, NULL, 10); - // else if(hash == hash_nr_slab_unreclaimable && strcmp(name, "nr_slab_unreclaimable") == 0) nr_slab_unreclaimable = strtoull(value, NULL, 10); - // else if(hash == hash_nr_unevictable && strcmp(name, "nr_unevictable") == 0) nr_unevictable = strtoull(value, NULL, 10); - // else if(hash == hash_nr_unstable && strcmp(name, "nr_unstable") == 0) nr_unstable = strtoull(value, NULL, 10); - // else if(hash == hash_nr_vmscan_immediate_reclaim && strcmp(name, "nr_vmscan_immediate_reclaim") == 0) nr_vmscan_immediate_reclaim = strtoull(value, NULL, 10); - // else if(hash == hash_nr_vmscan_write && strcmp(name, "nr_vmscan_write") == 0) nr_vmscan_write = strtoull(value, NULL, 10); - // else if(hash == hash_nr_writeback && strcmp(name, "nr_writeback") == 0) nr_writeback = strtoull(value, NULL, 10); - // else if(hash == hash_nr_writeback_temp && strcmp(name, "nr_writeback_temp") == 0) nr_writeback_temp = strtoull(value, NULL, 10); - // else if(hash == hash_nr_written && strcmp(name, "nr_written") == 0) nr_written = strtoull(value, NULL, 10); - // else if(hash == hash_pageoutrun && strcmp(name, "pageoutrun") == 0) pageoutrun = strtoull(value, NULL, 10); - // else if(hash == hash_pgactivate && strcmp(name, "pgactivate") == 0) pgactivate = strtoull(value, NULL, 10); - // else if(hash == hash_pgalloc_dma && strcmp(name, "pgalloc_dma") == 0) pgalloc_dma = strtoull(value, NULL, 10); - // else if(hash == hash_pgalloc_dma32 && strcmp(name, "pgalloc_dma32") == 0) pgalloc_dma32 = strtoull(value, NULL, 10); - // else if(hash == hash_pgalloc_movable && strcmp(name, "pgalloc_movable") == 0) pgalloc_movable = strtoull(value, NULL, 10); - // else if(hash == hash_pgalloc_normal && strcmp(name, "pgalloc_normal") == 0) pgalloc_normal = strtoull(value, NULL, 10); - // else if(hash == hash_pgdeactivate && strcmp(name, "pgdeactivate") == 0) pgdeactivate = strtoull(value, NULL, 10); - else if(hash == hash_pgfault && strcmp(name, "pgfault") == 0) pgfault = strtoull(value, NULL, 10); - // else if(hash == hash_pgfree && strcmp(name, "pgfree") == 0) pgfree = strtoull(value, NULL, 10); - // else if(hash == hash_pginodesteal && strcmp(name, "pginodesteal") == 0) pginodesteal = strtoull(value, NULL, 10); - else if(hash == hash_pgmajfault && strcmp(name, "pgmajfault") == 0) pgmajfault = strtoull(value, NULL, 10); - else if(hash == hash_pgpgin && strcmp(name, "pgpgin") == 0) pgpgin = strtoull(value, NULL, 10); - else if(hash == hash_pgpgout && strcmp(name, "pgpgout") == 0) pgpgout = strtoull(value, NULL, 10); - // else if(hash == hash_pgrefill_dma && strcmp(name, "pgrefill_dma") == 0) pgrefill_dma = strtoull(value, NULL, 10); - // else if(hash == hash_pgrefill_dma32 && strcmp(name, "pgrefill_dma32") == 0) pgrefill_dma32 = strtoull(value, NULL, 10); - // else if(hash == hash_pgrefill_movable && strcmp(name, "pgrefill_movable") == 0) pgrefill_movable = strtoull(value, NULL, 10); - // else if(hash == hash_pgrefill_normal && strcmp(name, "pgrefill_normal") == 0) pgrefill_normal = strtoull(value, NULL, 10); - // else if(hash == hash_pgrotated && strcmp(name, "pgrotated") == 0) pgrotated = strtoull(value, NULL, 10); - // else if(hash == hash_pgscan_direct_dma && strcmp(name, "pgscan_direct_dma") == 0) pgscan_direct_dma = strtoull(value, NULL, 10); - // else if(hash == hash_pgscan_direct_dma32 && strcmp(name, "pgscan_direct_dma32") == 0) pgscan_direct_dma32 = strtoull(value, NULL, 10); - // else if(hash == hash_pgscan_direct_movable && strcmp(name, "pgscan_direct_movable") == 0) pgscan_direct_movable = strtoull(value, NULL, 10); - // else if(hash == hash_pgscan_direct_normal && strcmp(name, "pgscan_direct_normal") == 0) pgscan_direct_normal = strtoull(value, NULL, 10); - // else if(hash == hash_pgscan_kswapd_dma && strcmp(name, "pgscan_kswapd_dma") == 0) pgscan_kswapd_dma = strtoull(value, NULL, 10); - // else if(hash == hash_pgscan_kswapd_dma32 && strcmp(name, "pgscan_kswapd_dma32") == 0) pgscan_kswapd_dma32 = strtoull(value, NULL, 10); - // else if(hash == hash_pgscan_kswapd_movable && strcmp(name, "pgscan_kswapd_movable") == 0) pgscan_kswapd_movable = strtoull(value, NULL, 10); - // else if(hash == hash_pgscan_kswapd_normal && strcmp(name, "pgscan_kswapd_normal") == 0) pgscan_kswapd_normal = strtoull(value, NULL, 10); - // else if(hash == hash_pgsteal_direct_dma && strcmp(name, "pgsteal_direct_dma") == 0) pgsteal_direct_dma = strtoull(value, NULL, 10); - // else if(hash == hash_pgsteal_direct_dma32 && strcmp(name, "pgsteal_direct_dma32") == 0) pgsteal_direct_dma32 = strtoull(value, NULL, 10); - // else if(hash == hash_pgsteal_direct_movable && strcmp(name, "pgsteal_direct_movable") == 0) pgsteal_direct_movable = strtoull(value, NULL, 10); - // else if(hash == hash_pgsteal_direct_normal && strcmp(name, "pgsteal_direct_normal") == 0) pgsteal_direct_normal = strtoull(value, NULL, 10); - // else if(hash == hash_pgsteal_kswapd_dma && strcmp(name, "pgsteal_kswapd_dma") == 0) pgsteal_kswapd_dma = strtoull(value, NULL, 10); - // else if(hash == hash_pgsteal_kswapd_dma32 && strcmp(name, "pgsteal_kswapd_dma32") == 0) pgsteal_kswapd_dma32 = strtoull(value, NULL, 10); - // else if(hash == hash_pgsteal_kswapd_movable && strcmp(name, "pgsteal_kswapd_movable") == 0) pgsteal_kswapd_movable = strtoull(value, NULL, 10); - // else if(hash == hash_pgsteal_kswapd_normal && strcmp(name, "pgsteal_kswapd_normal") == 0) pgsteal_kswapd_normal = strtoull(value, NULL, 10); - else if(hash == hash_pswpin && strcmp(name, "pswpin") == 0) pswpin = strtoull(value, NULL, 10); - else if(hash == hash_pswpout && strcmp(name, "pswpout") == 0) pswpout = strtoull(value, NULL, 10); - // else if(hash == hash_slabs_scanned && strcmp(name, "slabs_scanned") == 0) slabs_scanned = strtoull(value, NULL, 10); - // else if(hash == hash_thp_collapse_alloc && strcmp(name, "thp_collapse_alloc") == 0) thp_collapse_alloc = strtoull(value, NULL, 10); - // else if(hash == hash_thp_collapse_alloc_failed && strcmp(name, "thp_collapse_alloc_failed") == 0) thp_collapse_alloc_failed = strtoull(value, NULL, 10); - // else if(hash == hash_thp_fault_alloc && strcmp(name, "thp_fault_alloc") == 0) thp_fault_alloc = strtoull(value, NULL, 10); - // else if(hash == hash_thp_fault_fallback && strcmp(name, "thp_fault_fallback") == 0) thp_fault_fallback = strtoull(value, NULL, 10); - // else if(hash == hash_thp_split && strcmp(name, "thp_split") == 0) thp_split = strtoull(value, NULL, 10); - // else if(hash == hash_unevictable_pgs_cleared && strcmp(name, "unevictable_pgs_cleared") == 0) unevictable_pgs_cleared = strtoull(value, NULL, 10); - // else if(hash == hash_unevictable_pgs_culled && strcmp(name, "unevictable_pgs_culled") == 0) unevictable_pgs_culled = strtoull(value, NULL, 10); - // else if(hash == hash_unevictable_pgs_mlocked && strcmp(name, "unevictable_pgs_mlocked") == 0) unevictable_pgs_mlocked = strtoull(value, NULL, 10); - // else if(hash == hash_unevictable_pgs_mlockfreed && strcmp(name, "unevictable_pgs_mlockfreed") == 0) unevictable_pgs_mlockfreed = strtoull(value, NULL, 10); - // else if(hash == hash_unevictable_pgs_munlocked && strcmp(name, "unevictable_pgs_munlocked") == 0) unevictable_pgs_munlocked = strtoull(value, NULL, 10); - // else if(hash == hash_unevictable_pgs_rescued && strcmp(name, "unevictable_pgs_rescued") == 0) unevictable_pgs_rescued = strtoull(value, NULL, 10); - // else if(hash == hash_unevictable_pgs_scanned && strcmp(name, "unevictable_pgs_scanned") == 0) unevictable_pgs_scanned = strtoull(value, NULL, 10); - // else if(hash == hash_unevictable_pgs_stranded && strcmp(name, "unevictable_pgs_stranded") == 0) unevictable_pgs_stranded = strtoull(value, NULL, 10); + if(unlikely(arl_check(arl_base, + procfile_lineword(ff, l, 0), + procfile_lineword(ff, l, 1)))) break; } // -------------------------------------------------------------------- @@ -419,7 +91,7 @@ int do_proc_vmstat(int update_every, unsigned long long dt) { do_swapio = CONFIG_ONDEMAND_YES; static RRDSET *st_swapio = NULL; - if(!st_swapio) { + if(unlikely(!st_swapio)) { st_swapio = rrdset_create("system", "swapio", NULL, "swap", NULL, "Swap I/O", "kilobytes/s", 250, update_every, RRDSET_TYPE_AREA); rrddim_add(st_swapio, "in", NULL, sysconf(_SC_PAGESIZE), 1024, RRDDIM_INCREMENTAL); @@ -436,7 +108,7 @@ int do_proc_vmstat(int update_every, unsigned long long dt) { if(do_io) { static RRDSET *st_io = NULL; - if(!st_io) { + if(unlikely(!st_io)) { st_io = rrdset_create("system", "io", NULL, "disk", NULL, "Disk I/O", "kilobytes/s", 150, update_every, RRDSET_TYPE_AREA); rrddim_add(st_io, "in", NULL, 1, 1, RRDDIM_INCREMENTAL); @@ -453,7 +125,7 @@ int do_proc_vmstat(int update_every, unsigned long long dt) { if(do_pgfaults) { static RRDSET *st_pgfaults = NULL; - if(!st_pgfaults) { + if(unlikely(!st_pgfaults)) { st_pgfaults = rrdset_create("mem", "pgfaults", NULL, "system", NULL, "Memory Page Faults", "page faults/s", 500, update_every, RRDSET_TYPE_LINE); st_pgfaults->isdetail = 1; @@ -467,6 +139,54 @@ int do_proc_vmstat(int update_every, unsigned long long dt) { rrdset_done(st_pgfaults); } + // -------------------------------------------------------------------- + + // Ondemand criteria for NUMA. Since this won't change at run time, we + // check it only once. We check whether the node count is >= 2 because + // single-node systems have uninteresting statistics (since all accesses + // are local). + if(unlikely(has_numa == -1)) + has_numa = (numa_local || numa_foreign || numa_interleave || numa_other || numa_pte_updates || + numa_huge_pte_updates || numa_hint_faults || numa_hint_faults_local || numa_pages_migrated) ? 1 : 0; + + if(do_numa == CONFIG_ONDEMAND_YES || (do_numa == CONFIG_ONDEMAND_ONDEMAND && has_numa)) { + do_numa = CONFIG_ONDEMAND_YES; + + static RRDSET *st_numa = NULL; + if(unlikely(!st_numa)) { + st_numa = rrdset_create("mem", "numa", NULL, "numa", NULL, "NUMA events", "events/s", 800, update_every, RRDSET_TYPE_LINE); + st_numa->isdetail = 1; + + // These depend on CONFIG_NUMA in the kernel. + rrddim_add(st_numa, "local", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st_numa, "foreign", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st_numa, "interleave", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st_numa, "other", NULL, 1, 1, RRDDIM_INCREMENTAL); + + // The following stats depend on CONFIG_NUMA_BALANCING in the + // kernel. + rrddim_add(st_numa, "pte updates", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st_numa, "huge pte updates", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st_numa, "hint faults", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st_numa, "hint faults local", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st_numa, "pages migrated", NULL, 1, 1, RRDDIM_INCREMENTAL); + } + else rrdset_next(st_numa); + + rrddim_set(st_numa, "local", numa_local); + rrddim_set(st_numa, "foreign", numa_foreign); + rrddim_set(st_numa, "interleave", numa_interleave); + rrddim_set(st_numa, "other", numa_other); + + rrddim_set(st_numa, "pte updates", numa_pte_updates); + rrddim_set(st_numa, "huge pte updates", numa_huge_pte_updates); + rrddim_set(st_numa, "hint faults", numa_hint_faults); + rrddim_set(st_numa, "hint faults local", numa_hint_faults_local); + rrddim_set(st_numa, "pages migrated", numa_pages_migrated); + + rrdset_done(st_numa); + } + return 0; } diff --git a/src/procfile.c b/src/procfile.c index e2aa60582..6f52bf465 100644 --- a/src/procfile.c +++ b/src/procfile.c @@ -1,4 +1,5 @@ #include "common.h" +#include "procfile.h" #define PF_PREFIX "PROCFILE" @@ -10,15 +11,15 @@ int procfile_adaptive_initial_allocation = 0; // if adaptive allocation is set, these store the // max values we have seen so far -uint32_t procfile_max_lines = PFLINES_INCREASE_STEP; -uint32_t procfile_max_words = PFWORDS_INCREASE_STEP; +size_t procfile_max_lines = PFLINES_INCREASE_STEP; +size_t procfile_max_words = PFWORDS_INCREASE_STEP; size_t procfile_max_allocation = PROCFILE_INCREMENT_BUFFER; // ---------------------------------------------------------------------------- // An array of words - -pfwords *pfwords_add(pfwords *fw, char *str) { +static inline pfwords *pfwords_add(pfwords *fw, char *str) NEVERNULL; +static inline pfwords *pfwords_add(pfwords *fw, char *str) { // debug(D_PROCFILE, PF_PREFIX ": adding word No %d: '%s'", fw->len, str); if(unlikely(fw->len == fw->size)) { @@ -33,10 +34,11 @@ pfwords *pfwords_add(pfwords *fw, char *str) { return fw; } -pfwords *pfwords_new(void) { +static inline pfwords *pfwords_new(void) NEVERNULL; +static inline pfwords *pfwords_new(void) { // debug(D_PROCFILE, PF_PREFIX ": initializing words"); - uint32_t size = (procfile_adaptive_initial_allocation) ? procfile_max_words : PFWORDS_INCREASE_STEP; + size_t size = (procfile_adaptive_initial_allocation) ? procfile_max_words : PFWORDS_INCREASE_STEP; pfwords *new = mallocz(sizeof(pfwords) + size * sizeof(char *)); new->len = 0; @@ -44,12 +46,12 @@ pfwords *pfwords_new(void) { return new; } -void pfwords_reset(pfwords *fw) { +static inline void pfwords_reset(pfwords *fw) { // debug(D_PROCFILE, PF_PREFIX ": reseting words"); fw->len = 0; } -void pfwords_free(pfwords *fw) { +static inline void pfwords_free(pfwords *fw) { // debug(D_PROCFILE, PF_PREFIX ": freeing words"); freez(fw); @@ -59,7 +61,8 @@ void pfwords_free(pfwords *fw) { // ---------------------------------------------------------------------------- // An array of lines -pflines *pflines_add(pflines *fl, uint32_t first_word) { +static inline pflines *pflines_add(pflines *fl, size_t first_word) NEVERNULL; +static inline pflines *pflines_add(pflines *fl, size_t first_word) { // debug(D_PROCFILE, PF_PREFIX ": adding line %d at word %d", fl->len, first_word); if(unlikely(fl->len == fl->size)) { @@ -75,10 +78,11 @@ pflines *pflines_add(pflines *fl, uint32_t first_word) { return fl; } -pflines *pflines_new(void) { +static inline pflines *pflines_new(void) NEVERNULL; +static inline pflines *pflines_new(void) { // debug(D_PROCFILE, PF_PREFIX ": initializing lines"); - uint32_t size = (unlikely(procfile_adaptive_initial_allocation)) ? procfile_max_words : PFLINES_INCREASE_STEP; + size_t size = (unlikely(procfile_adaptive_initial_allocation)) ? procfile_max_words : PFLINES_INCREASE_STEP; pflines *new = mallocz(sizeof(pflines) + size * sizeof(ffline)); new->len = 0; @@ -86,13 +90,13 @@ pflines *pflines_new(void) { return new; } -void pflines_reset(pflines *fl) { +static inline void pflines_reset(pflines *fl) { // debug(D_PROCFILE, PF_PREFIX ": reseting lines"); fl->len = 0; } -void pflines_free(pflines *fl) { +static inline void pflines_free(pflines *fl) { // debug(D_PROCFILE, PF_PREFIX ": freeing lines"); freez(fl); @@ -119,20 +123,20 @@ void procfile_close(procfile *ff) { freez(ff); } -procfile *procfile_parser(procfile *ff) { - debug(D_PROCFILE, PF_PREFIX ": Parsing file '%s'", ff->filename); +static inline void procfile_parser(procfile *ff) { + // debug(D_PROCFILE, PF_PREFIX ": Parsing file '%s'", ff->filename); - char *s = ff->data, *e = &ff->data[ff->len], *t = ff->data, quote = 0; - uint32_t l = 0, w = 0; - int opened = 0; + register char *s = ff->data, *e = &ff->data[ff->len], *t = ff->data; + register char *separators = ff->separators; + char quote = 0; + size_t l = 0, w = 0, opened = 0; ff->lines = pflines_add(ff->lines, w); - if(unlikely(!ff->lines)) goto cleanup; while(likely(s < e)) { // we are not at the end - switch(ff->separators[(uint8_t)(*s)]) { + switch(separators[(unsigned char)(*s)]) { case PF_CHAR_IS_OPEN: if(s == t) { opened++; @@ -144,7 +148,7 @@ procfile *procfile_parser(procfile *ff) { } else s++; - continue; + break; case PF_CHAR_IS_CLOSE: if(opened) { @@ -153,8 +157,6 @@ procfile *procfile_parser(procfile *ff) { if(!opened) { *s = '\0'; ff->words = pfwords_add(ff->words, t); - if(unlikely(!ff->words)) goto cleanup; - ff->lines->lines[l].words++; w++; @@ -165,7 +167,7 @@ procfile *procfile_parser(procfile *ff) { } else s++; - continue; + break; case PF_CHAR_IS_QUOTE: if(unlikely(!quote && s == t)) { @@ -179,8 +181,6 @@ procfile *procfile_parser(procfile *ff) { *s = '\0'; ff->words = pfwords_add(ff->words, t); - if(unlikely(!ff->words)) goto cleanup; - ff->lines->lines[l].words++; w++; @@ -188,55 +188,50 @@ procfile *procfile_parser(procfile *ff) { } else s++; - continue; + break; case PF_CHAR_IS_SEPARATOR: if(unlikely(quote || opened)) { // we are inside a quote s++; - continue; + break; } if(unlikely(s == t)) { // skip all leading white spaces t = ++s; - continue; + break; } // end of word *s = '\0'; ff->words = pfwords_add(ff->words, t); - if(unlikely(!ff->words)) goto cleanup; - ff->lines->lines[l].words++; w++; t = ++s; - continue; + break; case PF_CHAR_IS_NEWLINE: // end of line *s = '\0'; ff->words = pfwords_add(ff->words, t); - if(unlikely(!ff->words)) goto cleanup; - ff->lines->lines[l].words++; w++; // debug(D_PROCFILE, PF_PREFIX ": ended line %d with %d words", l, ff->lines->lines[l].words); ff->lines = pflines_add(ff->lines, w); - if(unlikely(!ff->lines)) goto cleanup; l++; t = ++s; - continue; + break; default: s++; - continue; + break; } } @@ -250,31 +245,21 @@ procfile *procfile_parser(procfile *ff) { } ff->words = pfwords_add(ff->words, t); - if(unlikely(!ff->words)) goto cleanup; - ff->lines->lines[l].words++; - w++; } - - return ff; - -cleanup: - error(PF_PREFIX ": Failed to parse file '%s'", ff->filename); - procfile_close(ff); - return NULL; } procfile *procfile_readall(procfile *ff) { debug(D_PROCFILE, PF_PREFIX ": Reading file '%s'.", ff->filename); - ssize_t s, r = 1, x; + ssize_t r = 1; ff->len = 0; while(likely(r > 0)) { - s = ff->len; - x = ff->size - s; + ssize_t s = ff->len; + ssize_t x = ff->size - s; - if(!x) { + if(unlikely(!x)) { debug(D_PROCFILE, PF_PREFIX ": Expanding data buffer for file '%s'.", ff->filename); ff = reallocz(ff, sizeof(procfile) + ff->size + PROCFILE_INCREMENT_BUFFER); @@ -301,8 +286,7 @@ procfile *procfile_readall(procfile *ff) { pflines_reset(ff->lines); pfwords_reset(ff->words); - - ff = procfile_parser(ff); + procfile_parser(ff); if(unlikely(procfile_adaptive_initial_allocation)) { if(unlikely(ff->len > procfile_max_allocation)) procfile_max_allocation = ff->len; @@ -316,13 +300,13 @@ procfile *procfile_readall(procfile *ff) { static void procfile_set_separators(procfile *ff, const char *separators) { static char def[256] = { [0 ... 255] = 0 }; - int i; if(unlikely(!def[255])) { // this is thread safe // we check that the last byte is non-zero // if it is zero, multiple threads may be executing this at the same time // setting in def[] the exact same values + int i; for(i = 0; likely(i < 256) ;i++) { if(unlikely(i == '\n' || i == '\r')) def[i] = PF_CHAR_IS_NEWLINE; else if(unlikely(isspace(i) || !isprint(i))) def[i] = PF_CHAR_IS_SEPARATOR; @@ -348,7 +332,7 @@ void procfile_set_quotes(procfile *ff, const char *quotes) { // remove all quotes int i; for(i = 0; i < 256 ; i++) - if(ff->separators[i] == PF_CHAR_IS_QUOTE) + if(unlikely(ff->separators[i] == PF_CHAR_IS_QUOTE)) ff->separators[i] = PF_CHAR_IS_WORD; // if nothing given, return @@ -366,7 +350,7 @@ void procfile_set_open_close(procfile *ff, const char *open, const char *close) // remove all open/close int i; for(i = 0; i < 256 ; i++) - if(ff->separators[i] == PF_CHAR_IS_OPEN || ff->separators[i] == PF_CHAR_IS_CLOSE) + if(unlikely(ff->separators[i] == PF_CHAR_IS_OPEN || ff->separators[i] == PF_CHAR_IS_CLOSE)) ff->separators[i] = PF_CHAR_IS_WORD; // if nothing given, return @@ -405,12 +389,6 @@ procfile *procfile_open(const char *filename, const char *separators, uint32_t f ff->lines = pflines_new(); ff->words = pfwords_new(); - if(unlikely(!ff->lines || !ff->words)) { - error(PF_PREFIX ": Cannot initialize parser for file '%s'", filename); - procfile_close(ff); - return NULL; - } - procfile_set_separators(ff, separators); debug(D_PROCFILE, "File '%s' opened.", filename); @@ -442,20 +420,20 @@ procfile *procfile_reopen(procfile *ff, const char *filename, const char *separa // example parsing of procfile data void procfile_print(procfile *ff) { - uint32_t lines = procfile_lines(ff), l; - uint32_t words, w; + size_t lines = procfile_lines(ff), l; char *s; - debug(D_PROCFILE, "File '%s' with %u lines and %u words", ff->filename, ff->lines->len, ff->words->len); + debug(D_PROCFILE, "File '%s' with %zu lines and %zu words", ff->filename, ff->lines->len, ff->words->len); for(l = 0; likely(l < lines) ;l++) { - words = procfile_linewords(ff, l); + size_t words = procfile_linewords(ff, l); - debug(D_PROCFILE, " line %u starts at word %u and has %u words", l, ff->lines->lines[l].first, ff->lines->lines[l].words); + debug(D_PROCFILE, " line %zu starts at word %zu and has %zu words", l, ff->lines->lines[l].first, ff->lines->lines[l].words); + size_t w; for(w = 0; likely(w < words) ;w++) { s = procfile_lineword(ff, l, w); - debug(D_PROCFILE, " [%u.%u] '%s'", l, w, s); + debug(D_PROCFILE, " [%zu.%zu] '%s'", l, w, s); } } } diff --git a/src/procfile.h b/src/procfile.h index 5e00b2584..a586ba48d 100644 --- a/src/procfile.h +++ b/src/procfile.h @@ -30,8 +30,8 @@ // An array of words typedef struct { - uint32_t len; // used entries - uint32_t size; // capacity + size_t len; // used entries + size_t size; // capacity char *words[]; // array of pointers } pfwords; @@ -40,15 +40,15 @@ typedef struct { // An array of lines typedef struct { - uint32_t words; // how many words this line has - uint32_t first; // the id of the first word of this line - // in the words array + size_t words; // how many words this line has + size_t first; // the id of the first word of this line + // in the words array } ffline; typedef struct { - uint32_t len; // used entries - uint32_t size; // capacity - ffline lines[]; // array of lines + size_t len; // used entries + size_t size; // capacity + ffline lines[]; // array of lines } pflines; @@ -61,13 +61,13 @@ typedef struct { typedef struct { char filename[FILENAME_MAX + 1]; uint32_t flags; - int fd; // the file desriptor - size_t len; // the bytes we have placed into data - size_t size; // the bytes we have allocated for data + int fd; // the file desriptor + size_t len; // the bytes we have placed into data + size_t size; // the bytes we have allocated for data pflines *lines; pfwords *words; char separators[256]; - char data[]; // allocated buffer to keep file contents + char data[]; // allocated buffer to keep file contents } procfile; // close the proc file and free all related memory diff --git a/src/registry.c b/src/registry.c index a0fb629ad..d223cd6f1 100644 --- a/src/registry.c +++ b/src/registry.c @@ -1,1015 +1,29 @@ #include "common.h" -// ---------------------------------------------------------------------------- -// TODO -// -// 1. the default tracking cookie expires in 1 year, but the persons are not -// removed from the db - this means the database only grows - ideally the -// database should be cleaned in registry_save() for both on-disk and -// on-memory entries. -// -// Cleanup: -// i. Find all the PERSONs that have expired cookie -// ii. For each of their PERSON_URLs: -// - decrement the linked MACHINE links -// - if the linked MACHINE has no other links, remove the linked MACHINE too -// - remove the PERSON_URL -// -// 2. add protection to prevent abusing the registry by flooding it with -// requests to fill the memory and crash it. -// -// Possible protections: -// - limit the number of URLs per person -// - limit the number of URLs per machine -// - limit the number of persons -// - limit the number of machines -// - [DONE] limit the size of URLs -// - [DONE] limit the size of PERSON_URL names -// - limit the number of requests that add data to the registry, -// per client IP per hour -// -// 3. lower memory requirements -// -// - embed avl structures directly into registry objects, instead of DICTIONARY -// - store GUIDs in memory as UUID instead of char * -// (this will also remove the index hash, since UUIDs can be compared directly) -// - do not track persons using the demo machines only -// (i.e. start tracking them only when they access a non-demo machine) -// - [DONE] do not track custom dashboards by default - -#define REGISTRY_URL_FLAGS_DEFAULT 0x00 -#define REGISTRY_URL_FLAGS_EXPIRED 0x01 - -#define DICTIONARY_FLAGS DICTIONARY_FLAG_VALUE_LINK_DONT_CLONE | DICTIONARY_FLAG_NAME_LINK_DONT_CLONE - -// ---------------------------------------------------------------------------- -// COMMON structures - -struct registry { - int enabled; - - char machine_guid[36 + 1]; - - // entries counters / statistics - unsigned long long persons_count; - unsigned long long machines_count; - unsigned long long usages_count; - unsigned long long urls_count; - unsigned long long persons_urls_count; - unsigned long long machines_urls_count; - unsigned long long log_count; - - // memory counters / statistics - unsigned long long persons_memory; - unsigned long long machines_memory; - unsigned long long urls_memory; - unsigned long long persons_urls_memory; - unsigned long long machines_urls_memory; - - // configuration - unsigned long long save_registry_every_entries; - char *registry_domain; - char *hostname; - char *registry_to_announce; - time_t persons_expiration; // seconds to expire idle persons - int verify_cookies_redirects; - - size_t max_url_length; - size_t max_name_length; - - // file/path names - char *pathname; - char *db_filename; - char *log_filename; - char *machine_guid_filename; - - // open files - FILE *log_fp; - - // the database - DICTIONARY *persons; // dictionary of PERSON *, with key the PERSON.guid - DICTIONARY *machines; // dictionary of MACHINE *, with key the MACHINE.guid - DICTIONARY *urls; // dictionary of URL *, with key the URL.url - - // concurrency locking - // we keep different locks for different things - // so that many tasks can be completed in parallel - pthread_mutex_t persons_lock; - pthread_mutex_t machines_lock; - pthread_mutex_t urls_lock; - pthread_mutex_t person_urls_lock; - pthread_mutex_t machine_urls_lock; - pthread_mutex_t log_lock; -} registry; - - -// ---------------------------------------------------------------------------- -// URL structures -// Save memory by de-duplicating URLs -// so instead of storing URLs all over the place -// we store them here and we keep pointers elsewhere - -struct url { - uint32_t links; // the number of links to this URL - when none is left, we free it - uint16_t len; // the length of the URL in bytes - char url[1]; // the URL - dynamically allocated to more size -}; -typedef struct url URL; - - -// ---------------------------------------------------------------------------- -// MACHINE structures - -// For each MACHINE-URL pair we keep this -struct machine_url { - URL *url; // de-duplicated URL -// DICTIONARY *persons; // dictionary of PERSON * - - uint8_t flags; - uint32_t first_t; // the first time we saw this - uint32_t last_t; // the last time we saw this - uint32_t usages; // how many times this has been accessed -}; -typedef struct machine_url MACHINE_URL; - -// A machine -struct machine { - char guid[36 + 1]; // the GUID - - uint32_t links; // the number of PERSON_URLs linked to this machine - - DICTIONARY *urls; // MACHINE_URL * - - uint32_t first_t; // the first time we saw this - uint32_t last_t; // the last time we saw this - uint32_t usages; // how many times this has been accessed -}; -typedef struct machine MACHINE; - - -// ---------------------------------------------------------------------------- -// PERSON structures - -// for each PERSON-URL pair we keep this -struct person_url { - URL *url; // de-duplicated URL - MACHINE *machine; // link the MACHINE of this URL - - uint8_t flags; - uint32_t first_t; // the first time we saw this - uint32_t last_t; // the last time we saw this - uint32_t usages; // how many times this has been accessed - - char name[1]; // the name of the URL, as known by the user - // dynamically allocated to fit properly -}; -typedef struct person_url PERSON_URL; - -// A person -struct person { - char guid[36 + 1]; // the person GUID - - DICTIONARY *urls; // dictionary of PERSON_URL * - - uint32_t first_t; // the first time we saw this - uint32_t last_t; // the last time we saw this - uint32_t usages; // how many times this has been accessed -}; -typedef struct person PERSON; +#include "registry_internals.h" +#define REGISTRY_STATUS_OK "ok" +#define REGISTRY_STATUS_FAILED "failed" +#define REGISTRY_STATUS_DISABLED "disabled" // ---------------------------------------------------------------------------- // REGISTRY concurrency locking -static inline void registry_persons_lock(void) { - pthread_mutex_lock(®istry.persons_lock); -} - -static inline void registry_persons_unlock(void) { - pthread_mutex_unlock(®istry.persons_lock); -} - -static inline void registry_machines_lock(void) { - pthread_mutex_lock(®istry.machines_lock); -} - -static inline void registry_machines_unlock(void) { - pthread_mutex_unlock(®istry.machines_lock); -} - -static inline void registry_urls_lock(void) { - pthread_mutex_lock(®istry.urls_lock); -} - -static inline void registry_urls_unlock(void) { - pthread_mutex_unlock(®istry.urls_lock); -} - -// ideally, we should not lock the whole registry for -// updating a person's urls. -// however, to save the memory required for keeping a -// mutex (40 bytes) per person, we do... -static inline void registry_person_urls_lock(PERSON *p) { - (void)p; - pthread_mutex_lock(®istry.person_urls_lock); -} - -static inline void registry_person_urls_unlock(PERSON *p) { - (void)p; - pthread_mutex_unlock(®istry.person_urls_lock); -} - -// ideally, we should not lock the whole registry for -// updating a machine's urls. -// however, to save the memory required for keeping a -// mutex (40 bytes) per machine, we do... -static inline void registry_machine_urls_lock(MACHINE *m) { - (void)m; - pthread_mutex_lock(®istry.machine_urls_lock); -} - -static inline void registry_machine_urls_unlock(MACHINE *m) { - (void)m; - pthread_mutex_unlock(®istry.machine_urls_lock); -} - -static inline void registry_log_lock(void) { - pthread_mutex_lock(®istry.log_lock); -} - -static inline void registry_log_unlock(void) { - pthread_mutex_unlock(®istry.log_lock); -} - - -// ---------------------------------------------------------------------------- -// common functions - -// parse a GUID and re-generated to be always lower case -// this is used as a protection against the variations of GUIDs -static inline int registry_regenerate_guid(const char *guid, char *result) { - uuid_t uuid; - if(unlikely(uuid_parse(guid, uuid) == -1)) { - info("Registry: GUID '%s' is not a valid GUID.", guid); - return -1; - } - else { - uuid_unparse_lower(uuid, result); - -#ifdef NETDATA_INTERNAL_CHECKS - if(strcmp(guid, result)) - info("Registry: source GUID '%s' and re-generated GUID '%s' differ!", guid, result); -#endif /* NETDATA_INTERNAL_CHECKS */ - } - - return 0; -} - -// make sure the names of the machines / URLs do not contain any tabs -// (which are used as our separator in the database files) -// and are properly trimmed (before and after) -static inline char *registry_fix_machine_name(char *name, size_t *len) { - char *s = name?name:""; - - // skip leading spaces - while(*s && isspace(*s)) s++; - - // make sure all spaces are a SPACE - char *t = s; - while(*t) { - if(unlikely(isspace(*t))) - *t = ' '; - - t++; - } - - // remove trailing spaces - while(--t >= s) { - if(*t == ' ') - *t = '\0'; - else - break; - } - t++; - - if(likely(len)) - *len = (t - s); - - return s; -} - -static inline char *registry_fix_url(char *url, size_t *len) { - return registry_fix_machine_name(url, len); -} - - -// ---------------------------------------------------------------------------- -// forward definition of functions - -extern PERSON *registry_request_access(char *person_guid, char *machine_guid, char *url, char *name, time_t when); -extern PERSON *registry_request_delete(char *person_guid, char *machine_guid, char *url, char *delete_url, time_t when); - - -// ---------------------------------------------------------------------------- -// URL - -static inline URL *registry_url_allocate_nolock(const char *url, size_t urllen) { - // protection from too big URLs - if(urllen > registry.max_url_length) - urllen = registry.max_url_length; - - debug(D_REGISTRY, "Registry: registry_url_allocate_nolock('%s'): allocating %zu bytes", url, sizeof(URL) + urllen); - URL *u = mallocz(sizeof(URL) + urllen); - - // a simple strcpy() should do the job - // but I prefer to be safe, since the caller specified urllen - u->len = (uint16_t)urllen; - strncpyz(u->url, url, u->len); - u->links = 0; - - registry.urls_memory += sizeof(URL) + urllen; - - debug(D_REGISTRY, "Registry: registry_url_allocate_nolock('%s'): indexing it", url); - dictionary_set(registry.urls, u->url, u, sizeof(URL)); - - return u; -} - -static inline URL *registry_url_get_nolock(const char *url, size_t urllen) { - debug(D_REGISTRY, "Registry: registry_url_get_nolock('%s')", url); - - URL *u = dictionary_get(registry.urls, url); - if(!u) { - u = registry_url_allocate_nolock(url, urllen); - registry.urls_count++; - } - - return u; -} - -static inline URL *registry_url_get(const char *url, size_t urllen) { - debug(D_REGISTRY, "Registry: registry_url_get('%s')", url); - - registry_urls_lock(); - - URL *u = registry_url_get_nolock(url, urllen); - - registry_urls_unlock(); - - return u; -} - -static inline void registry_url_link_nolock(URL *u) { - u->links++; - debug(D_REGISTRY, "Registry: registry_url_link_nolock('%s'): URL has now %u links", u->url, u->links); -} - -static inline void registry_url_unlink_nolock(URL *u) { - u->links--; - if(!u->links) { - debug(D_REGISTRY, "Registry: registry_url_unlink_nolock('%s'): No more links for this URL", u->url); - dictionary_del(registry.urls, u->url); - freez(u); - } - else - debug(D_REGISTRY, "Registry: registry_url_unlink_nolock('%s'): URL has %u links left", u->url, u->links); -} - - -// ---------------------------------------------------------------------------- -// MACHINE - -static inline MACHINE *registry_machine_find(const char *machine_guid) { - debug(D_REGISTRY, "Registry: registry_machine_find('%s')", machine_guid); - return dictionary_get(registry.machines, machine_guid); -} - -static inline MACHINE_URL *registry_machine_url_allocate(MACHINE *m, URL *u, time_t when) { - debug(D_REGISTRY, "registry_machine_link_to_url('%s', '%s'): allocating %zu bytes", m->guid, u->url, sizeof(MACHINE_URL)); - - MACHINE_URL *mu = mallocz(sizeof(MACHINE_URL)); - - // mu->persons = dictionary_create(DICTIONARY_FLAGS); - // dictionary_set(mu->persons, p->guid, p, sizeof(PERSON)); - - mu->first_t = mu->last_t = (uint32_t)when; - mu->usages = 1; - mu->url = u; - mu->flags = REGISTRY_URL_FLAGS_DEFAULT; - - registry.machines_urls_memory += sizeof(MACHINE_URL); - - debug(D_REGISTRY, "registry_machine_link_to_url('%s', '%s'): indexing URL in machine", m->guid, u->url); - dictionary_set(m->urls, u->url, mu, sizeof(MACHINE_URL)); - registry_url_link_nolock(u); - - return mu; -} - -static inline MACHINE *registry_machine_allocate(const char *machine_guid, time_t when) { - debug(D_REGISTRY, "Registry: registry_machine_allocate('%s'): creating new machine, sizeof(MACHINE)=%zu", machine_guid, sizeof(MACHINE)); - - MACHINE *m = mallocz(sizeof(MACHINE)); - - strncpyz(m->guid, machine_guid, 36); - - debug(D_REGISTRY, "Registry: registry_machine_allocate('%s'): creating dictionary of urls", machine_guid); - m->urls = dictionary_create(DICTIONARY_FLAGS); - - m->first_t = m->last_t = (uint32_t)when; - m->usages = 0; - - registry.machines_memory += sizeof(MACHINE); - - registry.machines_count++; - dictionary_set(registry.machines, m->guid, m, sizeof(MACHINE)); - - return m; -} - -// 1. validate machine GUID -// 2. if it is valid, find it or create it and return it -// 3. if it is not valid, return NULL -static inline MACHINE *registry_machine_get(const char *machine_guid, time_t when) { - MACHINE *m = NULL; - - registry_machines_lock(); - - if(likely(machine_guid && *machine_guid)) { - // validate it is a GUID - char buf[36 + 1]; - if(unlikely(registry_regenerate_guid(machine_guid, buf) == -1)) - info("Registry: machine guid '%s' is not a valid guid. Ignoring it.", machine_guid); - else { - machine_guid = buf; - m = registry_machine_find(machine_guid); - if(!m) m = registry_machine_allocate(machine_guid, when); - } - } - - registry_machines_unlock(); - - return m; -} - - -// ---------------------------------------------------------------------------- -// PERSON - -static inline PERSON *registry_person_find(const char *person_guid) { - debug(D_REGISTRY, "Registry: registry_person_find('%s')", person_guid); - return dictionary_get(registry.persons, person_guid); -} - -static inline PERSON_URL *registry_person_url_allocate(PERSON *p, MACHINE *m, URL *u, char *name, size_t namelen, time_t when) { - // protection from too big names - if(namelen > registry.max_name_length) - namelen = registry.max_name_length; - - debug(D_REGISTRY, "registry_person_url_allocate('%s', '%s', '%s'): allocating %zu bytes", p->guid, m->guid, u->url, - sizeof(PERSON_URL) + namelen); - - PERSON_URL *pu = mallocz(sizeof(PERSON_URL) + namelen); - - // a simple strcpy() should do the job - // but I prefer to be safe, since the caller specified urllen - strncpyz(pu->name, name, namelen); - - pu->machine = m; - pu->first_t = pu->last_t = when; - pu->usages = 1; - pu->url = u; - pu->flags = REGISTRY_URL_FLAGS_DEFAULT; - m->links++; - - registry.persons_urls_memory += sizeof(PERSON_URL) + namelen; - - debug(D_REGISTRY, "registry_person_url_allocate('%s', '%s', '%s'): indexing URL in person", p->guid, m->guid, u->url); - dictionary_set(p->urls, u->url, pu, sizeof(PERSON_URL)); - registry_url_link_nolock(u); - - return pu; -} - -static inline PERSON_URL *registry_person_url_reallocate(PERSON *p, MACHINE *m, URL *u, char *name, size_t namelen, time_t when, PERSON_URL *pu) { - // this function is needed to change the name of a PERSON_URL - - debug(D_REGISTRY, "registry_person_url_reallocate('%s', '%s', '%s'): allocating %zu bytes", p->guid, m->guid, u->url, - sizeof(PERSON_URL) + namelen); - - PERSON_URL *tpu = registry_person_url_allocate(p, m, u, name, namelen, when); - tpu->first_t = pu->first_t; - tpu->last_t = pu->last_t; - tpu->usages = pu->usages; - - // ok, these are a hack - since the registry_person_url_allocate() is - // adding these, we have to subtract them - tpu->machine->links--; - registry.persons_urls_memory -= sizeof(PERSON_URL) + strlen(pu->name); - registry_url_unlink_nolock(u); - - freez(pu); - - return tpu; -} - -static inline PERSON *registry_person_allocate(const char *person_guid, time_t when) { - PERSON *p = NULL; - - debug(D_REGISTRY, "Registry: registry_person_allocate('%s'): allocating new person, sizeof(PERSON)=%zu", (person_guid)?person_guid:"", sizeof(PERSON)); - - p = mallocz(sizeof(PERSON)); - - if(!person_guid) { - for (; ;) { - uuid_t uuid; - uuid_generate(uuid); - uuid_unparse_lower(uuid, p->guid); - - debug(D_REGISTRY, "Registry: Checking if the generated person guid '%s' is unique", p->guid); - if (!dictionary_get(registry.persons, p->guid)) { - debug(D_REGISTRY, "Registry: generated person guid '%s' is unique", p->guid); - break; - } - else - info("Registry: generated person guid '%s' found in the registry. Retrying...", p->guid); - } - } - else - strncpyz(p->guid, person_guid, 36); - - debug(D_REGISTRY, "Registry: registry_person_allocate('%s'): creating dictionary of urls", p->guid); - p->urls = dictionary_create(DICTIONARY_FLAGS); - - p->first_t = p->last_t = when; - p->usages = 0; - - registry.persons_memory += sizeof(PERSON); - - registry.persons_count++; - dictionary_set(registry.persons, p->guid, p, sizeof(PERSON)); - - return p; -} - - -// 1. validate person GUID -// 2. if it is valid, find it -// 3. if it is not valid, create a new one -// 4. return it -static inline PERSON *registry_person_get(const char *person_guid, time_t when) { - PERSON *p = NULL; - - registry_persons_lock(); - - if(person_guid && *person_guid) { - char buf[36 + 1]; - // validate it is a GUID - if(unlikely(registry_regenerate_guid(person_guid, buf) == -1)) - info("Registry: person guid '%s' is not a valid guid. Ignoring it.", person_guid); - else { - person_guid = buf; - p = registry_person_find(person_guid); - } - } - - if(!p) p = registry_person_allocate(NULL, when); - - registry_persons_unlock(); - - return p; -} - -// ---------------------------------------------------------------------------- -// LINKING OF OBJECTS - -static inline PERSON_URL *registry_person_link_to_url(PERSON *p, MACHINE *m, URL *u, char *name, size_t namelen, time_t when) { - debug(D_REGISTRY, "registry_person_link_to_url('%s', '%s', '%s'): searching for URL in person", p->guid, m->guid, u->url); - - registry_person_urls_lock(p); - - PERSON_URL *pu = dictionary_get(p->urls, u->url); - if(!pu) { - debug(D_REGISTRY, "registry_person_link_to_url('%s', '%s', '%s'): not found", p->guid, m->guid, u->url); - pu = registry_person_url_allocate(p, m, u, name, namelen, when); - registry.persons_urls_count++; - } - else { - debug(D_REGISTRY, "registry_person_link_to_url('%s', '%s', '%s'): found", p->guid, m->guid, u->url); - pu->usages++; - if(likely(pu->last_t < (uint32_t)when)) pu->last_t = when; - - if(pu->machine != m) { - MACHINE_URL *mu = dictionary_get(pu->machine->urls, u->url); - if(mu) { - info("registry_person_link_to_url('%s', '%s', '%s'): URL switched machines (old was '%s') - expiring it from previous machine.", - p->guid, m->guid, u->url, pu->machine->guid); - mu->flags |= REGISTRY_URL_FLAGS_EXPIRED; - } - else { - info("registry_person_link_to_url('%s', '%s', '%s'): URL switched machines (old was '%s') - but the URL is not linked to the old machine.", - p->guid, m->guid, u->url, pu->machine->guid); - } - - pu->machine->links--; - pu->machine = m; - } - - if(strcmp(pu->name, name)) { - // the name of the PERSON_URL has changed ! - pu = registry_person_url_reallocate(p, m, u, name, namelen, when, pu); - } - } - - p->usages++; - if(likely(p->last_t < (uint32_t)when)) p->last_t = when; - - if(pu->flags & REGISTRY_URL_FLAGS_EXPIRED) { - info("registry_person_link_to_url('%s', '%s', '%s'): accessing an expired URL. Re-enabling URL.", p->guid, m->guid, u->url); - pu->flags &= ~REGISTRY_URL_FLAGS_EXPIRED; - } - - registry_person_urls_unlock(p); - - return pu; -} - -static inline MACHINE_URL *registry_machine_link_to_url(PERSON *p, MACHINE *m, URL *u, time_t when) { - debug(D_REGISTRY, "registry_machine_link_to_url('%s', '%s', '%s'): searching for URL in machine", p->guid, m->guid, u->url); - - registry_machine_urls_lock(m); - - MACHINE_URL *mu = dictionary_get(m->urls, u->url); - if(!mu) { - debug(D_REGISTRY, "registry_machine_link_to_url('%s', '%s', '%s'): not found", p->guid, m->guid, u->url); - mu = registry_machine_url_allocate(m, u, when); - registry.machines_urls_count++; - } - else { - debug(D_REGISTRY, "registry_machine_link_to_url('%s', '%s', '%s'): found", p->guid, m->guid, u->url); - mu->usages++; - if(likely(mu->last_t < (uint32_t)when)) mu->last_t = when; - } - - //debug(D_REGISTRY, "registry_machine_link_to_url('%s', '%s', '%s'): indexing person in machine", p->guid, m->guid, u->url); - //dictionary_set(mu->persons, p->guid, p, sizeof(PERSON)); - - m->usages++; - if(likely(m->last_t < (uint32_t)when)) m->last_t = when; - - if(mu->flags & REGISTRY_URL_FLAGS_EXPIRED) { - info("registry_machine_link_to_url('%s', '%s', '%s'): accessing an expired URL.", p->guid, m->guid, u->url); - mu->flags &= ~REGISTRY_URL_FLAGS_EXPIRED; - } - - registry_machine_urls_unlock(m); - - return mu; -} - -// ---------------------------------------------------------------------------- -// REGISTRY LOG LOAD/SAVE - -static inline int registry_should_save_db(void) { - debug(D_REGISTRY, "log entries %llu, max %llu", registry.log_count, registry.save_registry_every_entries); - return registry.log_count > registry.save_registry_every_entries; -} - -static inline void registry_log(const char action, PERSON *p, MACHINE *m, URL *u, char *name) { - if(likely(registry.log_fp)) { - // we lock only if the file is open - // to allow replaying the log at registry_log_load() - registry_log_lock(); - - if(unlikely(fprintf(registry.log_fp, "%c\t%08x\t%s\t%s\t%s\t%s\n", - action, - p->last_t, - p->guid, - m->guid, - name, - u->url) < 0)) - error("Registry: failed to save log. Registry data may be lost in case of abnormal restart."); - - // we increase the counter even on failures - // so that the registry will be saved periodically - registry.log_count++; - - registry_log_unlock(); - - // this must be outside the log_lock(), or a deadlock will happen. - // registry_save() checks the same inside the log_lock, so only - // one thread will save the db - if(unlikely(registry_should_save_db())) - registry_save(); - } -} - -static inline int registry_log_open_nolock(void) { - if(registry.log_fp) - fclose(registry.log_fp); - - registry.log_fp = fopen(registry.log_filename, "a"); - - if(registry.log_fp) { - if (setvbuf(registry.log_fp, NULL, _IOLBF, 0) != 0) - error("Cannot set line buffering on registry log file."); - return 0; - } - - error("Cannot open registry log file '%s'. Registry data will be lost in case of netdata or server crash.", registry.log_filename); - return -1; -} - -static inline void registry_log_close_nolock(void) { - if(registry.log_fp) { - fclose(registry.log_fp); - registry.log_fp = NULL; - } -} - -static inline void registry_log_recreate_nolock(void) { - if(registry.log_fp != NULL) { - registry_log_close_nolock(); - - // open it with truncate - registry.log_fp = fopen(registry.log_filename, "w"); - if(registry.log_fp) fclose(registry.log_fp); - else error("Cannot truncate registry log '%s'", registry.log_filename); - - registry.log_fp = NULL; - - registry_log_open_nolock(); - } -} - -int registry_log_load(void) { - char *s, buf[4096 + 1]; - size_t line = -1; - - // closing the log is required here - // otherwise we will append to it the values we read - registry_log_close_nolock(); - - debug(D_REGISTRY, "Registry: loading active db from: %s", registry.log_filename); - FILE *fp = fopen(registry.log_filename, "r"); - if(!fp) - error("Registry: cannot open registry file: %s", registry.log_filename); - else { - line = 0; - size_t len = 0; - while ((s = fgets_trim_len(buf, 4096, fp, &len))) { - line++; - - switch (s[0]) { - case 'A': // accesses - case 'D': // deletes - - // verify it is valid - if (unlikely(len < 85 || s[1] != '\t' || s[10] != '\t' || s[47] != '\t' || s[84] != '\t')) { - error("Registry: log line %zu is wrong (len = %zu).", line, len); - continue; - } - s[1] = s[10] = s[47] = s[84] = '\0'; - - // get the variables - time_t when = strtoul(&s[2], NULL, 16); - char *person_guid = &s[11]; - char *machine_guid = &s[48]; - char *name = &s[85]; - - // skip the name to find the url - char *url = name; - while(*url && *url != '\t') url++; - if(!*url) { - error("Registry: log line %zu does not have a url.", line); - continue; - } - *url++ = '\0'; - - // make sure the person exists - // without this, a new person guid will be created - PERSON *p = registry_person_find(person_guid); - if(!p) p = registry_person_allocate(person_guid, when); - - if(s[0] == 'A') - registry_request_access(p->guid, machine_guid, url, name, when); - else - registry_request_delete(p->guid, machine_guid, url, name, when); - - registry.log_count++; - break; - - default: - error("Registry: ignoring line %zu of filename '%s': %s.", line, registry.log_filename, s); - break; - } - } - - fclose(fp); - } - - // open the log again - registry_log_open_nolock(); - - return line; -} - - -// ---------------------------------------------------------------------------- -// REGISTRY REQUESTS - -PERSON *registry_request_access(char *person_guid, char *machine_guid, char *url, char *name, time_t when) { - debug(D_REGISTRY, "registry_request_access('%s', '%s', '%s'): NEW REQUEST", (person_guid)?person_guid:"", machine_guid, url); - - MACHINE *m = registry_machine_get(machine_guid, when); - if(!m) return NULL; - - // make sure the name is valid - size_t namelen; - name = registry_fix_machine_name(name, &namelen); - - size_t urllen; - url = registry_fix_url(url, &urllen); - - URL *u = registry_url_get(url, urllen); - PERSON *p = registry_person_get(person_guid, when); - - registry_person_link_to_url(p, m, u, name, namelen, when); - registry_machine_link_to_url(p, m, u, when); - - registry_log('A', p, m, u, name); - - registry.usages_count++; - return p; -} - -// verify the person, the machine and the URL exist in our DB -PERSON_URL *registry_verify_request(char *person_guid, char *machine_guid, char *url, PERSON **pp, MACHINE **mm) { - char pbuf[36 + 1], mbuf[36 + 1]; - - if(!person_guid || !*person_guid || !machine_guid || !*machine_guid || !url || !*url) { - info("Registry Request Verification: invalid request! person: '%s', machine '%s', url '%s'", person_guid?person_guid:"UNSET", machine_guid?machine_guid:"UNSET", url?url:"UNSET"); - return NULL; - } - - // normalize the url - url = registry_fix_url(url, NULL); - - // make sure the person GUID is valid - if(registry_regenerate_guid(person_guid, pbuf) == -1) { - info("Registry Request Verification: invalid person GUID, person: '%s', machine '%s', url '%s'", person_guid, machine_guid, url); - return NULL; - } - person_guid = pbuf; - - // make sure the machine GUID is valid - if(registry_regenerate_guid(machine_guid, mbuf) == -1) { - info("Registry Request Verification: invalid machine GUID, person: '%s', machine '%s', url '%s'", person_guid, machine_guid, url); - return NULL; - } - machine_guid = mbuf; - - // make sure the machine exists - MACHINE *m = registry_machine_find(machine_guid); - if(!m) { - info("Registry Request Verification: machine not found, person: '%s', machine '%s', url '%s'", person_guid, machine_guid, url); - return NULL; - } - if(mm) *mm = m; - - // make sure the person exist - PERSON *p = registry_person_find(person_guid); - if(!p) { - info("Registry Request Verification: person not found, person: '%s', machine '%s', url '%s'", person_guid, machine_guid, url); - return NULL; - } - if(pp) *pp = p; - - PERSON_URL *pu = dictionary_get(p->urls, url); - if(!pu) { - info("Registry Request Verification: URL not found for person, person: '%s', machine '%s', url '%s'", person_guid, machine_guid, url); - return NULL; - } - return pu; +static inline void registry_lock(void) { + pthread_mutex_lock(®istry.lock); } -PERSON *registry_request_delete(char *person_guid, char *machine_guid, char *url, char *delete_url, time_t when) { - (void)when; - - PERSON *p = NULL; - MACHINE *m = NULL; - PERSON_URL *pu = registry_verify_request(person_guid, machine_guid, url, &p, &m); - if(!pu || !p || !m) return NULL; - - // normalize the url - delete_url = registry_fix_url(delete_url, NULL); - - // make sure the user is not deleting the url it uses - if(!strcmp(delete_url, pu->url->url)) { - info("Registry Delete Request: delete URL is the one currently accessed, person: '%s', machine '%s', url '%s', delete url '%s'", p->guid, m->guid, pu->url->url, delete_url); - return NULL; - } - - registry_person_urls_lock(p); - - PERSON_URL *dpu = dictionary_get(p->urls, delete_url); - if(!dpu) { - info("Registry Delete Request: URL not found for person: '%s', machine '%s', url '%s', delete url '%s'", p->guid, m->guid, pu->url->url, delete_url); - registry_person_urls_unlock(p); - return NULL; - } - - registry_log('D', p, m, pu->url, dpu->url->url); - - dictionary_del(p->urls, dpu->url->url); - registry_url_unlink_nolock(dpu->url); - freez(dpu); - - registry_person_urls_unlock(p); - return p; -} - - -// a structure to pass to the dictionary_get_all() callback handler -struct machine_request_callback_data { - MACHINE *find_this_machine; - PERSON_URL *result; -}; - -// the callback function -// this will be run for every PERSON_URL of this PERSON -int machine_request_callback(void *entry, void *data) { - PERSON_URL *mypu = (PERSON_URL *)entry; - struct machine_request_callback_data *myrdata = (struct machine_request_callback_data *)data; - - if(mypu->machine == myrdata->find_this_machine) { - myrdata->result = mypu; - return -1; // this will also stop the walk through - } - - return 0; // continue -} - -MACHINE *registry_request_machine(char *person_guid, char *machine_guid, char *url, char *request_machine, time_t when) { - (void)when; - - char mbuf[36 + 1]; - - PERSON *p = NULL; - MACHINE *m = NULL; - PERSON_URL *pu = registry_verify_request(person_guid, machine_guid, url, &p, &m); - if(!pu || !p || !m) return NULL; - - // make sure the machine GUID is valid - if(registry_regenerate_guid(request_machine, mbuf) == -1) { - info("Registry Machine URLs request: invalid machine GUID, person: '%s', machine '%s', url '%s', request machine '%s'", p->guid, m->guid, pu->url->url, request_machine); - return NULL; - } - request_machine = mbuf; - - // make sure the machine exists - m = registry_machine_find(request_machine); - if(!m) { - info("Registry Machine URLs request: machine not found, person: '%s', machine '%s', url '%s', request machine '%s'", p->guid, machine_guid, pu->url->url, request_machine); - return NULL; - } - - // Verify the user has in the past accessed this machine - // We will walk through the PERSON_URLs to find the machine - // linking to our machine - - // a structure to pass to the dictionary_get_all() callback handler - struct machine_request_callback_data rdata = { m, NULL }; - - // request a walk through on the dictionary - // no need for locking here, the underlying dictionary has its own - dictionary_get_all(p->urls, machine_request_callback, &rdata); - - if(rdata.result) - return m; - - return NULL; +static inline void registry_unlock(void) { + pthread_mutex_unlock(®istry.lock); } // ---------------------------------------------------------------------------- -// REGISTRY JSON generation - -#define REGISTRY_STATUS_OK "ok" -#define REGISTRY_STATUS_FAILED "failed" -#define REGISTRY_STATUS_DISABLED "disabled" - -int registry_verify_cookies_redirects(void) { - return registry.verify_cookies_redirects; -} - -const char *registry_to_announce(void) { - return registry.registry_to_announce; -} +// COOKIES -void registry_set_cookie(struct web_client *w, const char *guid) { +static void registry_set_cookie(struct web_client *w, const char *guid) { char edate[100]; - time_t et = time(NULL) + registry.persons_expiration; + time_t et = now_realtime_sec() + registry.persons_expiration; struct tm etmbuf, *etm = gmtime_r(&et, &etmbuf); strftime(edate, sizeof(edate), "%a, %d %b %Y %H:%M:%S %Z", etm); @@ -1019,52 +33,50 @@ void registry_set_cookie(struct web_client *w, const char *guid) { snprintfz(w->cookie2, COOKIE_MAX, NETDATA_REGISTRY_COOKIE_NAME "=%s; Domain=%s; Expires=%s", guid, registry.registry_domain, edate); } -static inline void registry_set_person_cookie(struct web_client *w, PERSON *p) { +static inline void registry_set_person_cookie(struct web_client *w, REGISTRY_PERSON *p) { registry_set_cookie(w, p->guid); } + +// ---------------------------------------------------------------------------- +// JSON GENERATION + static inline void registry_json_header(struct web_client *w, const char *action, const char *status) { buffer_flush(w->response.data); w->response.data->contenttype = CT_APPLICATION_JSON; buffer_sprintf(w->response.data, "{\n\t\"action\": \"%s\",\n\t\"status\": \"%s\",\n\t\"hostname\": \"%s\",\n\t\"machine_guid\": \"%s\"", - action, status, registry.hostname, registry.machine_guid); + action, status, registry.hostname, registry.machine_guid); } static inline void registry_json_footer(struct web_client *w) { buffer_strcat(w->response.data, "\n}\n"); } -int registry_request_hello_json(struct web_client *w) { - registry_json_header(w, "hello", REGISTRY_STATUS_OK); - - buffer_sprintf(w->response.data, ",\n\t\"registry\": \"%s\"", - registry.registry_to_announce); - - registry_json_footer(w); - return 200; -} - static inline int registry_json_disabled(struct web_client *w, const char *action) { registry_json_header(w, action, REGISTRY_STATUS_DISABLED); buffer_sprintf(w->response.data, ",\n\t\"registry\": \"%s\"", - registry.registry_to_announce); + registry.registry_to_announce); registry_json_footer(w); return 200; } + +// ---------------------------------------------------------------------------- +// CALLBACKS FOR WALKING THROUGH REGISTRY OBJECTS + // structure used be the callbacks below struct registry_json_walk_person_urls_callback { - PERSON *p; - MACHINE *m; + REGISTRY_PERSON *p; + REGISTRY_MACHINE *m; struct web_client *w; int count; }; // callback for rendering PERSON_URLs -static inline int registry_json_person_url_callback(void *entry, void *data) { - PERSON_URL *pu = (PERSON_URL *)entry; +static int registry_json_person_url_callback(void *entry, void *data) { + REGISTRY_PERSON_URL *pu = (REGISTRY_PERSON_URL *)entry; struct registry_json_walk_person_urls_callback *c = (struct registry_json_walk_person_urls_callback *)data; struct web_client *w = c->w; @@ -1072,37 +84,92 @@ static inline int registry_json_person_url_callback(void *entry, void *data) { buffer_strcat(w->response.data, ","); buffer_sprintf(w->response.data, "\n\t\t[ \"%s\", \"%s\", %u000, %u, \"%s\" ]", - pu->machine->guid, pu->url->url, pu->last_t, pu->usages, pu->name); + pu->machine->guid, pu->url->url, pu->last_t, pu->usages, pu->machine_name); - return 1; + return 0; } // callback for rendering MACHINE_URLs -static inline int registry_json_machine_url_callback(void *entry, void *data) { - MACHINE_URL *mu = (MACHINE_URL *)entry; +static int registry_json_machine_url_callback(void *entry, void *data) { + REGISTRY_MACHINE_URL *mu = (REGISTRY_MACHINE_URL *)entry; struct registry_json_walk_person_urls_callback *c = (struct registry_json_walk_person_urls_callback *)data; struct web_client *w = c->w; - MACHINE *m = c->m; + REGISTRY_MACHINE *m = c->m; if(unlikely(c->count++)) buffer_strcat(w->response.data, ","); buffer_sprintf(w->response.data, "\n\t\t[ \"%s\", \"%s\", %u000, %u ]", - m->guid, mu->url->url, mu->last_t, mu->usages); + m->guid, mu->url->url, mu->last_t, mu->usages); return 1; } +// ---------------------------------------------------------------------------- + +// structure used be the callbacks below +struct registry_person_url_callback_verify_machine_exists_data { + REGISTRY_MACHINE *m; + int count; +}; + +static inline int registry_person_url_callback_verify_machine_exists(void *entry, void *data) { + struct registry_person_url_callback_verify_machine_exists_data *d = (struct registry_person_url_callback_verify_machine_exists_data *)data; + REGISTRY_PERSON_URL *pu = (REGISTRY_PERSON_URL *)entry; + REGISTRY_MACHINE *m = d->m; + + if(pu->machine == m) + d->count++; + + return 0; +} + +// ---------------------------------------------------------------------------- +// public HELLO request + +int registry_request_hello_json(struct web_client *w) { + registry_json_header(w, "hello", REGISTRY_STATUS_OK); + + buffer_sprintf(w->response.data, ",\n\t\"registry\": \"%s\"", + registry.registry_to_announce); + + registry_json_footer(w); + return 200; +} + +// ---------------------------------------------------------------------------- +//public ACCESS request + +#define REGISTRY_VERIFY_COOKIES_GUID "give-me-back-this-cookie-now--please" // the main method for registering an access int registry_request_access_json(struct web_client *w, char *person_guid, char *machine_guid, char *url, char *name, time_t when) { - if(!registry.enabled) + if(unlikely(!registry.enabled)) return registry_json_disabled(w, "access"); - PERSON *p = registry_request_access(person_guid, machine_guid, url, name, when); + // ------------------------------------------------------------------------ + // verify the browser supports cookies + + if(registry.verify_cookies_redirects > 0 && !person_guid[0]) { + buffer_flush(w->response.data); + registry_set_cookie(w, REGISTRY_VERIFY_COOKIES_GUID); + w->response.data->contenttype = CT_APPLICATION_JSON; + buffer_sprintf(w->response.data, "{ \"status\": \"redirect\", \"registry\": \"%s\" }", registry.registry_to_announce); + return 200; + } + + if(unlikely(person_guid[0] && !strcmp(person_guid, REGISTRY_VERIFY_COOKIES_GUID))) + person_guid[0] = '\0'; + + // ------------------------------------------------------------------------ + + registry_lock(); + + REGISTRY_PERSON *p = registry_request_access(person_guid, machine_guid, url, name, when); if(!p) { registry_json_header(w, "access", REGISTRY_STATUS_FAILED); registry_json_footer(w); + registry_unlock(); return 412; } @@ -1114,40 +181,54 @@ int registry_request_access_json(struct web_client *w, char *person_guid, char * buffer_sprintf(w->response.data, ",\n\t\"person_guid\": \"%s\",\n\t\"urls\": [", p->guid); struct registry_json_walk_person_urls_callback c = { p, NULL, w, 0 }; - dictionary_get_all(p->urls, registry_json_person_url_callback, &c); + avl_traverse(&p->person_urls, registry_json_person_url_callback, &c); buffer_strcat(w->response.data, "\n\t]\n"); registry_json_footer(w); + registry_unlock(); return 200; } +// ---------------------------------------------------------------------------- +// public DELETE request + // the main method for deleting a URL from a person int registry_request_delete_json(struct web_client *w, char *person_guid, char *machine_guid, char *url, char *delete_url, time_t when) { if(!registry.enabled) return registry_json_disabled(w, "delete"); - PERSON *p = registry_request_delete(person_guid, machine_guid, url, delete_url, when); + registry_lock(); + + REGISTRY_PERSON *p = registry_request_delete(person_guid, machine_guid, url, delete_url, when); if(!p) { registry_json_header(w, "delete", REGISTRY_STATUS_FAILED); registry_json_footer(w); + registry_unlock(); return 412; } // generate the response registry_json_header(w, "delete", REGISTRY_STATUS_OK); registry_json_footer(w); + registry_unlock(); return 200; } +// ---------------------------------------------------------------------------- +// public SEARCH request + // the main method for searching the URLs of a netdata int registry_request_search_json(struct web_client *w, char *person_guid, char *machine_guid, char *url, char *request_machine, time_t when) { if(!registry.enabled) return registry_json_disabled(w, "search"); - MACHINE *m = registry_request_machine(person_guid, machine_guid, url, request_machine, when); + registry_lock(); + + REGISTRY_MACHINE *m = registry_request_machine(person_guid, machine_guid, url, request_machine, when); if(!m) { registry_json_header(w, "search", REGISTRY_STATUS_FAILED); registry_json_footer(w); + registry_unlock(); return 404; } @@ -1155,75 +236,69 @@ int registry_request_search_json(struct web_client *w, char *person_guid, char * buffer_strcat(w->response.data, ",\n\t\"urls\": ["); struct registry_json_walk_person_urls_callback c = { NULL, m, w, 0 }; - dictionary_get_all(m->urls, registry_json_machine_url_callback, &c); + dictionary_get_all(m->machine_urls, registry_json_machine_url_callback, &c); buffer_strcat(w->response.data, "\n\t]\n"); registry_json_footer(w); + registry_unlock(); return 200; } -// structure used be the callbacks below -struct registry_person_url_callback_verify_machine_exists_data { - MACHINE *m; - int count; -}; - -int registry_person_url_callback_verify_machine_exists(void *entry, void *data) { - struct registry_person_url_callback_verify_machine_exists_data *d = (struct registry_person_url_callback_verify_machine_exists_data *)data; - PERSON_URL *pu = (PERSON_URL *)entry; - MACHINE *m = d->m; - - if(pu->machine == m) - d->count++; - - return 0; -} +// ---------------------------------------------------------------------------- +// SWITCH REQUEST // the main method for switching user identity int registry_request_switch_json(struct web_client *w, char *person_guid, char *machine_guid, char *url, char *new_person_guid, time_t when) { + if(!registry.enabled) + return registry_json_disabled(w, "switch"); + (void)url; (void)when; - if(!registry.enabled) - return registry_json_disabled(w, "switch"); + registry_lock(); - PERSON *op = registry_person_find(person_guid); + REGISTRY_PERSON *op = registry_person_find(person_guid); if(!op) { registry_json_header(w, "switch", REGISTRY_STATUS_FAILED); registry_json_footer(w); + registry_unlock(); return 430; } - PERSON *np = registry_person_find(new_person_guid); + REGISTRY_PERSON *np = registry_person_find(new_person_guid); if(!np) { registry_json_header(w, "switch", REGISTRY_STATUS_FAILED); registry_json_footer(w); + registry_unlock(); return 431; } - MACHINE *m = registry_machine_find(machine_guid); + REGISTRY_MACHINE *m = registry_machine_find(machine_guid); if(!m) { registry_json_header(w, "switch", REGISTRY_STATUS_FAILED); registry_json_footer(w); + registry_unlock(); return 432; } struct registry_person_url_callback_verify_machine_exists_data data = { m, 0 }; // verify the old person has access to this machine - dictionary_get_all(op->urls, registry_person_url_callback_verify_machine_exists, &data); + avl_traverse(&op->person_urls, registry_person_url_callback_verify_machine_exists, &data); if(!data.count) { registry_json_header(w, "switch", REGISTRY_STATUS_FAILED); registry_json_footer(w); + registry_unlock(); return 433; } // verify the new person has access to this machine data.count = 0; - dictionary_get_all(np->urls, registry_person_url_callback_verify_machine_exists, &data); + avl_traverse(&np->person_urls, registry_person_url_callback_verify_machine_exists, &data); if(!data.count) { registry_json_header(w, "switch", REGISTRY_STATUS_FAILED); registry_json_footer(w); + registry_unlock(); return 434; } @@ -1235,582 +310,9 @@ int registry_request_switch_json(struct web_client *w, char *person_guid, char * registry_json_header(w, "switch", REGISTRY_STATUS_OK); buffer_sprintf(w->response.data, ",\n\t\"person_guid\": \"%s\"", np->guid); registry_json_footer(w); - return 200; -} - - -// ---------------------------------------------------------------------------- -// REGISTRY THIS MACHINE UNIQUE ID - -static inline int is_machine_guid_blacklisted(const char *guid) { - // these are machine GUIDs that have been included in distribution packages. - // we blacklist them here, so that the next version of netdata will generate - // new ones. - - if(!strcmp(guid, "8a795b0c-2311-11e6-8563-000c295076a6") - || !strcmp(guid, "4aed1458-1c3e-11e6-a53f-000c290fc8f5") - ) { - error("Blacklisted machine GUID '%s' found.", guid); - return 1; - } - - return 0; -} - -char *registry_get_this_machine_guid(void) { - if(likely(registry.machine_guid[0])) - return registry.machine_guid; - - // read it from disk - int fd = open(registry.machine_guid_filename, O_RDONLY); - if(fd != -1) { - char buf[36 + 1]; - if(read(fd, buf, 36) != 36) - error("Failed to read machine GUID from '%s'", registry.machine_guid_filename); - else { - buf[36] = '\0'; - if(registry_regenerate_guid(buf, registry.machine_guid) == -1) { - error("Failed to validate machine GUID '%s' from '%s'. Ignoring it - this might mean this netdata will appear as duplicate in the registry.", - buf, registry.machine_guid_filename); - - registry.machine_guid[0] = '\0'; - } - else if(is_machine_guid_blacklisted(registry.machine_guid)) - registry.machine_guid[0] = '\0'; - } - close(fd); - } - - // generate a new one? - if(!registry.machine_guid[0]) { - uuid_t uuid; - - uuid_generate_time(uuid); - uuid_unparse_lower(uuid, registry.machine_guid); - registry.machine_guid[36] = '\0'; - - // save it - fd = open(registry.machine_guid_filename, O_WRONLY|O_CREAT|O_TRUNC, 444); - if(fd == -1) - fatal("Cannot create unique machine id file '%s'. Please fix this.", registry.machine_guid_filename); - - if(write(fd, registry.machine_guid, 36) != 36) - fatal("Cannot write the unique machine id file '%s'. Please fix this.", registry.machine_guid_filename); - - close(fd); - } - - setenv("NETDATA_REGISTRY_UNIQUE_ID", registry.machine_guid, 1); - - return registry.machine_guid; -} - - -// ---------------------------------------------------------------------------- -// REGISTRY LOAD/SAVE - -int registry_machine_save_url(void *entry, void *file) { - MACHINE_URL *mu = entry; - FILE *fp = file; - - debug(D_REGISTRY, "Registry: registry_machine_save_url('%s')", mu->url->url); - - int ret = fprintf(fp, "V\t%08x\t%08x\t%08x\t%02x\t%s\n", - mu->first_t, - mu->last_t, - mu->usages, - mu->flags, - mu->url->url - ); - - // error handling is done at registry_save() - - return ret; -} - -int registry_machine_save(void *entry, void *file) { - MACHINE *m = entry; - FILE *fp = file; - - debug(D_REGISTRY, "Registry: registry_machine_save('%s')", m->guid); - - int ret = fprintf(fp, "M\t%08x\t%08x\t%08x\t%s\n", - m->first_t, - m->last_t, - m->usages, - m->guid - ); - - if(ret >= 0) { - int ret2 = dictionary_get_all(m->urls, registry_machine_save_url, fp); - if(ret2 < 0) return ret2; - ret += ret2; - } - - // error handling is done at registry_save() - - return ret; -} - -static inline int registry_person_save_url(void *entry, void *file) { - PERSON_URL *pu = entry; - FILE *fp = file; - - debug(D_REGISTRY, "Registry: registry_person_save_url('%s')", pu->url->url); - - int ret = fprintf(fp, "U\t%08x\t%08x\t%08x\t%02x\t%s\t%s\t%s\n", - pu->first_t, - pu->last_t, - pu->usages, - pu->flags, - pu->machine->guid, - pu->name, - pu->url->url - ); - - // error handling is done at registry_save() - - return ret; -} - -static inline int registry_person_save(void *entry, void *file) { - PERSON *p = entry; - FILE *fp = file; - - debug(D_REGISTRY, "Registry: registry_person_save('%s')", p->guid); - - int ret = fprintf(fp, "P\t%08x\t%08x\t%08x\t%s\n", - p->first_t, - p->last_t, - p->usages, - p->guid - ); - - if(ret >= 0) { - int ret2 = dictionary_get_all(p->urls, registry_person_save_url, fp); - if (ret2 < 0) return ret2; - ret += ret2; - } - - // error handling is done at registry_save() - - return ret; -} - -int registry_save(void) { - if(!registry.enabled) return -1; - - // make sure the log is not updated - registry_log_lock(); - - if(unlikely(!registry_should_save_db())) { - registry_log_unlock(); - return -2; - } - - error_log_limit_unlimited(); - - char tmp_filename[FILENAME_MAX + 1]; - char old_filename[FILENAME_MAX + 1]; - - snprintfz(old_filename, FILENAME_MAX, "%s.old", registry.db_filename); - snprintfz(tmp_filename, FILENAME_MAX, "%s.tmp", registry.db_filename); - - debug(D_REGISTRY, "Registry: Creating file '%s'", tmp_filename); - FILE *fp = fopen(tmp_filename, "w"); - if(!fp) { - error("Registry: Cannot create file: %s", tmp_filename); - registry_log_unlock(); - error_log_limit_reset(); - return -1; - } - - // dictionary_get_all() has its own locking, so this is safe to do - debug(D_REGISTRY, "Saving all machines"); - int bytes1 = dictionary_get_all(registry.machines, registry_machine_save, fp); - if(bytes1 < 0) { - error("Registry: Cannot save registry machines - return value %d", bytes1); - fclose(fp); - registry_log_unlock(); - error_log_limit_reset(); - return bytes1; - } - debug(D_REGISTRY, "Registry: saving machines took %d bytes", bytes1); - - debug(D_REGISTRY, "Saving all persons"); - int bytes2 = dictionary_get_all(registry.persons, registry_person_save, fp); - if(bytes2 < 0) { - error("Registry: Cannot save registry persons - return value %d", bytes2); - fclose(fp); - registry_log_unlock(); - error_log_limit_reset(); - return bytes2; - } - debug(D_REGISTRY, "Registry: saving persons took %d bytes", bytes2); - - // save the totals - fprintf(fp, "T\t%016llx\t%016llx\t%016llx\t%016llx\t%016llx\t%016llx\n", - registry.persons_count, - registry.machines_count, - registry.usages_count + 1, // this is required - it is lost on db rotation - registry.urls_count, - registry.persons_urls_count, - registry.machines_urls_count - ); - - fclose(fp); - - errno = 0; - - // remove the .old db - debug(D_REGISTRY, "Registry: Removing old db '%s'", old_filename); - if(unlink(old_filename) == -1 && errno != ENOENT) - error("Registry: cannot remove old registry file '%s'", old_filename); - - // rename the db to .old - debug(D_REGISTRY, "Registry: Link current db '%s' to .old: '%s'", registry.db_filename, old_filename); - if(link(registry.db_filename, old_filename) == -1 && errno != ENOENT) - error("Registry: cannot move file '%s' to '%s'. Saving registry DB failed!", tmp_filename, registry.db_filename); - - else { - // remove the database (it is saved in .old) - debug(D_REGISTRY, "Registry: removing db '%s'", registry.db_filename); - if (unlink(registry.db_filename) == -1 && errno != ENOENT) - error("Registry: cannot remove old registry file '%s'", registry.db_filename); - - // move the .tmp to make it active - debug(D_REGISTRY, "Registry: linking tmp db '%s' to active db '%s'", tmp_filename, registry.db_filename); - if (link(tmp_filename, registry.db_filename) == -1) { - error("Registry: cannot move file '%s' to '%s'. Saving registry DB failed!", tmp_filename, - registry.db_filename); - - // move the .old back - debug(D_REGISTRY, "Registry: linking old db '%s' to active db '%s'", old_filename, registry.db_filename); - if(link(old_filename, registry.db_filename) == -1) - error("Registry: cannot move file '%s' to '%s'. Recovering the old registry DB failed!", old_filename, registry.db_filename); - } - else { - debug(D_REGISTRY, "Registry: removing tmp db '%s'", tmp_filename); - if(unlink(tmp_filename) == -1) - error("Registry: cannot remove tmp registry file '%s'", tmp_filename); - - // it has been moved successfully - // discard the current registry log - registry_log_recreate_nolock(); - registry.log_count = 0; - } - } - - // continue operations - registry_log_unlock(); - error_log_limit_reset(); - - return -1; -} - -static inline size_t registry_load(void) { - char *s, buf[4096 + 1]; - PERSON *p = NULL; - MACHINE *m = NULL; - URL *u = NULL; - size_t line = 0; - - debug(D_REGISTRY, "Registry: loading active db from: '%s'", registry.db_filename); - FILE *fp = fopen(registry.db_filename, "r"); - if(!fp) { - error("Registry: cannot open registry file: '%s'", registry.db_filename); - return 0; - } - - size_t len = 0; - buf[4096] = '\0'; - while((s = fgets_trim_len(buf, 4096, fp, &len))) { - line++; - - debug(D_REGISTRY, "Registry: read line %zu to length %zu: %s", line, len, s); - switch(*s) { - case 'T': // totals - if(unlikely(len != 103 || s[1] != '\t' || s[18] != '\t' || s[35] != '\t' || s[52] != '\t' || s[69] != '\t' || s[86] != '\t' || s[103] != '\0')) { - error("Registry totals line %zu is wrong (len = %zu).", line, len); - continue; - } - registry.persons_count = strtoull(&s[2], NULL, 16); - registry.machines_count = strtoull(&s[19], NULL, 16); - registry.usages_count = strtoull(&s[36], NULL, 16); - registry.urls_count = strtoull(&s[53], NULL, 16); - registry.persons_urls_count = strtoull(&s[70], NULL, 16); - registry.machines_urls_count = strtoull(&s[87], NULL, 16); - break; - - case 'P': // person - m = NULL; - // verify it is valid - if(unlikely(len != 65 || s[1] != '\t' || s[10] != '\t' || s[19] != '\t' || s[28] != '\t' || s[65] != '\0')) { - error("Registry person line %zu is wrong (len = %zu).", line, len); - continue; - } - - s[1] = s[10] = s[19] = s[28] = '\0'; - p = registry_person_allocate(&s[29], strtoul(&s[2], NULL, 16)); - p->last_t = strtoul(&s[11], NULL, 16); - p->usages = strtoul(&s[20], NULL, 16); - debug(D_REGISTRY, "Registry loaded person '%s', first: %u, last: %u, usages: %u", p->guid, p->first_t, p->last_t, p->usages); - break; - - case 'M': // machine - p = NULL; - // verify it is valid - if(unlikely(len != 65 || s[1] != '\t' || s[10] != '\t' || s[19] != '\t' || s[28] != '\t' || s[65] != '\0')) { - error("Registry person line %zu is wrong (len = %zu).", line, len); - continue; - } - - s[1] = s[10] = s[19] = s[28] = '\0'; - m = registry_machine_allocate(&s[29], strtoul(&s[2], NULL, 16)); - m->last_t = strtoul(&s[11], NULL, 16); - m->usages = strtoul(&s[20], NULL, 16); - debug(D_REGISTRY, "Registry loaded machine '%s', first: %u, last: %u, usages: %u", m->guid, m->first_t, m->last_t, m->usages); - break; - - case 'U': // person URL - if(unlikely(!p)) { - error("Registry: ignoring line %zu, no person loaded: %s", line, s); - continue; - } - - // verify it is valid - if(len < 69 || s[1] != '\t' || s[10] != '\t' || s[19] != '\t' || s[28] != '\t' || s[31] != '\t' || s[68] != '\t') { - error("Registry person URL line %zu is wrong (len = %zu).", line, len); - continue; - } - - s[1] = s[10] = s[19] = s[28] = s[31] = s[68] = '\0'; - - // skip the name to find the url - char *url = &s[69]; - while(*url && *url != '\t') url++; - if(!*url) { - error("Registry person URL line %zu does not have a url.", line); - continue; - } - *url++ = '\0'; - - // u = registry_url_allocate_nolock(url, strlen(url)); - u = registry_url_get_nolock(url, strlen(url)); - - time_t first_t = strtoul(&s[2], NULL, 16); - - m = registry_machine_find(&s[32]); - if(!m) m = registry_machine_allocate(&s[32], first_t); - - PERSON_URL *pu = registry_person_url_allocate(p, m, u, &s[69], strlen(&s[69]), first_t); - pu->last_t = strtoul(&s[11], NULL, 16); - pu->usages = strtoul(&s[20], NULL, 16); - pu->flags = strtoul(&s[29], NULL, 16); - debug(D_REGISTRY, "Registry loaded person URL '%s' with name '%s' of machine '%s', first: %u, last: %u, usages: %u, flags: %02x", u->url, pu->name, m->guid, pu->first_t, pu->last_t, pu->usages, pu->flags); - break; - - case 'V': // machine URL - if(unlikely(!m)) { - error("Registry: ignoring line %zu, no machine loaded: %s", line, s); - continue; - } - - // verify it is valid - if(len < 32 || s[1] != '\t' || s[10] != '\t' || s[19] != '\t' || s[28] != '\t' || s[31] != '\t') { - error("Registry person URL line %zu is wrong (len = %zu).", line, len); - continue; - } - - s[1] = s[10] = s[19] = s[28] = s[31] = '\0'; - // u = registry_url_allocate_nolock(&s[32], strlen(&s[32])); - u = registry_url_get_nolock(&s[32], strlen(&s[32])); - - MACHINE_URL *mu = registry_machine_url_allocate(m, u, strtoul(&s[2], NULL, 16)); - mu->last_t = strtoul(&s[11], NULL, 16); - mu->usages = strtoul(&s[20], NULL, 16); - mu->flags = strtoul(&s[29], NULL, 16); - debug(D_REGISTRY, "Registry loaded machine URL '%s', machine '%s', first: %u, last: %u, usages: %u, flags: %02x", u->url, m->guid, mu->first_t, mu->last_t, mu->usages, mu->flags); - break; - - default: - error("Registry: ignoring line %zu of filename '%s': %s.", line, registry.db_filename, s); - break; - } - } - fclose(fp); - - return line; -} - -// ---------------------------------------------------------------------------- -// REGISTRY - -int registry_init(void) { - char filename[FILENAME_MAX + 1]; - - // registry enabled? - registry.enabled = config_get_boolean("registry", "enabled", 0); - - // pathnames - registry.pathname = config_get("registry", "registry db directory", VARLIB_DIR "/registry"); - if(mkdir(registry.pathname, 0770) == -1 && errno != EEXIST) - fatal("Cannot create directory '%s'.", registry.pathname); - - // filenames - snprintfz(filename, FILENAME_MAX, "%s/netdata.public.unique.id", registry.pathname); - registry.machine_guid_filename = config_get("registry", "netdata unique id file", filename); - registry_get_this_machine_guid(); - - snprintfz(filename, FILENAME_MAX, "%s/registry.db", registry.pathname); - registry.db_filename = config_get("registry", "registry db file", filename); - - snprintfz(filename, FILENAME_MAX, "%s/registry-log.db", registry.pathname); - registry.log_filename = config_get("registry", "registry log file", filename); - - // configuration options - registry.save_registry_every_entries = config_get_number("registry", "registry save db every new entries", 1000000); - registry.persons_expiration = config_get_number("registry", "registry expire idle persons days", 365) * 86400; - registry.registry_domain = config_get("registry", "registry domain", ""); - registry.registry_to_announce = config_get("registry", "registry to announce", "https://registry.my-netdata.io"); - registry.hostname = config_get("registry", "registry hostname", config_get("global", "hostname", localhost.hostname)); - registry.verify_cookies_redirects = config_get_boolean("registry", "verify browser cookies support", 1); - - setenv("NETDATA_REGISTRY_HOSTNAME", registry.hostname, 1); - setenv("NETDATA_REGISTRY_URL", registry.registry_to_announce, 1); - - registry.max_url_length = config_get_number("registry", "max URL length", 1024); - if(registry.max_url_length < 10) { - registry.max_url_length = 10; - config_set_number("registry", "max URL length", registry.max_url_length); - } - - registry.max_name_length = config_get_number("registry", "max URL name length", 50); - if(registry.max_name_length < 10) { - registry.max_name_length = 10; - config_set_number("registry", "max URL name length", registry.max_name_length); - } - - // initialize entries counters - registry.persons_count = 0; - registry.machines_count = 0; - registry.usages_count = 0; - registry.urls_count = 0; - registry.persons_urls_count = 0; - registry.machines_urls_count = 0; - - // initialize memory counters - registry.persons_memory = 0; - registry.machines_memory = 0; - registry.urls_memory = 0; - registry.persons_urls_memory = 0; - registry.machines_urls_memory = 0; - - // initialize locks - pthread_mutex_init(®istry.persons_lock, NULL); - pthread_mutex_init(®istry.machines_lock, NULL); - pthread_mutex_init(®istry.urls_lock, NULL); - pthread_mutex_init(®istry.person_urls_lock, NULL); - pthread_mutex_init(®istry.machine_urls_lock, NULL); - - // create dictionaries - registry.persons = dictionary_create(DICTIONARY_FLAGS); - registry.machines = dictionary_create(DICTIONARY_FLAGS); - registry.urls = dictionary_create(DICTIONARY_FLAGS); - - // load the registry database - if(registry.enabled) { - registry_log_open_nolock(); - registry_load(); - registry_log_load(); - - if(unlikely(registry_should_save_db())) - registry_save(); - } - - return 0; -} - -void registry_free(void) { - if(!registry.enabled) return; - - // we need to destroy the dictionaries ourselves - // since the dictionaries use memory we allocated - - while(registry.persons->values_index.root) { - PERSON *p = ((NAME_VALUE *)registry.persons->values_index.root)->value; - - // fprintf(stderr, "\nPERSON: '%s', first: %u, last: %u, usages: %u\n", p->guid, p->first_t, p->last_t, p->usages); - - while(p->urls->values_index.root) { - PERSON_URL *pu = ((NAME_VALUE *)p->urls->values_index.root)->value; - - // fprintf(stderr, "\tURL: '%s', first: %u, last: %u, usages: %u, flags: 0x%02x\n", pu->url->url, pu->first_t, pu->last_t, pu->usages, pu->flags); - - debug(D_REGISTRY, "Registry: deleting url '%s' from person '%s'", pu->url->url, p->guid); - dictionary_del(p->urls, pu->url->url); - - debug(D_REGISTRY, "Registry: unlinking url '%s' from person", pu->url->url); - registry_url_unlink_nolock(pu->url); - - debug(D_REGISTRY, "Registry: freeing person url"); - freez(pu); - } - - debug(D_REGISTRY, "Registry: deleting person '%s' from persons registry", p->guid); - dictionary_del(registry.persons, p->guid); - - debug(D_REGISTRY, "Registry: destroying URL dictionary of person '%s'", p->guid); - dictionary_destroy(p->urls); - - debug(D_REGISTRY, "Registry: freeing person '%s'", p->guid); - freez(p); - } - - while(registry.machines->values_index.root) { - MACHINE *m = ((NAME_VALUE *)registry.machines->values_index.root)->value; - - // fprintf(stderr, "\nMACHINE: '%s', first: %u, last: %u, usages: %u\n", m->guid, m->first_t, m->last_t, m->usages); - - while(m->urls->values_index.root) { - MACHINE_URL *mu = ((NAME_VALUE *)m->urls->values_index.root)->value; - - // fprintf(stderr, "\tURL: '%s', first: %u, last: %u, usages: %u, flags: 0x%02x\n", mu->url->url, mu->first_t, mu->last_t, mu->usages, mu->flags); - - //debug(D_REGISTRY, "Registry: destroying persons dictionary from url '%s'", mu->url->url); - //dictionary_destroy(mu->persons); - - debug(D_REGISTRY, "Registry: deleting url '%s' from person '%s'", mu->url->url, m->guid); - dictionary_del(m->urls, mu->url->url); - - debug(D_REGISTRY, "Registry: unlinking url '%s' from machine", mu->url->url); - registry_url_unlink_nolock(mu->url); - - debug(D_REGISTRY, "Registry: freeing machine url"); - freez(mu); - } - - debug(D_REGISTRY, "Registry: deleting machine '%s' from machines registry", m->guid); - dictionary_del(registry.machines, m->guid); - - debug(D_REGISTRY, "Registry: destroying URL dictionary of machine '%s'", m->guid); - dictionary_destroy(m->urls); - - debug(D_REGISTRY, "Registry: freeing machine '%s'", m->guid); - freez(m); - } - - // and free the memory of remaining dictionary structures - - debug(D_REGISTRY, "Registry: destroying persons dictionary"); - dictionary_destroy(registry.persons); - - debug(D_REGISTRY, "Registry: destroying machines dictionary"); - dictionary_destroy(registry.machines); - - debug(D_REGISTRY, "Registry: destroying urls dictionary"); - dictionary_destroy(registry.urls); + registry_unlock(); + return 200; } // ---------------------------------------------------------------------------- @@ -1869,8 +371,8 @@ void registry_statistics(void) { rrddim_set(stm, "persons", registry.persons_memory + registry.persons_count * sizeof(NAME_VALUE) + sizeof(DICTIONARY)); rrddim_set(stm, "machines", registry.machines_memory + registry.machines_count * sizeof(NAME_VALUE) + sizeof(DICTIONARY)); - rrddim_set(stm, "urls", registry.urls_memory + registry.urls_count * sizeof(NAME_VALUE) + sizeof(DICTIONARY)); - rrddim_set(stm, "persons_urls", registry.persons_urls_memory + registry.persons_count * sizeof(DICTIONARY) + registry.persons_urls_count * sizeof(NAME_VALUE)); + rrddim_set(stm, "urls", registry.urls_memory); + rrddim_set(stm, "persons_urls", registry.persons_urls_memory); rrddim_set(stm, "machines_urls", registry.machines_urls_memory + registry.machines_count * sizeof(DICTIONARY) + registry.machines_urls_count * sizeof(NAME_VALUE)); rrdset_done(stm); } diff --git a/src/registry.h b/src/registry.h index c2b57a23d..4947486c4 100644 --- a/src/registry.h +++ b/src/registry.h @@ -1,25 +1,72 @@ +/* + * netdata registry + * + * this header file describes the public interface + * to the netdata registry + * + * only these high level functions are exposed + * + */ + +// ---------------------------------------------------------------------------- +// TODO +// +// 1. the default tracking cookie expires in 1 year, but the persons are not +// removed from the db - this means the database only grows - ideally the +// database should be cleaned in registry_db_save() for both on-disk and +// on-memory entries. +// +// Cleanup: +// i. Find all the PERSONs that have expired cookie +// ii. For each of their PERSON_URLs: +// - decrement the linked MACHINE links +// - if the linked MACHINE has no other links, remove the linked MACHINE too +// - remove the PERSON_URL +// +// 2. add protection to prevent abusing the registry by flooding it with +// requests to fill the memory and crash it. +// +// Possible protections: +// - limit the number of URLs per person +// - limit the number of URLs per machine +// - limit the number of persons +// - limit the number of machines +// - [DONE] limit the size of URLs +// - [DONE] limit the size of PERSON_URL names +// - limit the number of requests that add data to the registry, +// per client IP per hour +// +// 3. lower memory requirements +// +// - embed avl structures directly into registry objects, instead of DICTIONARY +// [DONE for PERSON_URLs, PENDING for MACHINE_URLs] +// - store GUIDs in memory as UUID instead of char * +// - do not track persons using the demo machines only +// (i.e. start tracking them only when they access a non-demo machine) +// - [DONE] do not track custom dashboards by default + + #ifndef NETDATA_REGISTRY_H #define NETDATA_REGISTRY_H 1 #define NETDATA_REGISTRY_COOKIE_NAME "netdata_registry_id" -extern void registry_set_cookie(struct web_client *w, const char *guid); -extern const char *registry_to_announce(void); -extern int registry_verify_cookies_redirects(void); +// initialize the registry +// should only happen when netdata starts +extern int registry_init(void); + +// free all data held by the registry +// should only happen when netdata exits +extern void registry_free(void); +// HTTP requests handled by the registry extern int registry_request_access_json(struct web_client *w, char *person_guid, char *machine_guid, char *url, char *name, time_t when); extern int registry_request_delete_json(struct web_client *w, char *person_guid, char *machine_guid, char *url, char *delete_url, time_t when); extern int registry_request_search_json(struct web_client *w, char *person_guid, char *machine_guid, char *url, char *request_machine, time_t when); extern int registry_request_switch_json(struct web_client *w, char *person_guid, char *machine_guid, char *url, char *new_person_guid, time_t when); extern int registry_request_hello_json(struct web_client *w); -extern int registry_init(void); -extern void registry_free(void); -extern int registry_save(void); - -extern char *registry_get_this_machine_guid(void); - +// update the registry monitoring charts extern void registry_statistics(void); - #endif /* NETDATA_REGISTRY_H */ diff --git a/src/registry_db.c b/src/registry_db.c new file mode 100644 index 000000000..de6c634c3 --- /dev/null +++ b/src/registry_db.c @@ -0,0 +1,343 @@ +#include "registry_internals.h" + +int registry_db_should_be_saved(void) { + debug(D_REGISTRY, "log entries %llu, max %llu", registry.log_count, registry.save_registry_every_entries); + return registry.log_count > registry.save_registry_every_entries; +} + +// ---------------------------------------------------------------------------- +// INTERNAL FUNCTIONS FOR SAVING REGISTRY OBJECTS + +static int registry_machine_save_url(void *entry, void *file) { + REGISTRY_MACHINE_URL *mu = entry; + FILE *fp = file; + + debug(D_REGISTRY, "Registry: registry_machine_save_url('%s')", mu->url->url); + + int ret = fprintf(fp, "V\t%08x\t%08x\t%08x\t%02x\t%s\n", + mu->first_t, + mu->last_t, + mu->usages, + mu->flags, + mu->url->url + ); + + // error handling is done at registry_db_save() + + return ret; +} + +static int registry_machine_save(void *entry, void *file) { + REGISTRY_MACHINE *m = entry; + FILE *fp = file; + + debug(D_REGISTRY, "Registry: registry_machine_save('%s')", m->guid); + + int ret = fprintf(fp, "M\t%08x\t%08x\t%08x\t%s\n", + m->first_t, + m->last_t, + m->usages, + m->guid + ); + + if(ret >= 0) { + int ret2 = dictionary_get_all(m->machine_urls, registry_machine_save_url, fp); + if(ret2 < 0) return ret2; + ret += ret2; + } + + // error handling is done at registry_db_save() + + return ret; +} + +static inline int registry_person_save_url(void *entry, void *file) { + REGISTRY_PERSON_URL *pu = entry; + FILE *fp = file; + + debug(D_REGISTRY, "Registry: registry_person_save_url('%s')", pu->url->url); + + int ret = fprintf(fp, "U\t%08x\t%08x\t%08x\t%02x\t%s\t%s\t%s\n", + pu->first_t, + pu->last_t, + pu->usages, + pu->flags, + pu->machine->guid, + pu->machine_name, + pu->url->url + ); + + // error handling is done at registry_db_save() + + return ret; +} + +static inline int registry_person_save(void *entry, void *file) { + REGISTRY_PERSON *p = entry; + FILE *fp = file; + + debug(D_REGISTRY, "Registry: registry_person_save('%s')", p->guid); + + int ret = fprintf(fp, "P\t%08x\t%08x\t%08x\t%s\n", + p->first_t, + p->last_t, + p->usages, + p->guid + ); + + if(ret >= 0) { + //int ret2 = dictionary_get_all(p->person_urls, registry_person_save_url, fp); + int ret2 = avl_traverse(&p->person_urls, registry_person_save_url, fp); + if (ret2 < 0) return ret2; + ret += ret2; + } + + // error handling is done at registry_db_save() + + return ret; +} + +// ---------------------------------------------------------------------------- +// SAVE THE REGISTRY DATABASE + +int registry_db_save(void) { + if(unlikely(!registry.enabled)) + return -1; + + if(unlikely(!registry_db_should_be_saved())) + return -2; + + error_log_limit_unlimited(); + + char tmp_filename[FILENAME_MAX + 1]; + char old_filename[FILENAME_MAX + 1]; + + snprintfz(old_filename, FILENAME_MAX, "%s.old", registry.db_filename); + snprintfz(tmp_filename, FILENAME_MAX, "%s.tmp", registry.db_filename); + + debug(D_REGISTRY, "Registry: Creating file '%s'", tmp_filename); + FILE *fp = fopen(tmp_filename, "w"); + if(!fp) { + error("Registry: Cannot create file: %s", tmp_filename); + error_log_limit_reset(); + return -1; + } + + // dictionary_get_all() has its own locking, so this is safe to do + + debug(D_REGISTRY, "Saving all machines"); + int bytes1 = dictionary_get_all(registry.machines, registry_machine_save, fp); + if(bytes1 < 0) { + error("Registry: Cannot save registry machines - return value %d", bytes1); + fclose(fp); + error_log_limit_reset(); + return bytes1; + } + debug(D_REGISTRY, "Registry: saving machines took %d bytes", bytes1); + + debug(D_REGISTRY, "Saving all persons"); + int bytes2 = dictionary_get_all(registry.persons, registry_person_save, fp); + if(bytes2 < 0) { + error("Registry: Cannot save registry persons - return value %d", bytes2); + fclose(fp); + error_log_limit_reset(); + return bytes2; + } + debug(D_REGISTRY, "Registry: saving persons took %d bytes", bytes2); + + // save the totals + fprintf(fp, "T\t%016llx\t%016llx\t%016llx\t%016llx\t%016llx\t%016llx\n", + registry.persons_count, + registry.machines_count, + registry.usages_count + 1, // this is required - it is lost on db rotation + registry.urls_count, + registry.persons_urls_count, + registry.machines_urls_count + ); + + fclose(fp); + + errno = 0; + + // remove the .old db + debug(D_REGISTRY, "Registry: Removing old db '%s'", old_filename); + if(unlink(old_filename) == -1 && errno != ENOENT) + error("Registry: cannot remove old registry file '%s'", old_filename); + + // rename the db to .old + debug(D_REGISTRY, "Registry: Link current db '%s' to .old: '%s'", registry.db_filename, old_filename); + if(link(registry.db_filename, old_filename) == -1 && errno != ENOENT) + error("Registry: cannot move file '%s' to '%s'. Saving registry DB failed!", registry.db_filename, old_filename); + + else { + // remove the database (it is saved in .old) + debug(D_REGISTRY, "Registry: removing db '%s'", registry.db_filename); + if (unlink(registry.db_filename) == -1 && errno != ENOENT) + error("Registry: cannot remove old registry file '%s'", registry.db_filename); + + // move the .tmp to make it active + debug(D_REGISTRY, "Registry: linking tmp db '%s' to active db '%s'", tmp_filename, registry.db_filename); + if (link(tmp_filename, registry.db_filename) == -1) { + error("Registry: cannot move file '%s' to '%s'. Saving registry DB failed!", tmp_filename, + registry.db_filename); + + // move the .old back + debug(D_REGISTRY, "Registry: linking old db '%s' to active db '%s'", old_filename, registry.db_filename); + if(link(old_filename, registry.db_filename) == -1) + error("Registry: cannot move file '%s' to '%s'. Recovering the old registry DB failed!", old_filename, registry.db_filename); + } + else { + debug(D_REGISTRY, "Registry: removing tmp db '%s'", tmp_filename); + if(unlink(tmp_filename) == -1) + error("Registry: cannot remove tmp registry file '%s'", tmp_filename); + + // it has been moved successfully + // discard the current registry log + registry_log_recreate(); + registry.log_count = 0; + } + } + + // continue operations + error_log_limit_reset(); + + return -1; +} + +// ---------------------------------------------------------------------------- +// LOAD THE REGISTRY DATABASE + +size_t registry_db_load(void) { + char *s, buf[4096 + 1]; + REGISTRY_PERSON *p = NULL; + REGISTRY_MACHINE *m = NULL; + REGISTRY_URL *u = NULL; + size_t line = 0; + + debug(D_REGISTRY, "Registry: loading active db from: '%s'", registry.db_filename); + FILE *fp = fopen(registry.db_filename, "r"); + if(!fp) { + error("Registry: cannot open registry file: '%s'", registry.db_filename); + return 0; + } + + size_t len = 0; + buf[4096] = '\0'; + while((s = fgets_trim_len(buf, 4096, fp, &len))) { + line++; + + debug(D_REGISTRY, "Registry: read line %zu to length %zu: %s", line, len, s); + switch(*s) { + case 'T': // totals + if(unlikely(len != 103 || s[1] != '\t' || s[18] != '\t' || s[35] != '\t' || s[52] != '\t' || s[69] != '\t' || s[86] != '\t' || s[103] != '\0')) { + error("Registry totals line %zu is wrong (len = %zu).", line, len); + continue; + } + registry.persons_count = strtoull(&s[2], NULL, 16); + registry.machines_count = strtoull(&s[19], NULL, 16); + registry.usages_count = strtoull(&s[36], NULL, 16); + registry.urls_count = strtoull(&s[53], NULL, 16); + registry.persons_urls_count = strtoull(&s[70], NULL, 16); + registry.machines_urls_count = strtoull(&s[87], NULL, 16); + break; + + case 'P': // person + m = NULL; + // verify it is valid + if(unlikely(len != 65 || s[1] != '\t' || s[10] != '\t' || s[19] != '\t' || s[28] != '\t' || s[65] != '\0')) { + error("Registry person line %zu is wrong (len = %zu).", line, len); + continue; + } + + s[1] = s[10] = s[19] = s[28] = '\0'; + p = registry_person_allocate(&s[29], strtoul(&s[2], NULL, 16)); + p->last_t = (uint32_t)strtoul(&s[11], NULL, 16); + p->usages = (uint32_t)strtoul(&s[20], NULL, 16); + debug(D_REGISTRY, "Registry loaded person '%s', first: %u, last: %u, usages: %u", p->guid, p->first_t, p->last_t, p->usages); + break; + + case 'M': // machine + p = NULL; + // verify it is valid + if(unlikely(len != 65 || s[1] != '\t' || s[10] != '\t' || s[19] != '\t' || s[28] != '\t' || s[65] != '\0')) { + error("Registry person line %zu is wrong (len = %zu).", line, len); + continue; + } + + s[1] = s[10] = s[19] = s[28] = '\0'; + m = registry_machine_allocate(&s[29], strtoul(&s[2], NULL, 16)); + m->last_t = (uint32_t)strtoul(&s[11], NULL, 16); + m->usages = (uint32_t)strtoul(&s[20], NULL, 16); + debug(D_REGISTRY, "Registry loaded machine '%s', first: %u, last: %u, usages: %u", m->guid, m->first_t, m->last_t, m->usages); + break; + + case 'U': // person URL + if(unlikely(!p)) { + error("Registry: ignoring line %zu, no person loaded: %s", line, s); + continue; + } + + // verify it is valid + if(len < 69 || s[1] != '\t' || s[10] != '\t' || s[19] != '\t' || s[28] != '\t' || s[31] != '\t' || s[68] != '\t') { + error("Registry person URL line %zu is wrong (len = %zu).", line, len); + continue; + } + + s[1] = s[10] = s[19] = s[28] = s[31] = s[68] = '\0'; + + // skip the name to find the url + char *url = &s[69]; + while(*url && *url != '\t') url++; + if(!*url) { + error("Registry person URL line %zu does not have a url.", line); + continue; + } + *url++ = '\0'; + + // u = registry_url_allocate_nolock(url, strlen(url)); + u = registry_url_get(url, strlen(url)); + + time_t first_t = strtoul(&s[2], NULL, 16); + + m = registry_machine_find(&s[32]); + if(!m) m = registry_machine_allocate(&s[32], first_t); + + REGISTRY_PERSON_URL *pu = registry_person_url_allocate(p, m, u, &s[69], strlen(&s[69]), first_t); + pu->last_t = (uint32_t)strtoul(&s[11], NULL, 16); + pu->usages = (uint32_t)strtoul(&s[20], NULL, 16); + pu->flags = (uint8_t)strtoul(&s[29], NULL, 16); + debug(D_REGISTRY, "Registry loaded person URL '%s' with name '%s' of machine '%s', first: %u, last: %u, usages: %u, flags: %02x", u->url, pu->machine_name, m->guid, pu->first_t, pu->last_t, pu->usages, pu->flags); + break; + + case 'V': // machine URL + if(unlikely(!m)) { + error("Registry: ignoring line %zu, no machine loaded: %s", line, s); + continue; + } + + // verify it is valid + if(len < 32 || s[1] != '\t' || s[10] != '\t' || s[19] != '\t' || s[28] != '\t' || s[31] != '\t') { + error("Registry person URL line %zu is wrong (len = %zu).", line, len); + continue; + } + + s[1] = s[10] = s[19] = s[28] = s[31] = '\0'; + // u = registry_url_allocate_nolock(&s[32], strlen(&s[32])); + u = registry_url_get(&s[32], strlen(&s[32])); + + REGISTRY_MACHINE_URL *mu = registry_machine_url_allocate(m, u, strtoul(&s[2], NULL, 16)); + mu->last_t = (uint32_t)strtoul(&s[11], NULL, 16); + mu->usages = (uint32_t)strtoul(&s[20], NULL, 16); + mu->flags = (uint8_t)strtoul(&s[29], NULL, 16); + debug(D_REGISTRY, "Registry loaded machine URL '%s', machine '%s', first: %u, last: %u, usages: %u, flags: %02x", u->url, m->guid, mu->first_t, mu->last_t, mu->usages, mu->flags); + break; + + default: + error("Registry: ignoring line %zu of filename '%s': %s.", line, registry.db_filename, s); + break; + } + } + fclose(fp); + + return line; +} diff --git a/src/registry_init.c b/src/registry_init.c new file mode 100644 index 000000000..fb61acd08 --- /dev/null +++ b/src/registry_init.c @@ -0,0 +1,136 @@ +#include "registry_internals.h" + +int registry_init(void) { + char filename[FILENAME_MAX + 1]; + + // registry enabled? + registry.enabled = config_get_boolean("registry", "enabled", 0); + + // pathnames + registry.pathname = config_get("registry", "registry db directory", VARLIB_DIR "/registry"); + if(mkdir(registry.pathname, 0770) == -1 && errno != EEXIST) + fatal("Cannot create directory '%s'.", registry.pathname); + + // filenames + snprintfz(filename, FILENAME_MAX, "%s/netdata.public.unique.id", registry.pathname); + registry.machine_guid_filename = config_get("registry", "netdata unique id file", filename); + registry_get_this_machine_guid(); + + snprintfz(filename, FILENAME_MAX, "%s/registry.db", registry.pathname); + registry.db_filename = config_get("registry", "registry db file", filename); + + snprintfz(filename, FILENAME_MAX, "%s/registry-log.db", registry.pathname); + registry.log_filename = config_get("registry", "registry log file", filename); + + // configuration options + registry.save_registry_every_entries = (unsigned long long)config_get_number("registry", "registry save db every new entries", 1000000); + registry.persons_expiration = config_get_number("registry", "registry expire idle persons days", 365) * 86400; + registry.registry_domain = config_get("registry", "registry domain", ""); + registry.registry_to_announce = config_get("registry", "registry to announce", "https://registry.my-netdata.io"); + registry.hostname = config_get("registry", "registry hostname", config_get("global", "hostname", localhost.hostname)); + registry.verify_cookies_redirects = config_get_boolean("registry", "verify browser cookies support", 1); + + setenv("NETDATA_REGISTRY_HOSTNAME", registry.hostname, 1); + setenv("NETDATA_REGISTRY_URL", registry.registry_to_announce, 1); + + registry.max_url_length = (size_t)config_get_number("registry", "max URL length", 1024); + if(registry.max_url_length < 10) { + registry.max_url_length = 10; + config_set_number("registry", "max URL length", (long long)registry.max_url_length); + } + + registry.max_name_length = (size_t)config_get_number("registry", "max URL name length", 50); + if(registry.max_name_length < 10) { + registry.max_name_length = 10; + config_set_number("registry", "max URL name length", (long long)registry.max_name_length); + } + + // initialize entries counters + registry.persons_count = 0; + registry.machines_count = 0; + registry.usages_count = 0; + registry.urls_count = 0; + registry.persons_urls_count = 0; + registry.machines_urls_count = 0; + + // initialize memory counters + registry.persons_memory = 0; + registry.machines_memory = 0; + registry.urls_memory = 0; + registry.persons_urls_memory = 0; + registry.machines_urls_memory = 0; + + // initialize locks + pthread_mutex_init(®istry.lock, NULL); + + // create dictionaries + registry.persons = dictionary_create(DICTIONARY_FLAGS); + registry.machines = dictionary_create(DICTIONARY_FLAGS); + avl_init(®istry.registry_urls_root_index, registry_url_compare); + + // load the registry database + if(registry.enabled) { + registry_log_open(); + registry_db_load(); + registry_log_load(); + + if(unlikely(registry_db_should_be_saved())) + registry_db_save(); + } + + return 0; +} + +void registry_free(void) { + if(!registry.enabled) return; + + // we need to destroy the dictionaries ourselves + // since the dictionaries use memory we allocated + + while(registry.persons->values_index.root) { + REGISTRY_PERSON *p = ((NAME_VALUE *)registry.persons->values_index.root)->value; + registry_person_del(p); + } + + while(registry.machines->values_index.root) { + REGISTRY_MACHINE *m = ((NAME_VALUE *)registry.machines->values_index.root)->value; + + // fprintf(stderr, "\nMACHINE: '%s', first: %u, last: %u, usages: %u\n", m->guid, m->first_t, m->last_t, m->usages); + + while(m->machine_urls->values_index.root) { + REGISTRY_MACHINE_URL *mu = ((NAME_VALUE *)m->machine_urls->values_index.root)->value; + + // fprintf(stderr, "\tURL: '%s', first: %u, last: %u, usages: %u, flags: 0x%02x\n", mu->url->url, mu->first_t, mu->last_t, mu->usages, mu->flags); + + //debug(D_REGISTRY, "Registry: destroying persons dictionary from url '%s'", mu->url->url); + //dictionary_destroy(mu->persons); + + debug(D_REGISTRY, "Registry: deleting url '%s' from person '%s'", mu->url->url, m->guid); + dictionary_del(m->machine_urls, mu->url->url); + + debug(D_REGISTRY, "Registry: unlinking url '%s' from machine", mu->url->url); + registry_url_unlink(mu->url); + + debug(D_REGISTRY, "Registry: freeing machine url"); + freez(mu); + } + + debug(D_REGISTRY, "Registry: deleting machine '%s' from machines registry", m->guid); + dictionary_del(registry.machines, m->guid); + + debug(D_REGISTRY, "Registry: destroying URL dictionary of machine '%s'", m->guid); + dictionary_destroy(m->machine_urls); + + debug(D_REGISTRY, "Registry: freeing machine '%s'", m->guid); + freez(m); + } + + // and free the memory of remaining dictionary structures + + debug(D_REGISTRY, "Registry: destroying persons dictionary"); + dictionary_destroy(registry.persons); + + debug(D_REGISTRY, "Registry: destroying machines dictionary"); + dictionary_destroy(registry.machines); +} + diff --git a/src/registry_internals.c b/src/registry_internals.c new file mode 100644 index 000000000..d32d549e2 --- /dev/null +++ b/src/registry_internals.c @@ -0,0 +1,323 @@ +#include "registry_internals.h" + +struct registry registry; + +// ---------------------------------------------------------------------------- +// common functions + +// parse a GUID and re-generated to be always lower case +// this is used as a protection against the variations of GUIDs +int registry_regenerate_guid(const char *guid, char *result) { + uuid_t uuid; + if(unlikely(uuid_parse(guid, uuid) == -1)) { + info("Registry: GUID '%s' is not a valid GUID.", guid); + return -1; + } + else { + uuid_unparse_lower(uuid, result); + +#ifdef NETDATA_INTERNAL_CHECKS + if(strcmp(guid, result)) + info("Registry: source GUID '%s' and re-generated GUID '%s' differ!", guid, result); +#endif /* NETDATA_INTERNAL_CHECKS */ + } + + return 0; +} + +// make sure the names of the machines / URLs do not contain any tabs +// (which are used as our separator in the database files) +// and are properly trimmed (before and after) +static inline char *registry_fix_machine_name(char *name, size_t *len) { + char *s = name?name:""; + + // skip leading spaces + while(*s && isspace(*s)) s++; + + // make sure all spaces are a SPACE + char *t = s; + while(*t) { + if(unlikely(isspace(*t))) + *t = ' '; + + t++; + } + + // remove trailing spaces + while(--t >= s) { + if(*t == ' ') + *t = '\0'; + else + break; + } + t++; + + if(likely(len)) + *len = (t - s); + + return s; +} + +static inline char *registry_fix_url(char *url, size_t *len) { + size_t l = 0; + char *s = registry_fix_machine_name(url, &l); + + // protection from too big URLs + if(l > registry.max_url_length) { + l = registry.max_url_length; + s[l] = '\0'; + } + + if(len) *len = l; + return s; +} + + +// ---------------------------------------------------------------------------- +// forward definition of functions + +extern REGISTRY_PERSON *registry_request_access(char *person_guid, char *machine_guid, char *url, char *name, time_t when); +extern REGISTRY_PERSON *registry_request_delete(char *person_guid, char *machine_guid, char *url, char *delete_url, time_t when); + + +// ---------------------------------------------------------------------------- +// HELPERS + +// verify the person, the machine and the URL exist in our DB +REGISTRY_PERSON_URL *registry_verify_request(char *person_guid, char *machine_guid, char *url, REGISTRY_PERSON **pp, REGISTRY_MACHINE **mm) { + char pbuf[GUID_LEN + 1], mbuf[GUID_LEN + 1]; + + if(!person_guid || !*person_guid || !machine_guid || !*machine_guid || !url || !*url) { + info("Registry Request Verification: invalid request! person: '%s', machine '%s', url '%s'", person_guid?person_guid:"UNSET", machine_guid?machine_guid:"UNSET", url?url:"UNSET"); + return NULL; + } + + // normalize the url + url = registry_fix_url(url, NULL); + + // make sure the person GUID is valid + if(registry_regenerate_guid(person_guid, pbuf) == -1) { + info("Registry Request Verification: invalid person GUID, person: '%s', machine '%s', url '%s'", person_guid, machine_guid, url); + return NULL; + } + person_guid = pbuf; + + // make sure the machine GUID is valid + if(registry_regenerate_guid(machine_guid, mbuf) == -1) { + info("Registry Request Verification: invalid machine GUID, person: '%s', machine '%s', url '%s'", person_guid, machine_guid, url); + return NULL; + } + machine_guid = mbuf; + + // make sure the machine exists + REGISTRY_MACHINE *m = registry_machine_find(machine_guid); + if(!m) { + info("Registry Request Verification: machine not found, person: '%s', machine '%s', url '%s'", person_guid, machine_guid, url); + return NULL; + } + if(mm) *mm = m; + + // make sure the person exist + REGISTRY_PERSON *p = registry_person_find(person_guid); + if(!p) { + info("Registry Request Verification: person not found, person: '%s', machine '%s', url '%s'", person_guid, machine_guid, url); + return NULL; + } + if(pp) *pp = p; + + REGISTRY_PERSON_URL *pu = registry_person_url_index_find(p, url); + if(!pu) { + info("Registry Request Verification: URL not found for person, person: '%s', machine '%s', url '%s'", person_guid, machine_guid, url); + return NULL; + } + return pu; +} + + +// ---------------------------------------------------------------------------- +// REGISTRY REQUESTS + +REGISTRY_PERSON *registry_request_access(char *person_guid, char *machine_guid, char *url, char *name, time_t when) { + debug(D_REGISTRY, "registry_request_access('%s', '%s', '%s'): NEW REQUEST", (person_guid)?person_guid:"", machine_guid, url); + + REGISTRY_MACHINE *m = registry_machine_get(machine_guid, when); + if(!m) return NULL; + + // make sure the name is valid + size_t namelen; + name = registry_fix_machine_name(name, &namelen); + + size_t urllen; + url = registry_fix_url(url, &urllen); + + REGISTRY_PERSON *p = registry_person_get(person_guid, when); + + REGISTRY_URL *u = registry_url_get(url, urllen); + registry_person_link_to_url(p, m, u, name, namelen, when); + registry_machine_link_to_url(m, u, when); + + registry_log('A', p, m, u, name); + + registry.usages_count++; + + return p; +} + +REGISTRY_PERSON *registry_request_delete(char *person_guid, char *machine_guid, char *url, char *delete_url, time_t when) { + (void) when; + + REGISTRY_PERSON *p = NULL; + REGISTRY_MACHINE *m = NULL; + REGISTRY_PERSON_URL *pu = registry_verify_request(person_guid, machine_guid, url, &p, &m); + if(!pu || !p || !m) return NULL; + + // normalize the url + delete_url = registry_fix_url(delete_url, NULL); + + // make sure the user is not deleting the url it uses + if(!strcmp(delete_url, pu->url->url)) { + info("Registry Delete Request: delete URL is the one currently accessed, person: '%s', machine '%s', url '%s', delete url '%s'" + , p->guid, m->guid, pu->url->url, delete_url); + return NULL; + } + + REGISTRY_PERSON_URL *dpu = registry_person_url_index_find(p, delete_url); + if(!dpu) { + info("Registry Delete Request: URL not found for person: '%s', machine '%s', url '%s', delete url '%s'", p->guid + , m->guid, pu->url->url, delete_url); + return NULL; + } + + registry_log('D', p, m, pu->url, dpu->url->url); + registry_person_unlink_from_url(p, dpu); + + return p; +} + + +// a structure to pass to the dictionary_get_all() callback handler +struct machine_request_callback_data { + REGISTRY_MACHINE *find_this_machine; + REGISTRY_PERSON_URL *result; +}; + +// the callback function +// this will be run for every PERSON_URL of this PERSON +static int machine_request_callback(void *entry, void *data) { + REGISTRY_PERSON_URL *mypu = (REGISTRY_PERSON_URL *)entry; + struct machine_request_callback_data *myrdata = (struct machine_request_callback_data *)data; + + if(mypu->machine == myrdata->find_this_machine) { + myrdata->result = mypu; + return -1; // this will also stop the walk through + } + + return 0; // continue +} + +REGISTRY_MACHINE *registry_request_machine(char *person_guid, char *machine_guid, char *url, char *request_machine, time_t when) { + (void)when; + + char mbuf[GUID_LEN + 1]; + + REGISTRY_PERSON *p = NULL; + REGISTRY_MACHINE *m = NULL; + REGISTRY_PERSON_URL *pu = registry_verify_request(person_guid, machine_guid, url, &p, &m); + if(!pu || !p || !m) return NULL; + + // make sure the machine GUID is valid + if(registry_regenerate_guid(request_machine, mbuf) == -1) { + info("Registry Machine URLs request: invalid machine GUID, person: '%s', machine '%s', url '%s', request machine '%s'", p->guid, m->guid, pu->url->url, request_machine); + return NULL; + } + request_machine = mbuf; + + // make sure the machine exists + m = registry_machine_find(request_machine); + if(!m) { + info("Registry Machine URLs request: machine not found, person: '%s', machine '%s', url '%s', request machine '%s'", p->guid, machine_guid, pu->url->url, request_machine); + return NULL; + } + + // Verify the user has in the past accessed this machine + // We will walk through the PERSON_URLs to find the machine + // linking to our machine + + // a structure to pass to the dictionary_get_all() callback handler + struct machine_request_callback_data rdata = { m, NULL }; + + // request a walk through on the dictionary + avl_traverse(&p->person_urls, machine_request_callback, &rdata); + + if(rdata.result) + return m; + + return NULL; +} + + +// ---------------------------------------------------------------------------- +// REGISTRY THIS MACHINE UNIQUE ID + +static inline int is_machine_guid_blacklisted(const char *guid) { + // these are machine GUIDs that have been included in distribution packages. + // we blacklist them here, so that the next version of netdata will generate + // new ones. + + if(!strcmp(guid, "8a795b0c-2311-11e6-8563-000c295076a6") + || !strcmp(guid, "4aed1458-1c3e-11e6-a53f-000c290fc8f5") + ) { + error("Blacklisted machine GUID '%s' found.", guid); + return 1; + } + + return 0; +} + +char *registry_get_this_machine_guid(void) { + if(likely(registry.machine_guid[0])) + return registry.machine_guid; + + // read it from disk + int fd = open(registry.machine_guid_filename, O_RDONLY); + if(fd != -1) { + char buf[GUID_LEN + 1]; + if(read(fd, buf, GUID_LEN) != GUID_LEN) + error("Failed to read machine GUID from '%s'", registry.machine_guid_filename); + else { + buf[GUID_LEN] = '\0'; + if(registry_regenerate_guid(buf, registry.machine_guid) == -1) { + error("Failed to validate machine GUID '%s' from '%s'. Ignoring it - this might mean this netdata will appear as duplicate in the registry.", + buf, registry.machine_guid_filename); + + registry.machine_guid[0] = '\0'; + } + else if(is_machine_guid_blacklisted(registry.machine_guid)) + registry.machine_guid[0] = '\0'; + } + close(fd); + } + + // generate a new one? + if(!registry.machine_guid[0]) { + uuid_t uuid; + + uuid_generate_time(uuid); + uuid_unparse_lower(uuid, registry.machine_guid); + registry.machine_guid[GUID_LEN] = '\0'; + + // save it + fd = open(registry.machine_guid_filename, O_WRONLY|O_CREAT|O_TRUNC, 444); + if(fd == -1) + fatal("Cannot create unique machine id file '%s'. Please fix this.", registry.machine_guid_filename); + + if(write(fd, registry.machine_guid, GUID_LEN) != GUID_LEN) + fatal("Cannot write the unique machine id file '%s'. Please fix this.", registry.machine_guid_filename); + + close(fd); + } + + setenv("NETDATA_REGISTRY_UNIQUE_ID", registry.machine_guid, 1); + + return registry.machine_guid; +} diff --git a/src/registry_internals.h b/src/registry_internals.h new file mode 100644 index 000000000..9c0b74452 --- /dev/null +++ b/src/registry_internals.h @@ -0,0 +1,92 @@ +#include "common.h" + +#ifndef NETDATA_REGISTRY_INTERNALS_H_H +#define NETDATA_REGISTRY_INTERNALS_H_H + +#define REGISTRY_URL_FLAGS_DEFAULT 0x00 +#define REGISTRY_URL_FLAGS_EXPIRED 0x01 + +#define DICTIONARY_FLAGS DICTIONARY_FLAG_VALUE_LINK_DONT_CLONE | DICTIONARY_FLAG_NAME_LINK_DONT_CLONE | DICTIONARY_FLAG_SINGLE_THREADED + +// ---------------------------------------------------------------------------- +// COMMON structures + +struct registry { + int enabled; + + char machine_guid[GUID_LEN + 1]; + + // entries counters / statistics + unsigned long long persons_count; + unsigned long long machines_count; + unsigned long long usages_count; + unsigned long long urls_count; + unsigned long long persons_urls_count; + unsigned long long machines_urls_count; + unsigned long long log_count; + + // memory counters / statistics + unsigned long long persons_memory; + unsigned long long machines_memory; + unsigned long long urls_memory; + unsigned long long persons_urls_memory; + unsigned long long machines_urls_memory; + + // configuration + unsigned long long save_registry_every_entries; + char *registry_domain; + char *hostname; + char *registry_to_announce; + time_t persons_expiration; // seconds to expire idle persons + int verify_cookies_redirects; + + size_t max_url_length; + size_t max_name_length; + + // file/path names + char *pathname; + char *db_filename; + char *log_filename; + char *machine_guid_filename; + + // open files + FILE *log_fp; + + // the database + DICTIONARY *persons; // dictionary of REGISTRY_PERSON *, with key the REGISTRY_PERSON.guid + DICTIONARY *machines; // dictionary of REGISTRY_MACHINE *, with key the REGISTRY_MACHINE.guid + + avl_tree registry_urls_root_index; + + pthread_mutex_t lock; +}; + +extern int registry_regenerate_guid(const char *guid, char *result); + +#include "registry_url.h" +#include "registry_machine.h" +#include "registry_person.h" +#include "registry.h" + +extern struct registry registry; + +extern char *registry_get_this_machine_guid(void); + +// REGISTRY LOW-LEVEL REQUESTS (in registry-internals.c) +extern REGISTRY_PERSON *registry_request_access(char *person_guid, char *machine_guid, char *url, char *name, time_t when); +extern REGISTRY_PERSON *registry_request_delete(char *person_guid, char *machine_guid, char *url, char *delete_url, time_t when); +extern REGISTRY_MACHINE *registry_request_machine(char *person_guid, char *machine_guid, char *url, char *request_machine, time_t when); + +// REGISTRY LOG (in registry_log.c) +extern void registry_log(const char action, REGISTRY_PERSON *p, REGISTRY_MACHINE *m, REGISTRY_URL *u, char *name); +extern int registry_log_open(void); +extern void registry_log_close(void); +extern void registry_log_recreate(void); +extern ssize_t registry_log_load(void); + +// REGISTRY DB (in registry_db.c) +extern int registry_db_save(void); +extern size_t registry_db_load(void); +extern int registry_db_should_be_saved(void); + +#endif //NETDATA_REGISTRY_INTERNALS_H_H diff --git a/src/registry_log.c b/src/registry_log.c new file mode 100644 index 000000000..3229a34b4 --- /dev/null +++ b/src/registry_log.c @@ -0,0 +1,133 @@ +#include "registry_internals.h" + +void registry_log(const char action, REGISTRY_PERSON *p, REGISTRY_MACHINE *m, REGISTRY_URL *u, char *name) { + if(likely(registry.log_fp)) { + if(unlikely(fprintf(registry.log_fp, "%c\t%08x\t%s\t%s\t%s\t%s\n", + action, + p->last_t, + p->guid, + m->guid, + name, + u->url) < 0)) + error("Registry: failed to save log. Registry data may be lost in case of abnormal restart."); + + // we increase the counter even on failures + // so that the registry will be saved periodically + registry.log_count++; + + // this must be outside the log_lock(), or a deadlock will happen. + // registry_db_save() checks the same inside the log_lock, so only + // one thread will save the db + if(unlikely(registry_db_should_be_saved())) + registry_db_save(); + } +} + +int registry_log_open(void) { + if(registry.log_fp) + fclose(registry.log_fp); + + registry.log_fp = fopen(registry.log_filename, "a"); + if(registry.log_fp) { + if (setvbuf(registry.log_fp, NULL, _IOLBF, 0) != 0) + error("Cannot set line buffering on registry log file."); + return 0; + } + + error("Cannot open registry log file '%s'. Registry data will be lost in case of netdata or server crash.", registry.log_filename); + return -1; +} + +void registry_log_close(void) { + if(registry.log_fp) { + fclose(registry.log_fp); + registry.log_fp = NULL; + } +} + +void registry_log_recreate(void) { + if(registry.log_fp != NULL) { + registry_log_close(); + + // open it with truncate + registry.log_fp = fopen(registry.log_filename, "w"); + if(registry.log_fp) fclose(registry.log_fp); + else error("Cannot truncate registry log '%s'", registry.log_filename); + + registry.log_fp = NULL; + registry_log_open(); + } +} + +ssize_t registry_log_load(void) { + ssize_t line = -1; + + // closing the log is required here + // otherwise we will append to it the values we read + registry_log_close(); + + debug(D_REGISTRY, "Registry: loading active db from: %s", registry.log_filename); + FILE *fp = fopen(registry.log_filename, "r"); + if(!fp) + error("Registry: cannot open registry file: %s", registry.log_filename); + else { + char *s, buf[4096 + 1]; + line = 0; + size_t len = 0; + + while ((s = fgets_trim_len(buf, 4096, fp, &len))) { + line++; + + switch (s[0]) { + case 'A': // accesses + case 'D': // deletes + + // verify it is valid + if (unlikely(len < 85 || s[1] != '\t' || s[10] != '\t' || s[47] != '\t' || s[84] != '\t')) { + error("Registry: log line %zd is wrong (len = %zu).", line, len); + continue; + } + s[1] = s[10] = s[47] = s[84] = '\0'; + + // get the variables + time_t when = strtoul(&s[2], NULL, 16); + char *person_guid = &s[11]; + char *machine_guid = &s[48]; + char *name = &s[85]; + + // skip the name to find the url + char *url = name; + while(*url && *url != '\t') url++; + if(!*url) { + error("Registry: log line %zd does not have a url.", line); + continue; + } + *url++ = '\0'; + + // make sure the person exists + // without this, a new person guid will be created + REGISTRY_PERSON *p = registry_person_find(person_guid); + if(!p) p = registry_person_allocate(person_guid, when); + + if(s[0] == 'A') + registry_request_access(p->guid, machine_guid, url, name, when); + else + registry_request_delete(p->guid, machine_guid, url, name, when); + + registry.log_count++; + break; + + default: + error("Registry: ignoring line %zd of filename '%s': %s.", line, registry.log_filename, s); + break; + } + } + + fclose(fp); + } + + // open the log again + registry_log_open(); + + return line; +} diff --git a/src/registry_machine.c b/src/registry_machine.c new file mode 100644 index 000000000..3510736df --- /dev/null +++ b/src/registry_machine.c @@ -0,0 +1,101 @@ +#include "registry_internals.h" + +// ---------------------------------------------------------------------------- +// MACHINE + +REGISTRY_MACHINE *registry_machine_find(const char *machine_guid) { + debug(D_REGISTRY, "Registry: registry_machine_find('%s')", machine_guid); + return dictionary_get(registry.machines, machine_guid); +} + +REGISTRY_MACHINE_URL *registry_machine_url_allocate(REGISTRY_MACHINE *m, REGISTRY_URL *u, time_t when) { + debug(D_REGISTRY, "registry_machine_url_allocate('%s', '%s'): allocating %zu bytes", m->guid, u->url, sizeof(REGISTRY_MACHINE_URL)); + + REGISTRY_MACHINE_URL *mu = mallocz(sizeof(REGISTRY_MACHINE_URL)); + + mu->first_t = mu->last_t = (uint32_t)when; + mu->usages = 1; + mu->url = u; + mu->flags = REGISTRY_URL_FLAGS_DEFAULT; + + registry.machines_urls_memory += sizeof(REGISTRY_MACHINE_URL); + + debug(D_REGISTRY, "registry_machine_url_allocate('%s', '%s'): indexing URL in machine", m->guid, u->url); + dictionary_set(m->machine_urls, u->url, mu, sizeof(REGISTRY_MACHINE_URL)); + + registry_url_link(u); + + return mu; +} + +REGISTRY_MACHINE *registry_machine_allocate(const char *machine_guid, time_t when) { + debug(D_REGISTRY, "Registry: registry_machine_allocate('%s'): creating new machine, sizeof(MACHINE)=%zu", machine_guid, sizeof(REGISTRY_MACHINE)); + + REGISTRY_MACHINE *m = mallocz(sizeof(REGISTRY_MACHINE)); + + strncpyz(m->guid, machine_guid, GUID_LEN); + + debug(D_REGISTRY, "Registry: registry_machine_allocate('%s'): creating dictionary of urls", machine_guid); + m->machine_urls = dictionary_create(DICTIONARY_FLAGS); + + m->first_t = m->last_t = (uint32_t)when; + m->usages = 0; + + registry.machines_memory += sizeof(REGISTRY_MACHINE); + + registry.machines_count++; + dictionary_set(registry.machines, m->guid, m, sizeof(REGISTRY_MACHINE)); + + return m; +} + +// 1. validate machine GUID +// 2. if it is valid, find it or create it and return it +// 3. if it is not valid, return NULL +REGISTRY_MACHINE *registry_machine_get(const char *machine_guid, time_t when) { + REGISTRY_MACHINE *m = NULL; + + if(likely(machine_guid && *machine_guid)) { + // validate it is a GUID + char buf[GUID_LEN + 1]; + if(unlikely(registry_regenerate_guid(machine_guid, buf) == -1)) + info("Registry: machine guid '%s' is not a valid guid. Ignoring it.", machine_guid); + else { + machine_guid = buf; + m = registry_machine_find(machine_guid); + if(!m) m = registry_machine_allocate(machine_guid, when); + } + } + + return m; +} + + +// ---------------------------------------------------------------------------- +// LINKING OF OBJECTS + +REGISTRY_MACHINE_URL *registry_machine_link_to_url(REGISTRY_MACHINE *m, REGISTRY_URL *u, time_t when) { + debug(D_REGISTRY, "registry_machine_link_to_url('%s', '%s'): searching for URL in machine", m->guid, u->url); + + REGISTRY_MACHINE_URL *mu = dictionary_get(m->machine_urls, u->url); + if(!mu) { + debug(D_REGISTRY, "registry_machine_link_to_url('%s', '%s'): not found", m->guid, u->url); + mu = registry_machine_url_allocate(m, u, when); + registry.machines_urls_count++; + } + else { + debug(D_REGISTRY, "registry_machine_link_to_url('%s', '%s'): found", m->guid, u->url); + mu->usages++; + if(likely(mu->last_t < (uint32_t)when)) mu->last_t = (uint32_t)when; + } + + m->usages++; + if(likely(m->last_t < (uint32_t)when)) m->last_t = (uint32_t)when; + + if(mu->flags & REGISTRY_URL_FLAGS_EXPIRED) { + debug(D_REGISTRY, "registry_machine_link_to_url('%s', '%s'): accessing an expired URL.", m->guid, u->url); + mu->flags &= ~REGISTRY_URL_FLAGS_EXPIRED; + } + + return mu; +} diff --git a/src/registry_machine.h b/src/registry_machine.h new file mode 100644 index 000000000..be824d168 --- /dev/null +++ b/src/registry_machine.h @@ -0,0 +1,41 @@ +#ifndef NETDATA_REGISTRY_MACHINE_H +#define NETDATA_REGISTRY_MACHINE_H + +#include "registry_internals.h" + +// ---------------------------------------------------------------------------- +// MACHINE structures + +// For each MACHINE-URL pair we keep this +struct registry_machine_url { + REGISTRY_URL *url; // de-duplicated URL + + uint8_t flags; + + uint32_t first_t; // the first time we saw this + uint32_t last_t; // the last time we saw this + uint32_t usages; // how many times this has been accessed +}; +typedef struct registry_machine_url REGISTRY_MACHINE_URL; + +// A machine +struct registry_machine { + char guid[GUID_LEN + 1]; // the GUID + + uint32_t links; // the number of REGISTRY_PERSON_URL linked to this machine + + DICTIONARY *machine_urls; // MACHINE_URL * + + uint32_t first_t; // the first time we saw this + uint32_t last_t; // the last time we saw this + uint32_t usages; // how many times this has been accessed +}; +typedef struct registry_machine REGISTRY_MACHINE; + +extern REGISTRY_MACHINE *registry_machine_find(const char *machine_guid); +extern REGISTRY_MACHINE_URL *registry_machine_url_allocate(REGISTRY_MACHINE *m, REGISTRY_URL *u, time_t when); +extern REGISTRY_MACHINE *registry_machine_allocate(const char *machine_guid, time_t when); +extern REGISTRY_MACHINE *registry_machine_get(const char *machine_guid, time_t when); +extern REGISTRY_MACHINE_URL *registry_machine_link_to_url(REGISTRY_MACHINE *m, REGISTRY_URL *u, time_t when); + +#endif //NETDATA_REGISTRY_MACHINE_H diff --git a/src/registry_person.c b/src/registry_person.c new file mode 100644 index 000000000..5f9099c9a --- /dev/null +++ b/src/registry_person.c @@ -0,0 +1,264 @@ +#include "registry_internals.h" + +// ---------------------------------------------------------------------------- +// PERSON_URL INDEX + +int person_url_compare(void *a, void *b) { + register uint32_t hash1 = ((REGISTRY_PERSON_URL *)a)->url->hash; + register uint32_t hash2 = ((REGISTRY_PERSON_URL *)b)->url->hash; + + if(hash1 < hash2) return -1; + else if(hash1 > hash2) return 1; + else return strcmp(((REGISTRY_PERSON_URL *)a)->url->url, ((REGISTRY_PERSON_URL *)b)->url->url); +} + +inline REGISTRY_PERSON_URL *registry_person_url_index_find(REGISTRY_PERSON *p, const char *url) { + debug(D_REGISTRY, "Registry: registry_person_url_index_find('%s', '%s')", p->guid, url); + + char buf[sizeof(REGISTRY_URL) + strlen(url)]; + + REGISTRY_URL *u = (REGISTRY_URL *)&buf; + strcpy(u->url, url); + u->hash = simple_hash(u->url); + + REGISTRY_PERSON_URL tpu = { .url = u }; + + REGISTRY_PERSON_URL *pu = (REGISTRY_PERSON_URL *)avl_search(&p->person_urls, (void *)&tpu); + return pu; +} + +inline REGISTRY_PERSON_URL *registry_person_url_index_add(REGISTRY_PERSON *p, REGISTRY_PERSON_URL *pu) { + debug(D_REGISTRY, "Registry: registry_person_url_index_add('%s', '%s')", p->guid, pu->url->url); + REGISTRY_PERSON_URL *tpu = (REGISTRY_PERSON_URL *)avl_insert(&(p->person_urls), (avl *)(pu)); + if(tpu != pu) + error("Registry: registry_person_url_index_add('%s', '%s') already exists as '%s'", p->guid, pu->url->url, tpu->url->url); + + return tpu; +} + +inline REGISTRY_PERSON_URL *registry_person_url_index_del(REGISTRY_PERSON *p, REGISTRY_PERSON_URL *pu) { + debug(D_REGISTRY, "Registry: registry_person_url_index_del('%s', '%s')", p->guid, pu->url->url); + REGISTRY_PERSON_URL *tpu = (REGISTRY_PERSON_URL *)avl_remove(&(p->person_urls), (avl *)(pu)); + if(!tpu) + error("Registry: registry_person_url_index_del('%s', '%s') deleted nothing", p->guid, pu->url->url); + else if(tpu != pu) + error("Registry: registry_person_url_index_del('%s', '%s') deleted wrong URL '%s'", p->guid, pu->url->url, tpu->url->url); + + return tpu; +} + +// ---------------------------------------------------------------------------- +// PERSON_URL + +REGISTRY_PERSON_URL *registry_person_url_allocate(REGISTRY_PERSON *p, REGISTRY_MACHINE *m, REGISTRY_URL *u, char *name, size_t namelen, time_t when) { + debug(D_REGISTRY, "registry_person_url_allocate('%s', '%s', '%s'): allocating %zu bytes", p->guid, m->guid, u->url, sizeof(REGISTRY_PERSON_URL) + namelen); + + // protection from too big names + if(namelen > registry.max_name_length) + namelen = registry.max_name_length; + + REGISTRY_PERSON_URL *pu = mallocz(sizeof(REGISTRY_PERSON_URL) + namelen); + + // a simple strcpy() should do the job + // but I prefer to be safe, since the caller specified urllen + strncpyz(pu->machine_name, name, namelen); + + pu->machine = m; + pu->first_t = pu->last_t = (uint32_t)when; + pu->usages = 1; + pu->url = u; + pu->flags = REGISTRY_URL_FLAGS_DEFAULT; + m->links++; + + registry.persons_urls_memory += sizeof(REGISTRY_PERSON_URL) + namelen; + + debug(D_REGISTRY, "registry_person_url_allocate('%s', '%s', '%s'): indexing URL in person", p->guid, m->guid, u->url); + REGISTRY_PERSON_URL *tpu = registry_person_url_index_add(p, pu); + if(tpu != pu) { + error("Registry: Attempted to add duplicate person url '%s' with name '%s' to person '%s'", u->url, name, p->guid); + free(pu); + pu = tpu; + } + else + registry_url_link(u); + + return pu; +} + +void registry_person_url_free(REGISTRY_PERSON *p, REGISTRY_PERSON_URL *pu) { + debug(D_REGISTRY, "registry_person_url_free('%s', '%s')", p->guid, pu->url->url); + + REGISTRY_PERSON_URL *tpu = registry_person_url_index_del(p, pu); + if(tpu) { + registry_url_unlink(tpu->url); + tpu->machine->links--; + registry.persons_urls_memory -= sizeof(REGISTRY_PERSON_URL) + strlen(tpu->machine_name); + freez(tpu); + } +} + +// this function is needed to change the name of a PERSON_URL +REGISTRY_PERSON_URL *registry_person_url_reallocate(REGISTRY_PERSON *p, REGISTRY_MACHINE *m, REGISTRY_URL *u, char *name, size_t namelen, time_t when, REGISTRY_PERSON_URL *pu) { + debug(D_REGISTRY, "registry_person_url_reallocate('%s', '%s', '%s'): allocating %zu bytes", p->guid, m->guid, u->url, sizeof(REGISTRY_PERSON_URL) + namelen); + + // keep a backup + REGISTRY_PERSON_URL pu2 = { + .first_t = pu->first_t, + .last_t = pu->last_t, + .usages = pu->usages, + .flags = pu->flags, + .machine = pu->machine, + .machine_name = "" + }; + + // remove the existing one from the index + registry_person_url_free(p, pu); + pu = &pu2; + + // allocate a new one + REGISTRY_PERSON_URL *tpu = registry_person_url_allocate(p, m, u, name, namelen, when); + tpu->first_t = pu->first_t; + tpu->last_t = pu->last_t; + tpu->usages = pu->usages; + tpu->flags = pu->flags; + + return tpu; +} + + +// ---------------------------------------------------------------------------- +// PERSON + +REGISTRY_PERSON *registry_person_find(const char *person_guid) { + debug(D_REGISTRY, "Registry: registry_person_find('%s')", person_guid); + return dictionary_get(registry.persons, person_guid); +} + +REGISTRY_PERSON *registry_person_allocate(const char *person_guid, time_t when) { + debug(D_REGISTRY, "Registry: registry_person_allocate('%s'): allocating new person, sizeof(PERSON)=%zu", (person_guid)?person_guid:"", sizeof(REGISTRY_PERSON)); + + REGISTRY_PERSON *p = mallocz(sizeof(REGISTRY_PERSON)); + if(!person_guid) { + for(;;) { + uuid_t uuid; + uuid_generate(uuid); + uuid_unparse_lower(uuid, p->guid); + + debug(D_REGISTRY, "Registry: Checking if the generated person guid '%s' is unique", p->guid); + if (!dictionary_get(registry.persons, p->guid)) { + debug(D_REGISTRY, "Registry: generated person guid '%s' is unique", p->guid); + break; + } + else + info("Registry: generated person guid '%s' found in the registry. Retrying...", p->guid); + } + } + else + strncpyz(p->guid, person_guid, GUID_LEN); + + debug(D_REGISTRY, "Registry: registry_person_allocate('%s'): creating dictionary of urls", p->guid); + avl_init(&p->person_urls, person_url_compare); + + p->first_t = p->last_t = (uint32_t)when; + p->usages = 0; + + registry.persons_memory += sizeof(REGISTRY_PERSON); + + registry.persons_count++; + dictionary_set(registry.persons, p->guid, p, sizeof(REGISTRY_PERSON)); + + return p; +} + + +// 1. validate person GUID +// 2. if it is valid, find it +// 3. if it is not valid, create a new one +// 4. return it +REGISTRY_PERSON *registry_person_get(const char *person_guid, time_t when) { + debug(D_REGISTRY, "Registry: registry_person_get('%s'): creating dictionary of urls", person_guid); + + REGISTRY_PERSON *p = NULL; + + if(person_guid && *person_guid) { + char buf[GUID_LEN + 1]; + // validate it is a GUID + if(unlikely(registry_regenerate_guid(person_guid, buf) == -1)) + info("Registry: person guid '%s' is not a valid guid. Ignoring it.", person_guid); + else { + person_guid = buf; + p = registry_person_find(person_guid); + } + } + + if(!p) p = registry_person_allocate(NULL, when); + + return p; +} + +void registry_person_del(REGISTRY_PERSON *p) { + debug(D_REGISTRY, "Registry: registry_person_del('%s'): creating dictionary of urls", p->guid); + + while(p->person_urls.root) + registry_person_unlink_from_url(p, (REGISTRY_PERSON_URL *)p->person_urls.root); + + debug(D_REGISTRY, "Registry: deleting person '%s' from persons registry", p->guid); + dictionary_del(registry.persons, p->guid); + + debug(D_REGISTRY, "Registry: freeing person '%s'", p->guid); + freez(p); +} + +// ---------------------------------------------------------------------------- +// LINKING OF OBJECTS + +REGISTRY_PERSON_URL *registry_person_link_to_url(REGISTRY_PERSON *p, REGISTRY_MACHINE *m, REGISTRY_URL *u, char *name, size_t namelen, time_t when) { + debug(D_REGISTRY, "registry_person_link_to_url('%s', '%s', '%s'): searching for URL in person", p->guid, m->guid, u->url); + + REGISTRY_PERSON_URL *pu = registry_person_url_index_find(p, u->url); + if(!pu) { + debug(D_REGISTRY, "registry_person_link_to_url('%s', '%s', '%s'): not found", p->guid, m->guid, u->url); + pu = registry_person_url_allocate(p, m, u, name, namelen, when); + registry.persons_urls_count++; + } + else { + debug(D_REGISTRY, "registry_person_link_to_url('%s', '%s', '%s'): found", p->guid, m->guid, u->url); + pu->usages++; + if(likely(pu->last_t < (uint32_t)when)) pu->last_t = (uint32_t)when; + + if(pu->machine != m) { + REGISTRY_MACHINE_URL *mu = dictionary_get(pu->machine->machine_urls, u->url); + if(mu) { + debug(D_REGISTRY, "registry_person_link_to_url('%s', '%s', '%s'): URL switched machines (old was '%s') - expiring it from previous machine.", + p->guid, m->guid, u->url, pu->machine->guid); + mu->flags |= REGISTRY_URL_FLAGS_EXPIRED; + } + else { + debug(D_REGISTRY, "registry_person_link_to_url('%s', '%s', '%s'): URL switched machines (old was '%s') - but the URL is not linked to the old machine.", + p->guid, m->guid, u->url, pu->machine->guid); + } + + pu->machine->links--; + pu->machine = m; + } + + if(strcmp(pu->machine_name, name)) { + // the name of the PERSON_URL has changed ! + pu = registry_person_url_reallocate(p, m, u, name, namelen, when, pu); + } + } + + p->usages++; + if(likely(p->last_t < (uint32_t)when)) p->last_t = (uint32_t)when; + + if(pu->flags & REGISTRY_URL_FLAGS_EXPIRED) { + debug(D_REGISTRY, "registry_person_link_to_url('%s', '%s', '%s'): accessing an expired URL. Re-enabling URL.", p->guid, m->guid, u->url); + pu->flags &= ~REGISTRY_URL_FLAGS_EXPIRED; + } + + return pu; +} + +void registry_person_unlink_from_url(REGISTRY_PERSON *p, REGISTRY_PERSON_URL *pu) { + registry_person_url_free(p, pu); +} diff --git a/src/registry_person.h b/src/registry_person.h new file mode 100644 index 000000000..5f6cc2443 --- /dev/null +++ b/src/registry_person.h @@ -0,0 +1,60 @@ +#ifndef NETDATA_REGISTRY_PERSON_H +#define NETDATA_REGISTRY_PERSON_H + +#include "registry_internals.h" + +// ---------------------------------------------------------------------------- +// PERSON structures + +// for each PERSON-URL pair we keep this +struct registry_person_url { + avl avl; // binary tree node + + REGISTRY_URL *url; // de-duplicated URL + REGISTRY_MACHINE *machine; // link the MACHINE of this URL + + uint8_t flags; + + uint32_t first_t; // the first time we saw this + uint32_t last_t; // the last time we saw this + uint32_t usages; // how many times this has been accessed + + char machine_name[1]; // the name of the machine, as known by the user + // dynamically allocated to fit properly +}; +typedef struct registry_person_url REGISTRY_PERSON_URL; + +// A person +struct registry_person { + char guid[GUID_LEN + 1]; // the person GUID + + avl_tree person_urls; // dictionary of PERSON_URLs + + uint32_t first_t; // the first time we saw this + uint32_t last_t; // the last time we saw this + uint32_t usages; // how many times this has been accessed + + //uint32_t flags; + //char *email; +}; +typedef struct registry_person REGISTRY_PERSON; + +// PERSON_URL +extern REGISTRY_PERSON_URL *registry_person_url_index_find(REGISTRY_PERSON *p, const char *url); +extern REGISTRY_PERSON_URL *registry_person_url_index_add(REGISTRY_PERSON *p, REGISTRY_PERSON_URL *pu) NEVERNULL WARNUNUSED; +extern REGISTRY_PERSON_URL *registry_person_url_index_del(REGISTRY_PERSON *p, REGISTRY_PERSON_URL *pu) WARNUNUSED; + +extern REGISTRY_PERSON_URL *registry_person_url_allocate(REGISTRY_PERSON *p, REGISTRY_MACHINE *m, REGISTRY_URL *u, char *name, size_t namelen, time_t when); +extern REGISTRY_PERSON_URL *registry_person_url_reallocate(REGISTRY_PERSON *p, REGISTRY_MACHINE *m, REGISTRY_URL *u, char *name, size_t namelen, time_t when, REGISTRY_PERSON_URL *pu); + +// PERSON +extern REGISTRY_PERSON *registry_person_find(const char *person_guid); +extern REGISTRY_PERSON *registry_person_allocate(const char *person_guid, time_t when); +extern REGISTRY_PERSON *registry_person_get(const char *person_guid, time_t when); +extern void registry_person_del(REGISTRY_PERSON *p); + +// LINKING PERSON -> PERSON_URL +extern REGISTRY_PERSON_URL *registry_person_link_to_url(REGISTRY_PERSON *p, REGISTRY_MACHINE *m, REGISTRY_URL *u, char *name, size_t namelen, time_t when); +extern void registry_person_unlink_from_url(REGISTRY_PERSON *p, REGISTRY_PERSON_URL *pu); + +#endif //NETDATA_REGISTRY_PERSON_H diff --git a/src/registry_url.c b/src/registry_url.c new file mode 100644 index 000000000..52d36a898 --- /dev/null +++ b/src/registry_url.c @@ -0,0 +1,85 @@ +#include "registry_internals.h" + +// ---------------------------------------------------------------------------- +// REGISTRY_URL + +int registry_url_compare(void *a, void *b) { + if(((REGISTRY_URL *)a)->hash < ((REGISTRY_URL *)b)->hash) return -1; + else if(((REGISTRY_URL *)a)->hash > ((REGISTRY_URL *)b)->hash) return 1; + else return strcmp(((REGISTRY_URL *)a)->url, ((REGISTRY_URL *)b)->url); +} + +inline REGISTRY_URL *registry_url_index_add(REGISTRY_URL *u) { + return (REGISTRY_URL *)avl_insert(&(registry.registry_urls_root_index), (avl *)(u)); +} + +inline REGISTRY_URL *registry_url_index_del(REGISTRY_URL *u) { + return (REGISTRY_URL *)avl_remove(&(registry.registry_urls_root_index), (avl *)(u)); +} + +REGISTRY_URL *registry_url_get(const char *url, size_t urllen) { + // protection from too big URLs + if(urllen > registry.max_url_length) + urllen = registry.max_url_length; + + debug(D_REGISTRY, "Registry: registry_url_get('%s', %zu)", url, urllen); + + char buf[sizeof(REGISTRY_URL) + urllen]; // no need for +1, 1 is already in REGISTRY_URL + REGISTRY_URL *n = (REGISTRY_URL *)&buf[0]; + n->len = (uint16_t)urllen; + strncpyz(n->url, url, n->len); + n->hash = simple_hash(n->url); + + REGISTRY_URL *u = (REGISTRY_URL *)avl_search(&(registry.registry_urls_root_index), (avl *)n); + if(!u) { + debug(D_REGISTRY, "Registry: registry_url_get('%s', %zu): allocating %zu bytes", url, urllen, sizeof(REGISTRY_URL) + urllen); + u = callocz(1, sizeof(REGISTRY_URL) + urllen); // no need for +1, 1 is already in REGISTRY_URL + + // a simple strcpy() should do the job + // but I prefer to be safe, since the caller specified urllen + u->len = (uint16_t)urllen; + strncpyz(u->url, url, u->len); + u->links = 0; + u->hash = simple_hash(u->url); + + registry.urls_memory += sizeof(REGISTRY_URL) + urllen; // no need for +1, 1 is already in REGISTRY_URL + + debug(D_REGISTRY, "Registry: registry_url_get('%s'): indexing it", url); + n = registry_url_index_add(u); + if(n != u) { + error("INTERNAL ERROR: registry_url_get(): url '%s' already exists in the registry as '%s'", u->url, n->url); + free(u); + u = n; + } + else + registry.urls_count++; + } + + return u; +} + +void registry_url_link(REGISTRY_URL *u) { + u->links++; + debug(D_REGISTRY, "Registry: registry_url_link('%s'): URL has now %u links", u->url, u->links); +} + +void registry_url_unlink(REGISTRY_URL *u) { + u->links--; + if(!u->links) { + debug(D_REGISTRY, "Registry: registry_url_unlink('%s'): No more links for this URL", u->url); + REGISTRY_URL *n = registry_url_index_del(u); + if(!n) { + error("INTERNAL ERROR: registry_url_unlink('%s'): cannot find url in index", u->url); + } + else { + if(n != u) { + error("INTERNAL ERROR: registry_url_unlink('%s'): deleted different url '%s'", u->url, n->url); + } + + registry.urls_memory -= sizeof(REGISTRY_URL) + n->len; // no need for +1, 1 is already in REGISTRY_URL + freez(n); + } + } + else + debug(D_REGISTRY, "Registry: registry_url_unlink('%s'): URL has %u links left", u->url, u->links); +} diff --git a/src/registry_url.h b/src/registry_url.h new file mode 100644 index 000000000..5ac52f5a5 --- /dev/null +++ b/src/registry_url.h @@ -0,0 +1,33 @@ +#ifndef NETDATA_REGISTRY_URL_H +#define NETDATA_REGISTRY_URL_H + +#include "registry_internals.h" + +// ---------------------------------------------------------------------------- +// URL structures +// Save memory by de-duplicating URLs +// so instead of storing URLs all over the place +// we store them here and we keep pointers elsewhere + +struct registry_url { + avl avl; + uint32_t hash; // the index hash + + uint32_t links; // the number of links to this URL - when none is left, we free it + + uint16_t len; // the length of the URL in bytes + char url[1]; // the URL - dynamically allocated to more size +}; +typedef struct registry_url REGISTRY_URL; + +// REGISTRY_URL INDEX +extern int registry_url_compare(void *a, void *b); +extern REGISTRY_URL *registry_url_index_del(REGISTRY_URL *u) WARNUNUSED; +extern REGISTRY_URL *registry_url_index_add(REGISTRY_URL *u) NEVERNULL WARNUNUSED; + +// REGISTRY_URL MANAGEMENT +extern REGISTRY_URL *registry_url_get(const char *url, size_t urllen) NEVERNULL; +extern void registry_url_link(REGISTRY_URL *u); +extern void registry_url_unlink(REGISTRY_URL *u); + +#endif //NETDATA_REGISTRY_URL_H @@ -56,7 +56,7 @@ RRDHOST localhost = { void rrdhost_init(char *hostname) { localhost.hostname = hostname; localhost.health_log.next_log_id = - localhost.health_log.next_alarm_id = time(NULL); + localhost.health_log.next_alarm_id = now_realtime_sec(); } void rrdhost_rwlock(RRDHOST *host) { @@ -118,7 +118,7 @@ RRDFAMILY *rrdfamily_create(const char *id) { RRDFAMILY *ret = rrdfamily_index_add(&localhost, rc); if(ret != rc) - fatal("INTERNAL ERROR: Expected to INSERT RRDFAMILY '%s' into index, but inserted '%s'.", rc->family, (ret)?ret->family:"NONE"); + fatal("RRDFAMILY: INTERNAL ERROR: Expected to INSERT RRDFAMILY '%s' into index, but inserted '%s'.", rc->family, (ret)?ret->family:"NONE"); } rc->use_count++; @@ -130,10 +130,10 @@ void rrdfamily_free(RRDFAMILY *rc) { if(!rc->use_count) { RRDFAMILY *ret = rrdfamily_index_del(&localhost, rc); if(ret != rc) - fatal("INTERNAL ERROR: Expected to DELETE RRDFAMILY '%s' from index, but deleted '%s'.", rc->family, (ret)?ret->family:"NONE"); + fatal("RRDFAMILY: INTERNAL ERROR: Expected to DELETE RRDFAMILY '%s' from index, but deleted '%s'.", rc->family, (ret)?ret->family:"NONE"); if(rc->variables_root_index.avl_tree.root != NULL) - fatal("INTERNAL ERROR: Variables index of RRDFAMILY '%s' that is freed, is not empty.", rc->family); + fatal("RRDFAMILY: INTERNAL ERROR: Variables index of RRDFAMILY '%s' that is freed, is not empty.", rc->family); freez((void *)rc->family); freez(rc); @@ -222,8 +222,8 @@ static int rrddim_compare(void* a, void* b) { else return strcmp(((RRDDIM *)a)->id, ((RRDDIM *)b)->id); } -#define rrddim_index_add(st, rd) avl_insert_lock(&((st)->dimensions_index), (avl *)(rd)) -#define rrddim_index_del(st,rd ) avl_remove_lock(&((st)->dimensions_index), (avl *)(rd)) +#define rrddim_index_add(st, rd) (RRDDIM *)avl_insert_lock(&((st)->dimensions_index), (avl *)(rd)) +#define rrddim_index_del(st,rd ) (RRDDIM *)avl_remove_lock(&((st)->dimensions_index), (avl *)(rd)) static RRDDIM *rrddim_index_find(RRDSET *st, const char *id, uint32_t hash) { RRDDIM tmp; @@ -353,22 +353,36 @@ char *rrdset_strncpyz_name(char *to, const char *from, size_t length) void rrdset_set_name(RRDSET *st, const char *name) { - debug(D_RRD_CALLS, "rrdset_set_name() old: %s, new: %s", st->name, name); + if(unlikely(st->name && !strcmp(st->name, name))) + return; - if(st->name) { - rrdset_index_del_name(&localhost, st); - rrdsetvar_rename_all(st); - } + debug(D_RRD_CALLS, "rrdset_set_name() old: %s, new: %s", st->name, name); char b[CONFIG_MAX_VALUE + 1]; char n[RRD_ID_LENGTH_MAX + 1]; snprintfz(n, RRD_ID_LENGTH_MAX, "%s.%s", st->type, name); rrdset_strncpyz_name(b, n, CONFIG_MAX_VALUE); - st->name = config_get(st->id, "name", b); - st->hash_name = simple_hash(st->name); - rrdset_index_add_name(&localhost, st); + if(st->name) { + rrdset_index_del_name(&localhost, st); + st->name = config_set_default(st->id, "name", b); + st->hash_name = simple_hash(st->name); + rrdsetvar_rename_all(st); + } + else { + st->name = config_get(st->id, "name", b); + st->hash_name = simple_hash(st->name); + } + + pthread_rwlock_wrlock(&st->rwlock); + RRDDIM *rd; + for(rd = st->dimensions; rd ;rd = rd->next) + rrddimvar_rename_all(rd); + pthread_rwlock_unlock(&st->rwlock); + + if(unlikely(rrdset_index_add_name(&localhost, st) != st)) + error("RRDSET: INTERNAL ERROR: attempted to index duplicate chart name '%s'", st->name); } // ---------------------------------------------------------------------------- @@ -425,7 +439,7 @@ void rrdset_reset(RRDSET *st) memset(rd->values, 0, rd->entries * sizeof(storage_number)); } } -static long align_entries_to_pagesize(long entries) { +static inline long align_entries_to_pagesize(long entries) { if(entries < 5) entries = 5; if(entries > RRD_HISTORY_ENTRIES_MAX) entries = RRD_HISTORY_ENTRIES_MAX; @@ -447,6 +461,11 @@ static long align_entries_to_pagesize(long entries) { #endif } +static inline void timeval_align(struct timeval *tv, int update_every) { + tv->tv_sec -= tv->tv_sec % update_every; + tv->tv_usec = 500000; +} + RRDSET *rrdset_create(const char *type, const char *id, const char *name, const char *family, const char *context, const char *title, const char *units, long priority, int update_every, int chart_type) { if(!type || !type[0]) { @@ -461,11 +480,10 @@ RRDSET *rrdset_create(const char *type, const char *id, const char *name, const char fullid[RRD_ID_LENGTH_MAX + 1]; char fullfilename[FILENAME_MAX + 1]; - RRDSET *st = NULL; snprintfz(fullid, RRD_ID_LENGTH_MAX, "%s.%s", type, id); - st = rrdset_find(fullid); + RRDSET *st = rrdset_find(fullid); if(st) { error("Cannot create rrd stats for '%s', it already exists.", fullid); return st; @@ -508,11 +526,15 @@ RRDSET *rrdset_create(const char *type, const char *id, const char *name, const error("File %s does not have the desired update frequency. Clearing it.", fullfilename); memset(st, 0, size); } - else if((time(NULL) - st->last_updated.tv_sec) > update_every * entries) { + else if((now_realtime_sec() - st->last_updated.tv_sec) > update_every * entries) { errno = 0; error("File %s is too old. Clearing it.", fullfilename); memset(st, 0, size); } + + // make sure the database is aligned + if(st->last_updated.tv_sec) + timeval_align(&st->last_updated, update_every); } if(st) { @@ -527,6 +549,11 @@ RRDSET *rrdset_create(const char *type, const char *id, const char *name, const st->mapped = rrd_memory_mode; st->variables = NULL; st->alarms = NULL; + memset(&st->rwlock, 0, sizeof(pthread_rwlock_t)); + memset(&st->avl, 0, sizeof(avl)); + memset(&st->avlname, 0, sizeof(avl)); + memset(&st->variables_root_index, 0, sizeof(avl_tree_lock)); + memset(&st->dimensions_index, 0, sizeof(avl_tree_lock)); } else { st = callocz(1, size); @@ -588,8 +615,10 @@ RRDSET *rrdset_create(const char *type, const char *id, const char *name, const { char varvalue[CONFIG_MAX_VALUE + 1]; + char varvalue2[CONFIG_MAX_VALUE + 1]; snprintfz(varvalue, CONFIG_MAX_VALUE, "%s (%s)", title?title:"", st->name); - st->title = config_get(st->id, "title", varvalue); + json_escape_string(varvalue2, varvalue, sizeof(varvalue2)); + st->title = config_get(st->id, "title", varvalue2); } st->rrdfamily = rrdfamily_create(st->family); @@ -606,7 +635,8 @@ RRDSET *rrdset_create(const char *type, const char *id, const char *name, const rrdsetvar_create(st, "update_every", RRDVAR_TYPE_INT, &st->update_every, 0); } - rrdset_index_add(&localhost, st); + if(unlikely(rrdset_index_add(&localhost, st) != st)) + error("RRDSET: INTERNAL ERROR: attempt to index duplicate chart '%s'", st->id); rrdsetcalc_link_matching(st); rrdcalctemplate_link_matching(st); @@ -618,21 +648,29 @@ RRDSET *rrdset_create(const char *type, const char *id, const char *name, const RRDDIM *rrddim_add(RRDSET *st, const char *id, const char *name, long multiplier, long divisor, int algorithm) { + RRDDIM *rd = rrddim_find(st, id); + if(rd) { + debug(D_RRD_CALLS, "Cannot create rrd dimension '%s/%s', it already exists.", st->id, name?name:"<NONAME>"); + return rd; + } + char filename[FILENAME_MAX + 1]; char fullfilename[FILENAME_MAX + 1]; char varname[CONFIG_MAX_NAME + 1]; - RRDDIM *rd = NULL; unsigned long size = sizeof(RRDDIM) + (st->entries * sizeof(storage_number)); debug(D_RRD_CALLS, "Adding dimension '%s/%s'.", st->id, id); rrdset_strncpyz_name(filename, id, FILENAME_MAX); snprintfz(fullfilename, FILENAME_MAX, "%s/%s.db", st->cache_dir, filename); - if(rrd_memory_mode != RRD_MEMORY_MODE_RAM) rd = (RRDDIM *)mymmap(fullfilename, size, ((rrd_memory_mode == RRD_MEMORY_MODE_MAP)?MAP_SHARED:MAP_PRIVATE), 1); + + if(rrd_memory_mode != RRD_MEMORY_MODE_RAM) + rd = (RRDDIM *)mymmap(fullfilename, size, ((rrd_memory_mode == RRD_MEMORY_MODE_MAP)?MAP_SHARED:MAP_PRIVATE), 1); + if(rd) { struct timeval now; - gettimeofday(&now, NULL); + now_realtime_timeval(&now); if(strcmp(rd->magic, RRDDIMENSION_MAGIC) != 0) { errno = 0; @@ -664,7 +702,7 @@ RRDDIM *rrddim_add(RRDSET *st, const char *id, const char *name, long multiplier error("File %s does not have the same refresh frequency. Clearing it.", fullfilename); memset(rd, 0, size); } - else if(usec_dt(&now, &rd->last_collected_time) > (rd->entries * rd->update_every * 1000000ULL)) { + else if(dt_usec(&now, &rd->last_collected_time) > (rd->entries * rd->update_every * USEC_PER_SEC)) { errno = 0; error("File %s is too old. Clearing it.", fullfilename); memset(rd, 0, size); @@ -685,6 +723,7 @@ RRDDIM *rrddim_add(RRDSET *st, const char *id, const char *name, long multiplier rd->variables = NULL; rd->next = NULL; rd->name = NULL; + memset(&rd->avl, 0, sizeof(avl)); } else { // if we didn't manage to get a mmap'd dimension, just create one @@ -748,18 +787,22 @@ RRDDIM *rrddim_add(RRDSET *st, const char *id, const char *name, long multiplier pthread_rwlock_unlock(&st->rwlock); - rrddim_index_add(st, rd); + if(unlikely(rrddim_index_add(st, rd) != rd)) + error("RRDDIM: INTERNAL ERROR: attempt to index duplicate dimension '%s' on chart '%s'", rd->id, st->id); return(rd); } void rrddim_set_name(RRDSET *st, RRDDIM *rd, const char *name) { - debug(D_RRD_CALLS, "rrddim_set_name() %s.%s", st->name, rd->name); + if(unlikely(rd->name && !strcmp(rd->name, name))) + return; + + debug(D_RRD_CALLS, "rrddim_set_name() from %s.%s to %s.%s", st->name, rd->name, st->name, name); char varname[CONFIG_MAX_NAME + 1]; snprintfz(varname, CONFIG_MAX_NAME, "dim %s name", rd->id); - config_set_default(st->id, varname, name); + rd->name = config_set_default(st->id, varname, name); rrddimvar_rename_all(rd); } @@ -784,7 +827,8 @@ void rrddim_free(RRDSET *st, RRDDIM *rd) while(rd->variables) rrddimvar_free(rd->variables); - rrddim_index_del(st, rd); + if(unlikely(rrddim_index_del(st, rd) != rd)) + error("RRDDIM: INTERNAL ERROR: attempt to remove from index dimension '%s' on chart '%s', removed a different dimension.", rd->id, st->id); // free(rd->annotations); if(rd->mapped == RRD_MEMORY_MODE_SAVE) { @@ -825,7 +869,10 @@ void rrdset_free_all(void) while(st->dimensions) rrddim_free(st, st->dimensions); - rrdset_index_del(&localhost, st); + if(unlikely(rrdset_index_del(&localhost, st) != st)) + error("RRDSET: INTERNAL ERROR: attempt to remove from index chart '%s', removed a different chart.", st->id); + + rrdset_index_del_name(&localhost, st); st->rrdfamily->use_count--; if(!st->rrdfamily->use_count) @@ -833,14 +880,7 @@ void rrdset_free_all(void) pthread_rwlock_unlock(&st->rwlock); - if(st->mapped == RRD_MEMORY_MODE_SAVE) { - debug(D_RRD_CALLS, "Saving stats '%s' to '%s'.", st->name, st->cache_filename); - savememory(st->cache_filename, st, st->memsize); - - debug(D_RRD_CALLS, "Unmapping stats '%s'.", st->name); - munmap(st, st->memsize); - } - else if(st->mapped == RRD_MEMORY_MODE_MAP) { + if(st->mapped == RRD_MEMORY_MODE_SAVE || st->mapped == RRD_MEMORY_MODE_MAP) { debug(D_RRD_CALLS, "Unmapping stats '%s'.", st->name); munmap(st, st->memsize); } @@ -862,9 +902,12 @@ void rrdset_save_all(void) { RRDSET *st; RRDDIM *rd; + // we get an write lock + // to ensure only one thread is saving the database rrdhost_rwlock(&localhost); + for(st = localhost.rrdset_root; st ; st = st->next) { - pthread_rwlock_wrlock(&st->rwlock); + pthread_rwlock_rdlock(&st->rwlock); if(st->mapped == RRD_MEMORY_MODE_SAVE) { debug(D_RRD_CALLS, "Saving stats '%s' to '%s'.", st->name, st->cache_filename); @@ -880,6 +923,7 @@ void rrdset_save_all(void) { pthread_rwlock_unlock(&st->rwlock); } + rrdhost_unlock(&localhost); } @@ -953,7 +997,7 @@ collected_number rrddim_set_by_pointer(RRDSET *st, RRDDIM *rd, collected_number { debug(D_RRD_CALLS, "rrddim_set_by_pointer() for chart %s, dimension %s, value " COLLECTED_NUMBER_FORMAT, st->name, rd->name, value); - gettimeofday(&rd->last_collected_time, NULL); + now_realtime_timeval(&rd->last_collected_time); rd->collected_value = value; rd->updated = 1; rd->counter++; @@ -974,38 +1018,61 @@ collected_number rrddim_set(RRDSET *st, const char *id, collected_number value) return rrddim_set_by_pointer(st, rd, value); } -void rrdset_next_usec(RRDSET *st, unsigned long long microseconds) +void rrdset_next_usec_unfiltered(RRDSET *st, usec_t microseconds) { - if(!microseconds) rrdset_next(st); - else { - debug(D_RRD_CALLS, "rrdset_next_usec() for chart %s with microseconds %llu", st->name, microseconds); - - if(unlikely(st->debug)) debug(D_RRD_STATS, "%s: NEXT: %llu microseconds", st->name, microseconds); - st->usec_since_last_update = microseconds; + if(unlikely(!st->last_collected_time.tv_sec || !microseconds)) { + // the first entry + microseconds = st->update_every * USEC_PER_SEC; } + st->usec_since_last_update = microseconds; } -void rrdset_next(RRDSET *st) +void rrdset_next_usec(RRDSET *st, usec_t microseconds) { - unsigned long long microseconds = 0; + struct timeval now; + now_realtime_timeval(&now); - if(likely(st->last_collected_time.tv_sec)) { - struct timeval now; - gettimeofday(&now, NULL); - microseconds = usec_dt(&now, &st->last_collected_time); + if(unlikely(!st->last_collected_time.tv_sec)) { + // the first entry + microseconds = st->update_every * USEC_PER_SEC; + } + else if(unlikely(!microseconds)) { + // no dt given by the plugin + microseconds = dt_usec(&now, &st->last_collected_time); } - // prevent infinite loop - else microseconds = st->update_every * 1000000ULL; + else { + // microseconds has the time since the last collection + usec_t now_usec = timeval_usec(&now); + usec_t last_usec = timeval_usec(&st->last_collected_time); + usec_t since_last_usec = dt_usec(&now, &st->last_collected_time); - rrdset_next_usec(st, microseconds); -} + // verify the microseconds given is good + if(unlikely(microseconds > since_last_usec)) { + debug(D_RRD_CALLS, "dt %llu usec given is too big - it leads %llu usec to the future, for chart '%s' (%s).", microseconds, microseconds - since_last_usec, st->name, st->id); -void rrdset_next_plugins(RRDSET *st) -{ - rrdset_next(st); +#ifdef NETDATA_INTERNAL_CHECKS + if(unlikely(last_usec + microseconds > now_usec + 1000)) + error("dt %llu usec given is too big - it leads %llu usec to the future, for chart '%s' (%s).", microseconds, microseconds - since_last_usec, st->name, st->id); +#endif + + microseconds = since_last_usec; + } + else if(unlikely(microseconds < since_last_usec * 0.8)) { + debug(D_RRD_CALLS, "dt %llu usec given is too small - expected %llu usec up to -20%%, for chart '%s' (%s).", microseconds, since_last_usec, st->name, st->id); + +#ifdef NETDATA_INTERNAL_CHECKS + error("dt %llu usec given is too small - expected %llu usec up to -20%%, for chart '%s' (%s).", microseconds, since_last_usec, st->name, st->id); +#endif + microseconds = since_last_usec; + } + } + debug(D_RRD_CALLS, "rrdset_next_usec() for chart %s with microseconds %llu", st->name, microseconds); + + if(unlikely(st->debug)) debug(D_RRD_STATS, "%s: NEXT: %llu microseconds", st->name, microseconds); + st->usec_since_last_update = microseconds; } -unsigned long long rrdset_done(RRDSET *st) +usec_t rrdset_done(RRDSET *st) { if(unlikely(netdata_exit)) return 0; @@ -1023,12 +1090,12 @@ unsigned long long rrdset_done(RRDSET *st) unsigned int stored_entries = 0; // the number of entries we have stored in the db, during this call to rrdset_done() - unsigned long long + usec_t last_collect_ut, // the timestamp in microseconds, of the last collected value now_collect_ut, // the timestamp in microseconds, of this collected value (this is NOW) last_stored_ut, // the timestamp in microseconds, of the last stored entry in the db next_store_ut, // the timestamp in microseconds, of the next entry to store in the db - update_every_ut = st->update_every * 1000000ULL; // st->update_every in microseconds + update_every_ut = st->update_every * USEC_PER_SEC; // st->update_every in microseconds if(unlikely(pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &pthreadoldcancelstate) != 0)) error("Cannot set pthread cancel state to DISABLE."); @@ -1044,7 +1111,7 @@ unsigned long long rrdset_done(RRDSET *st) // check if the chart has a long time to be updated if(unlikely(st->usec_since_last_update > st->entries * update_every_ut)) { - info("%s: took too long to be updated (%0.3Lf secs). Reseting it.", st->name, (long double)(st->usec_since_last_update / 1000000.0)); + info("%s: took too long to be updated (%0.3Lf secs). Resetting it.", st->name, (long double)(st->usec_since_last_update / 1000000.0)); rrdset_reset(st); st->usec_since_last_update = update_every_ut; first_entry = 1; @@ -1055,8 +1122,10 @@ unsigned long long rrdset_done(RRDSET *st) if(unlikely(!st->last_collected_time.tv_sec)) { // it is the first entry // set the last_collected_time to now - gettimeofday(&st->last_collected_time, NULL); - last_collect_ut = st->last_collected_time.tv_sec * 1000000ULL + st->last_collected_time.tv_usec - update_every_ut; + now_realtime_timeval(&st->last_collected_time); + timeval_align(&st->last_collected_time, st->update_every); + + last_collect_ut = st->last_collected_time.tv_sec * USEC_PER_SEC + st->last_collected_time.tv_usec - update_every_ut; // the first entry should not be stored store_this_entry = 0; @@ -1067,10 +1136,10 @@ unsigned long long rrdset_done(RRDSET *st) else { // it is not the first entry // calculate the proper last_collected_time, using usec_since_last_update - last_collect_ut = st->last_collected_time.tv_sec * 1000000ULL + st->last_collected_time.tv_usec; - unsigned long long ut = last_collect_ut + st->usec_since_last_update; - st->last_collected_time.tv_sec = (time_t) (ut / 1000000ULL); - st->last_collected_time.tv_usec = (suseconds_t) (ut % 1000000ULL); + last_collect_ut = st->last_collected_time.tv_sec * USEC_PER_SEC + st->last_collected_time.tv_usec; + usec_t ut = last_collect_ut + st->usec_since_last_update; + st->last_collected_time.tv_sec = (time_t) (ut / USEC_PER_SEC); + st->last_collected_time.tv_usec = (suseconds_t) (ut % USEC_PER_SEC); } // if this set has not been updated in the past @@ -1078,9 +1147,9 @@ unsigned long long rrdset_done(RRDSET *st) if(unlikely(!st->last_updated.tv_sec)) { // it has never been updated before // set a fake last_updated, in the past using usec_since_last_update - unsigned long long ut = st->last_collected_time.tv_sec * 1000000ULL + st->last_collected_time.tv_usec - st->usec_since_last_update; - st->last_updated.tv_sec = (time_t) (ut / 1000000ULL); - st->last_updated.tv_usec = (suseconds_t) (ut % 1000000ULL); + usec_t ut = st->last_collected_time.tv_sec * USEC_PER_SEC + st->last_collected_time.tv_usec - st->usec_since_last_update; + st->last_updated.tv_sec = (time_t) (ut / USEC_PER_SEC); + st->last_updated.tv_usec = (suseconds_t) (ut % USEC_PER_SEC); // the first entry should not be stored store_this_entry = 0; @@ -1090,17 +1159,18 @@ unsigned long long rrdset_done(RRDSET *st) } // check if we will re-write the entire data set - if(unlikely(usec_dt(&st->last_collected_time, &st->last_updated) > st->entries * update_every_ut)) { - info("%s: too old data (last updated at %ld.%ld, last collected at %ld.%ld). Reseting it. Will not store the next entry.", st->name, st->last_updated.tv_sec, st->last_updated.tv_usec, st->last_collected_time.tv_sec, st->last_collected_time.tv_usec); + if(unlikely(dt_usec(&st->last_collected_time, &st->last_updated) > st->entries * update_every_ut)) { + info("%s: too old data (last updated at %ld.%ld, last collected at %ld.%ld). Resetting it. Will not store the next entry.", st->name, st->last_updated.tv_sec, st->last_updated.tv_usec, st->last_collected_time.tv_sec, st->last_collected_time.tv_usec); rrdset_reset(st); st->usec_since_last_update = update_every_ut; - gettimeofday(&st->last_collected_time, NULL); + now_realtime_timeval(&st->last_collected_time); + timeval_align(&st->last_collected_time, st->update_every); - unsigned long long ut = st->last_collected_time.tv_sec * 1000000ULL + st->last_collected_time.tv_usec - st->usec_since_last_update; - st->last_updated.tv_sec = (time_t) (ut / 1000000ULL); - st->last_updated.tv_usec = (suseconds_t) (ut % 1000000ULL); + usec_t ut = st->last_collected_time.tv_sec * USEC_PER_SEC + st->last_collected_time.tv_usec - st->usec_since_last_update; + st->last_updated.tv_sec = (time_t) (ut / USEC_PER_SEC); + st->last_updated.tv_usec = (suseconds_t) (ut % USEC_PER_SEC); // the first entry should not be stored store_this_entry = 0; @@ -1111,9 +1181,9 @@ unsigned long long rrdset_done(RRDSET *st) // last_stored_ut = the last time we added a value to the storage // now_collect_ut = the time the current value has been collected // next_store_ut = the time of the next interpolation point - last_stored_ut = st->last_updated.tv_sec * 1000000ULL + st->last_updated.tv_usec; - now_collect_ut = st->last_collected_time.tv_sec * 1000000ULL + st->last_collected_time.tv_usec; - next_store_ut = (st->last_updated.tv_sec + st->update_every) * 1000000ULL; + last_stored_ut = st->last_updated.tv_sec * USEC_PER_SEC + st->last_updated.tv_usec; + now_collect_ut = st->last_collected_time.tv_sec * USEC_PER_SEC + st->last_collected_time.tv_usec; + next_store_ut = (st->last_updated.tv_sec + st->update_every) * USEC_PER_SEC; if(unlikely(st->debug)) { debug(D_RRD_STATS, "%s: last_collect_ut = %0.3Lf (last collection time)", st->name, (long double)last_collect_ut/1000000.0); @@ -1311,9 +1381,12 @@ unsigned long long rrdset_done(RRDSET *st) if(unlikely(now_collect_ut < next_store_ut)) { // this is collected in the same interpolation point if(unlikely(st->debug)) debug(D_RRD_STATS, "%s: THIS IS IN THE SAME INTERPOLATION POINT", st->name); +#ifdef NETDATA_INTERNAL_CHECKS + info("%s is collected in the same interpolation point: short by %llu microseconds", st->name, next_store_ut - now_collect_ut); +#endif } - unsigned long long first_ut = last_stored_ut; + usec_t first_ut = last_stored_ut; long long iterations = (now_collect_ut - last_stored_ut) / (update_every_ut); if((now_collect_ut % (update_every_ut)) == 0) iterations++; @@ -1327,7 +1400,7 @@ unsigned long long rrdset_done(RRDSET *st) debug(D_RRD_STATS, "%s: next_store_ut = %0.3Lf (next interpolation point)", st->name, (long double)next_store_ut/1000000.0); } - st->last_updated.tv_sec = (time_t) (next_store_ut / 1000000ULL); + st->last_updated.tv_sec = (time_t) (next_store_ut / USEC_PER_SEC); st->last_updated.tv_usec = 0; for( rd = st->dimensions ; likely(rd) ; rd = rd->next ) { @@ -244,7 +244,7 @@ struct rrdset { uint32_t hash_name; // a simple hash on the name - unsigned long long usec_since_last_update; // the time in microseconds since the last collection of data + usec_t usec_since_last_update; // the time in microseconds since the last collection of data struct timeval last_updated; // when this data set was last updated (updated every time the rrd_stats_done() function) struct timeval last_collected_time; // when did this data set last collected values @@ -355,11 +355,11 @@ extern RRDSET *rrdset_find(const char *id); extern RRDSET *rrdset_find_bytype(const char *type, const char *id); extern RRDSET *rrdset_find_byname(const char *name); -extern void rrdset_next_usec(RRDSET *st, unsigned long long microseconds); -extern void rrdset_next(RRDSET *st); -extern void rrdset_next_plugins(RRDSET *st); +extern void rrdset_next_usec_unfiltered(RRDSET *st, usec_t microseconds); +extern void rrdset_next_usec(RRDSET *st, usec_t microseconds); +#define rrdset_next(st) rrdset_next_usec(st, 0ULL) -extern unsigned long long rrdset_done(RRDSET *st); +extern usec_t rrdset_done(RRDSET *st); // get the total duration in seconds of the round robin database #define rrdset_duration(st) ((time_t)( (((st)->counter >= ((unsigned long)(st)->entries))?(unsigned long)(st)->entries:(st)->counter) * (st)->update_every )) diff --git a/src/rrd2json.c b/src/rrd2json.c index 474b5915d..067475006 100644 --- a/src/rrd2json.c +++ b/src/rrd2json.c @@ -1,6 +1,6 @@ #include "common.h" -void rrd_stats_api_v1_chart(RRDSET *st, BUFFER *wb) +void rrd_stats_api_v1_chart_with_data(RRDSET *st, BUFFER *wb, size_t *dimensions_count, size_t *memory_used) { pthread_rwlock_rdlock(&st->rwlock); @@ -41,7 +41,7 @@ void rrd_stats_api_v1_chart(RRDSET *st, BUFFER *wb) unsigned long memory = st->memsize; - int c = 0; + size_t dimensions = 0; RRDDIM *rd; for(rd = st->dimensions; rd ; rd = rd->next) { if(rd->flags & RRDDIM_FLAG_HIDDEN) continue; @@ -51,14 +51,17 @@ void rrd_stats_api_v1_chart(RRDSET *st, BUFFER *wb) buffer_sprintf(wb, "%s" "\t\t\t\t\"%s\": { \"name\": \"%s\" }" - , c?",\n":"" + , dimensions?",\n":"" , rd->id , rd->name ); - c++; + dimensions++; } + if(dimensions_count) *dimensions_count += dimensions; + if(memory_used) *memory_used += memory; + buffer_strcat(wb, "\n\t\t\t},\n\t\t\t\"green\": "); buffer_rrd_value(wb, st->green); buffer_strcat(wb, ",\n\t\t\t\"red\": "); @@ -71,9 +74,13 @@ void rrd_stats_api_v1_chart(RRDSET *st, BUFFER *wb) pthread_rwlock_unlock(&st->rwlock); } +void rrd_stats_api_v1_chart(RRDSET *st, BUFFER *wb) { + rrd_stats_api_v1_chart_with_data(st, wb, NULL, NULL); +} + void rrd_stats_api_v1_charts(BUFFER *wb) { - long c; + size_t c, dimensions = 0, memory = 0, alarms = 0; RRDSET *st; buffer_sprintf(wb, "{\n" @@ -93,19 +100,202 @@ void rrd_stats_api_v1_charts(BUFFER *wb) buffer_strcat(wb, "\n\t\t\""); buffer_strcat(wb, st->id); buffer_strcat(wb, "\": "); - rrd_stats_api_v1_chart(st, wb); + rrd_stats_api_v1_chart_with_data(st, wb, &dimensions, &memory); c++; } } + + RRDCALC *rc; + for(rc = localhost.alarms; rc ; rc = rc->next) { + if(rc->rrdset) + alarms++; + } pthread_rwlock_unlock(&localhost.rrdset_root_rwlock); - buffer_strcat(wb, "\n\t}\n}\n"); + buffer_sprintf(wb, "\n\t}" + ",\n\t\"charts_count\": %zu" + ",\n\t\"dimensions_count\": %zu" + ",\n\t\"alarms_count\": %zu" + ",\n\t\"rrd_memory_bytes\": %zu" + "\n}\n" + , c + , dimensions + , alarms + , memory + ); } +// ---------------------------------------------------------------------------- +// PROMETHEUS +// /api/v1/allmetrics?format=prometheus + +static inline size_t prometheus_name_copy(char *d, const char *s, size_t usable) { + size_t n; + + for(n = 0; *s && n < usable ; d++, s++, n++) { + register char c = *s; + + if(unlikely(!isalnum(c))) *d = '_'; + else *d = c; + } + *d = '\0'; + + return n; +} + +#define PROMETHEUS_ELEMENT_MAX 256 + +void rrd_stats_api_v1_charts_allmetrics_prometheus(BUFFER *wb) +{ + pthread_rwlock_rdlock(&localhost.rrdset_root_rwlock); + + char host[PROMETHEUS_ELEMENT_MAX + 1]; + prometheus_name_copy(host, config_get("global", "hostname", "localhost"), PROMETHEUS_ELEMENT_MAX); + + // for each chart + RRDSET *st; + for(st = localhost.rrdset_root; st ; st = st->next) { + char chart[PROMETHEUS_ELEMENT_MAX + 1]; + prometheus_name_copy(chart, st->id, PROMETHEUS_ELEMENT_MAX); + + buffer_strcat(wb, "\n"); + if(st->enabled && st->dimensions) { + pthread_rwlock_rdlock(&st->rwlock); + + // for each dimension + RRDDIM *rd; + for(rd = st->dimensions; rd ; rd = rd->next) { + if(rd->counter) { + char dimension[PROMETHEUS_ELEMENT_MAX + 1]; + prometheus_name_copy(dimension, rd->id, PROMETHEUS_ELEMENT_MAX); + + // buffer_sprintf(wb, "# HELP %s.%s %s\n", st->id, rd->id, st->units); + + switch(rd->algorithm) { + case RRDDIM_INCREMENTAL: + case RRDDIM_PCENT_OVER_DIFF_TOTAL: + buffer_sprintf(wb, "# TYPE %s_%s counter\n", chart, dimension); + break; + + default: + buffer_sprintf(wb, "# TYPE %s_%s gauge\n", chart, dimension); + break; + } + + // calculated_number n = (calculated_number)rd->last_collected_value * (calculated_number)(abs(rd->multiplier)) / (calculated_number)(abs(rd->divisor)); + // buffer_sprintf(wb, "%s.%s " CALCULATED_NUMBER_FORMAT " %llu\n", st->id, rd->id, n, + // (unsigned long long)((rd->last_collected_time.tv_sec * 1000) + (rd->last_collected_time.tv_usec / 1000))); + + buffer_sprintf(wb, "%s_%s{instance=\"%s\"} " COLLECTED_NUMBER_FORMAT " %llu\n", + chart, dimension, host, rd->last_collected_value, + (unsigned long long)((rd->last_collected_time.tv_sec * 1000) + (rd->last_collected_time.tv_usec / 1000))); + + } + } + + pthread_rwlock_unlock(&st->rwlock); + } + } + + pthread_rwlock_unlock(&localhost.rrdset_root_rwlock); +} + +// ---------------------------------------------------------------------------- +// BASH +// /api/v1/allmetrics?format=bash + +static inline size_t shell_name_copy(char *d, const char *s, size_t usable) { + size_t n; + + for(n = 0; *s && n < usable ; d++, s++, n++) { + register char c = *s; + + if(unlikely(!isalnum(c))) *d = '_'; + else *d = (char)toupper(c); + } + *d = '\0'; + + return n; +} + +#define SHELL_ELEMENT_MAX 100 + +void rrd_stats_api_v1_charts_allmetrics_shell(BUFFER *wb) +{ + pthread_rwlock_rdlock(&localhost.rrdset_root_rwlock); + + char host[SHELL_ELEMENT_MAX + 1]; + shell_name_copy(host, config_get("global", "hostname", "localhost"), SHELL_ELEMENT_MAX); + + // for each chart + RRDSET *st; + for(st = localhost.rrdset_root; st ; st = st->next) { + calculated_number total = 0.0; + char chart[SHELL_ELEMENT_MAX + 1]; + shell_name_copy(chart, st->id, SHELL_ELEMENT_MAX); + + buffer_sprintf(wb, "\n# chart: %s (name: %s)\n", st->id, st->name); + if(st->enabled && st->dimensions) { + pthread_rwlock_rdlock(&st->rwlock); + + // for each dimension + RRDDIM *rd; + for(rd = st->dimensions; rd ; rd = rd->next) { + if(rd->counter) { + char dimension[SHELL_ELEMENT_MAX + 1]; + shell_name_copy(dimension, rd->id, SHELL_ELEMENT_MAX); + + calculated_number n = rd->last_stored_value; + + if(isnan(n) || isinf(n)) + buffer_sprintf(wb, "NETDATA_%s_%s=\"\" # %s\n", chart, dimension, st->units); + else { + if(rd->multiplier < 0 || rd->divisor < 0) n = -n; + n = roundl(n); + if(!(rd->flags & RRDDIM_FLAG_HIDDEN)) total += n; + buffer_sprintf(wb, "NETDATA_%s_%s=\"%0.0Lf\" # %s\n", chart, dimension, n, st->units); + } + } + } + + total = roundl(total); + buffer_sprintf(wb, "NETDATA_%s_VISIBLETOTAL=\"%0.0Lf\" # %s\n", chart, total, st->units); + pthread_rwlock_unlock(&st->rwlock); + } + } + + buffer_strcat(wb, "\n# NETDATA ALARMS RUNNING\n"); + + RRDCALC *rc; + for(rc = localhost.alarms; rc ;rc = rc->next) { + if(!rc->rrdset) continue; + + char chart[SHELL_ELEMENT_MAX + 1]; + shell_name_copy(chart, rc->rrdset->id, SHELL_ELEMENT_MAX); + + char alarm[SHELL_ELEMENT_MAX + 1]; + shell_name_copy(alarm, rc->name, SHELL_ELEMENT_MAX); + + calculated_number n = rc->value; + + if(isnan(n) || isinf(n)) + buffer_sprintf(wb, "NETDATA_ALARM_%s_%s_VALUE=\"\" # %s\n", chart, alarm, rc->units); + else { + n = roundl(n); + buffer_sprintf(wb, "NETDATA_ALARM_%s_%s_VALUE=\"%0.0Lf\" # %s\n", chart, alarm, n, rc->units); + } + + buffer_sprintf(wb, "NETDATA_ALARM_%s_%s_STATUS=\"%s\"\n", chart, alarm, rrdcalc_status2string(rc->status)); + } + + pthread_rwlock_unlock(&localhost.rrdset_root_rwlock); +} + +// ---------------------------------------------------------------------------- unsigned long rrd_stats_one_json(RRDSET *st, char *options, BUFFER *wb) { - time_t now = time(NULL); + time_t now = now_realtime_sec(); pthread_rwlock_rdlock(&st->rwlock); @@ -434,17 +624,21 @@ void rrdr_buffer_print_format(BUFFER *wb, uint32_t format) uint32_t rrdr_check_options(RRDR *r, uint32_t options, const char *dims) { + (void)dims; + if(options & RRDR_OPTION_NONZERO) { long i; - if(dims && *dims) { + // commented due to #1514 + + //if(dims && *dims) { // the caller wants specific dimensions // disable NONZERO option // to make sure we don't accidentally prevent // the specific dimensions from being returned - i = 0; - } - else { + // i = 0; + //} + //else { // find how many dimensions are not zero long c; RRDDIM *rd; @@ -453,7 +647,7 @@ uint32_t rrdr_check_options(RRDR *r, uint32_t options, const char *dims) if(unlikely(!(r->od[c] & RRDR_NONZERO))) continue; i++; } - } + //} // if with nonzero we get i = 0 (no dimensions will be returned) // disable nonzero to show all dimensions @@ -1158,7 +1352,7 @@ inline static void rrdr_free(RRDR *r) freez(r); } -inline void rrdr_done(RRDR *r) +static inline void rrdr_done(RRDR *r) { r->rows = r->c + 1; r->c = 0; @@ -1209,19 +1403,32 @@ RRDR *rrd2rrdr(RRDSET *st, long points, long long after, long long before, int g time_t last_entry_t = rrdset_last_entry_t(st); if(before == 0 && after == 0) { + // dump the all the data before = last_entry_t; after = first_entry_t; absolute_period_requested = 0; } - // allow relative for before and after (smaller than 3 years) - if(((before < 0)?-before:before) <= (3 * 365 * 86400)) { - before = last_entry_t + before; + // allow relative for before (smaller than API_RELATIVE_TIME_MAX) + if(((before < 0)?-before:before) <= API_RELATIVE_TIME_MAX) { + if(abs(before) % st->update_every) { + // make sure it is multiple of st->update_every + if(before < 0) before = before - st->update_every - before % st->update_every; + else before = before + st->update_every - before % st->update_every; + } + if(before > 0) before = first_entry_t + before; + else before = last_entry_t + before; absolute_period_requested = 0; } - if(((after < 0)?-after:after) <= (3 * 365 * 86400)) { + // allow relative for after (smaller than API_RELATIVE_TIME_MAX) + if(((after < 0)?-after:after) <= API_RELATIVE_TIME_MAX) { if(after == 0) after = -st->update_every; + if(abs(after) % st->update_every) { + // make sure it is multiple of st->update_every + if(after < 0) after = after - st->update_every - after % st->update_every; + else after = after + st->update_every - after % st->update_every; + } after = before + after; absolute_period_requested = 0; } diff --git a/src/rrd2json.h b/src/rrd2json.h index e3e5cb690..7b1401970 100644 --- a/src/rrd2json.h +++ b/src/rrd2json.h @@ -2,7 +2,8 @@ #define NETDATA_RRD2JSON_H 1 #define HOSTNAME_MAX 1024 -extern char *hostname; + +#define API_RELATIVE_TIME_MAX (3 * 365 * 86400) // type of JSON generations #define DATASOURCE_INVALID -1 @@ -30,6 +31,12 @@ extern char *hostname; #define DATASOURCE_FORMAT_SSV_COMMA "ssvcomma" #define DATASOURCE_FORMAT_CSV_JSON_ARRAY "csvjsonarray" +#define ALLMETRICS_FORMAT_SHELL "shell" +#define ALLMETRICS_FORMAT_PROMETHEUS "prometheus" + +#define ALLMETRICS_SHELL 1 +#define ALLMETRICS_PROMETHEUS 2 + #define GROUP_UNDEFINED 0 #define GROUP_AVERAGE 1 #define GROUP_MIN 2 @@ -54,6 +61,9 @@ extern char *hostname; extern void rrd_stats_api_v1_chart(RRDSET *st, BUFFER *wb); extern void rrd_stats_api_v1_charts(BUFFER *wb); +extern void rrd_stats_api_v1_charts_allmetrics_shell(BUFFER *wb); +extern void rrd_stats_api_v1_charts_allmetrics_prometheus(BUFFER *wb); + extern unsigned long rrd_stats_one_json(RRDSET *st, char *options, BUFFER *wb); extern void rrd_stats_graph_json(RRDSET *st, char *options, BUFFER *wb); diff --git a/src/simple_pattern.c b/src/simple_pattern.c new file mode 100644 index 000000000..7e4424297 --- /dev/null +++ b/src/simple_pattern.c @@ -0,0 +1,197 @@ +#include "common.h" + +struct simple_pattern { + const char *match; + size_t len; + + SIMPLE_PREFIX_MODE mode; + char negative; + + struct simple_pattern *child; + + struct simple_pattern *next; +}; + +static inline struct simple_pattern *parse_pattern(const char *str, SIMPLE_PREFIX_MODE default_mode) { + SIMPLE_PREFIX_MODE mode; + struct simple_pattern *child = NULL; + + char *buf = strdupz(str); + char *s = buf, *c = buf; + + // skip asterisks in front + while(*c == '*') c++; + + // find the next asterisk + while(*c && *c != '*') c++; + + // do we have an asterisk in the middle? + if(*c == '*' && c[1] != '\0') { + // yes, we have + child = parse_pattern(c, default_mode); + c[1] = '\0'; + } + + // check what this one matches + + size_t len = strlen(s); + if(len >= 2 && *s == '*' && s[len - 1] == '*') { + s[len - 1] = '\0'; + s++; + mode = SIMPLE_PATTERN_SUBSTRING; + } + else if(len >= 1 && *s == '*') { + s++; + mode = SIMPLE_PATTERN_SUFFIX; + } + else if(len >= 1 && s[len - 1] == '*') { + s[len - 1] = '\0'; + mode = SIMPLE_PATTERN_PREFIX; + } + else + mode = default_mode; + + // allocate the structure + struct simple_pattern *m = callocz(1, sizeof(struct simple_pattern)); + if(*s) { + m->match = strdupz(s); + m->len = strlen(m->match); + m->mode = mode; + } + else { + m->mode = SIMPLE_PATTERN_SUBSTRING; + } + + m->child = child; + + freez(buf); + + return m; +} + +SIMPLE_PATTERN *simple_pattern_create(const char *list, SIMPLE_PREFIX_MODE default_mode) { + struct simple_pattern *root = NULL, *last = NULL; + + if(unlikely(!list || !*list)) return root; + + char *buf = strdupz(list); + if(buf && *buf) { + char *s = buf; + + while(s && *s) { + char negative = 0; + + // skip all spaces + while(isspace(*s)) s++; + + if(*s == '!') { + negative = 1; + s++; + } + + // empty string + if(unlikely(!*s)) break; + + // find the next space + char *c = s; + while(*c && !isspace(*c)) c++; + + // find the next word + char *n; + if(likely(*c)) n = c + 1; + else n = NULL; + + // terminate our string + *c = '\0'; + + struct simple_pattern *m = parse_pattern(s, default_mode); + m->negative = negative; + + if(likely(n)) *c = ' '; + + // link it at the end + if(unlikely(!root)) + root = last = m; + else { + last->next = m; + last = m; + } + + // prepare for next loop + s = n; + } + } + + freez(buf); + return (SIMPLE_PATTERN *)root; +} + +static inline int match_pattern(struct simple_pattern *m, const char *str, size_t len) { + char *s; + + if(m->len <= len) { + switch(m->mode) { + case SIMPLE_PATTERN_SUBSTRING: + if(!m->len) return 1; + if((s = strstr(str, m->match))) { + if(!m->child) return 1; + return match_pattern(m->child, &s[m->len], len - (s - str) - m->len); + } + break; + + case SIMPLE_PATTERN_PREFIX: + if(unlikely(strncmp(str, m->match, m->len) == 0)) { + if(!m->child) return 1; + return match_pattern(m->child, &str[m->len], len - m->len); + } + break; + + case SIMPLE_PATTERN_SUFFIX: + if(unlikely(strcmp(&str[len - m->len], m->match) == 0)) { + if(!m->child) return 1; + return 0; + } + break; + + case SIMPLE_PATTERN_EXACT: + default: + if(unlikely(strcmp(str, m->match) == 0)) { + if(!m->child) return 1; + return 0; + } + break; + } + } + + return 0; +} + +int simple_pattern_matches(SIMPLE_PATTERN *list, const char *str) { + struct simple_pattern *m, *root = (struct simple_pattern *)list; + + if(unlikely(!root)) return 0; + + size_t len = strlen(str); + for(m = root; m ; m = m->next) + if(match_pattern(m, str, len)) { + if(m->negative) return 0; + return 1; + } + + return 0; +} + +static inline void free_pattern(struct simple_pattern *m) { + if(!m) return; + + if(m->next) free_pattern(m->next); + if(m->child) free_pattern(m->child); + freez((void *)m->match); + freez(m); +} + +void simple_pattern_free(SIMPLE_PATTERN *list) { + if(!list) return; + + free_pattern(((struct simple_pattern *)list)->next); +} diff --git a/src/simple_pattern.h b/src/simple_pattern.h new file mode 100644 index 000000000..3768c5089 --- /dev/null +++ b/src/simple_pattern.h @@ -0,0 +1,25 @@ +#ifndef NETDATA_SIMPLE_PATTERN_H +#define NETDATA_SIMPLE_PATTERN_H + +typedef enum { + SIMPLE_PATTERN_EXACT, + SIMPLE_PATTERN_PREFIX, + SIMPLE_PATTERN_SUFFIX, + SIMPLE_PATTERN_SUBSTRING +} SIMPLE_PREFIX_MODE; + +typedef void SIMPLE_PATTERN; + +// create a simple_pattern from the string given +// default_mode is used in cases where EXACT matches, without an asterisk, +// should be considered PREFIX matches. +extern SIMPLE_PATTERN *simple_pattern_create(const char *list, SIMPLE_PREFIX_MODE default_mode); + +// test if string str is matched from the pattern +extern int simple_pattern_matches(SIMPLE_PATTERN *list, const char *str); + +// free a simple_pattern that was created with simple_pattern_create() +// list can be NULL, in which case, this does nothing. +extern void simple_pattern_free(SIMPLE_PATTERN *list); + +#endif //NETDATA_SIMPLE_PATTERN_H diff --git a/src/socket.c b/src/socket.c new file mode 100644 index 000000000..643811e44 --- /dev/null +++ b/src/socket.c @@ -0,0 +1,179 @@ +#include "common.h" + +// connect_to() +// +// definition format: +// +// [PROTOCOL:]IP[%INTERFACE][:PORT] +// +// PROTOCOL = tcp or udp +// IP = IPv4 or IPv6 IP or hostname, optionally enclosed in [] (required for IPv6) +// INTERFACE = for IPv6 only, the network interface to use +// PORT = port number or service name + +int connect_to(const char *definition, int default_port, struct timeval *timeout) { + struct addrinfo hints; + struct addrinfo *ai_head = NULL, *ai = NULL; + + char buffer[strlen(definition) + 1]; + strcpy(buffer, definition); + + char default_service[10 + 1]; + snprintfz(default_service, 10, "%d", default_port); + + char *host = buffer, *service = default_service, *interface = ""; + int protocol = IPPROTO_TCP, socktype = SOCK_STREAM; + uint32_t scope_id = 0; + + if(strncmp(host, "tcp:", 4) == 0) { + host += 4; + protocol = IPPROTO_TCP; + socktype = SOCK_STREAM; + } + else if(strncmp(host, "udp:", 4) == 0) { + host += 4; + protocol = IPPROTO_UDP; + socktype = SOCK_DGRAM; + } + + char *e = host; + if(*e == '[') { + e = ++host; + while(*e && *e != ']') e++; + if(*e == ']') { + *e = '\0'; + e++; + } + } + else { + while(*e && *e != ':' && *e != '%') e++; + } + + if(*e == '%') { + *e = '\0'; + e++; + interface = e; + while(*e && *e != ':') e++; + } + + if(*e == ':') { + *e = '\0'; + e++; + service = e; + } + + debug(D_CONNECT_TO, "Attempting connection to host = '%s', service = '%s', interface = '%s', protocol = %d (tcp = %d, udp = %d)", host, service, interface, protocol, IPPROTO_TCP, IPPROTO_UDP); + + if(!*host) { + error("Definition '%s' does not specify a host.", definition); + return -1; + } + + if(*interface) { + scope_id = if_nametoindex(interface); + if(!scope_id) + error("Cannot find a network interface named '%s'. Continuing with limiting the network interface", interface); + } + + if(!*service) + service = default_service; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; /* Allow IPv4 or IPv6 */ + hints.ai_socktype = socktype; + hints.ai_protocol = protocol; + + int ai_err = getaddrinfo(host, service, &hints, &ai_head); + if (ai_err != 0) { + error("Cannot resolve host '%s', port '%s': %s", host, service, gai_strerror(ai_err)); + return -1; + } + + int fd = -1; + for (ai = ai_head; ai != NULL && fd == -1; ai = ai->ai_next) { + + if (ai->ai_family == PF_INET6) { + struct sockaddr_in6 *pSadrIn6 = (struct sockaddr_in6 *) ai->ai_addr; + if(pSadrIn6->sin6_scope_id == 0) { + pSadrIn6->sin6_scope_id = scope_id; + } + } + + char hostBfr[NI_MAXHOST + 1]; + char servBfr[NI_MAXSERV + 1]; + + getnameinfo(ai->ai_addr, + ai->ai_addrlen, + hostBfr, + sizeof(hostBfr), + servBfr, + sizeof(servBfr), + NI_NUMERICHOST | NI_NUMERICSERV); + + debug(D_CONNECT_TO, "Address info: host = '%s', service = '%s', ai_flags = 0x%02X, ai_family = %d (PF_INET = %d, PF_INET6 = %d), ai_socktype = %d (SOCK_STREAM = %d, SOCK_DGRAM = %d), ai_protocol = %d (IPPROTO_TCP = %d, IPPROTO_UDP = %d), ai_addrlen = %lu (sockaddr_in = %lu, sockaddr_in6 = %lu)", + hostBfr, + servBfr, + (unsigned int)ai->ai_flags, + ai->ai_family, + PF_INET, + PF_INET6, + ai->ai_socktype, + SOCK_STREAM, + SOCK_DGRAM, + ai->ai_protocol, + IPPROTO_TCP, + IPPROTO_UDP, + (unsigned long)ai->ai_addrlen, + (unsigned long)sizeof(struct sockaddr_in), + (unsigned long)sizeof(struct sockaddr_in6)); + + switch (ai->ai_addr->sa_family) { + case PF_INET: { + struct sockaddr_in *pSadrIn = (struct sockaddr_in *)ai->ai_addr; + debug(D_CONNECT_TO, "ai_addr = sin_family: %d (AF_INET = %d, AF_INET6 = %d), sin_addr: '%s', sin_port: '%s'", + pSadrIn->sin_family, + AF_INET, + AF_INET6, + hostBfr, + servBfr); + break; + } + + case PF_INET6: { + struct sockaddr_in6 *pSadrIn6 = (struct sockaddr_in6 *) ai->ai_addr; + debug(D_CONNECT_TO,"ai_addr = sin6_family: %d (AF_INET = %d, AF_INET6 = %d), sin6_addr: '%s', sin6_port: '%s', sin6_flowinfo: %u, sin6_scope_id: %u", + pSadrIn6->sin6_family, + AF_INET, + AF_INET6, + hostBfr, + servBfr, + pSadrIn6->sin6_flowinfo, + pSadrIn6->sin6_scope_id); + break; + } + + default: { + debug(D_CONNECT_TO, "Unknown protocol family %d.", ai->ai_family); + continue; + } + } + + fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); + if(fd != -1) { + if(setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, (char *)timeout, sizeof(struct timeval)) < 0) + error("Failed to set timeout on the socket to ip '%s' port '%s'", hostBfr, servBfr); + + if(connect(fd, ai->ai_addr, ai->ai_addrlen) < 0) { + error("Failed to connect to '%s', port '%s'", hostBfr, servBfr); + close(fd); + fd = -1; + } + + debug(D_CONNECT_TO, "Connected to '%s' on port '%s'.", hostBfr, servBfr); + } + } + + freeaddrinfo(ai_head); + + return fd; +} diff --git a/src/socket.h b/src/socket.h new file mode 100644 index 000000000..791c0ce54 --- /dev/null +++ b/src/socket.h @@ -0,0 +1,10 @@ +// +// Created by costa on 24/12/2016. +// + +#ifndef NETDATA_SOCKET_H +#define NETDATA_SOCKET_H + +extern int connect_to(const char *definition, int default_port, struct timeval *timeout); + +#endif //NETDATA_SOCKET_H diff --git a/src/sys_devices_system_edac_mc.c b/src/sys_devices_system_edac_mc.c new file mode 100644 index 000000000..c764615f1 --- /dev/null +++ b/src/sys_devices_system_edac_mc.c @@ -0,0 +1,183 @@ +#include "common.h" + +struct mc { + char *name; + char ce_updated; + char ue_updated; + + char *ce_count_filename; + char *ue_count_filename; + + procfile *ce_ff; + procfile *ue_ff; + + collected_number ce_count; + collected_number ue_count; + + RRDDIM *ce_rd; + RRDDIM *ue_rd; + + struct mc *next; +}; +static struct mc *mc_root = NULL; + +static void find_all_mc() { + char name[FILENAME_MAX + 1]; + snprintfz(name, FILENAME_MAX, "%s%s", global_host_prefix, "/sys/devices/system/edac/mc"); + char *dirname = config_get("plugin:proc:/sys/devices/system/edac/mc", "directory to monitor", name); + + DIR *dir = opendir(dirname); + if(!dir) { + error("Cannot read ECC memory errors directory '%s'", dirname); + return; + } + + struct dirent *de = NULL; + while((de = readdir(dir))) { + if(de->d_type == DT_DIR && de->d_name[0] == 'm' && de->d_name[1] == 'c' && isdigit(de->d_name[2])) { + struct mc *m = callocz(1, sizeof(struct mc)); + m->name = strdupz(de->d_name); + + struct stat st; + + snprintfz(name, FILENAME_MAX, "%s/%s/ce_count", dirname, de->d_name); + if(stat(name, &st) != -1) + m->ce_count_filename = strdupz(name); + + snprintfz(name, FILENAME_MAX, "%s/%s/ue_count", dirname, de->d_name); + if(stat(name, &st) != -1) + m->ue_count_filename = strdupz(name); + + if(!m->ce_count_filename && !m->ue_count_filename) { + freez(m->name); + freez(m); + } + else { + m->next = mc_root; + mc_root = m; + } + } + } + + closedir(dir); +} + +int do_proc_sys_devices_system_edac_mc(int update_every, usec_t dt) { + (void)dt; + + if(unlikely(mc_root == NULL)) { + find_all_mc(); + if(unlikely(mc_root == NULL)) + return 1; + } + + static int do_ce = -1, do_ue = -1; + calculated_number ce_sum = 0, ue_sum = 0; + struct mc *m; + + if(unlikely(do_ce == -1)) { + do_ce = config_get_boolean_ondemand("plugin:proc:/sys/devices/system/edac/mc", "enable ECC memory correctable errors", CONFIG_ONDEMAND_ONDEMAND); + do_ue = config_get_boolean_ondemand("plugin:proc:/sys/devices/system/edac/mc", "enable ECC memory uncorrectable errors", CONFIG_ONDEMAND_ONDEMAND); + } + + if(do_ce != CONFIG_ONDEMAND_NO) { + for(m = mc_root; m; m = m->next) { + if(m->ce_count_filename) { + m->ce_updated = 0; + + if(unlikely(!m->ce_ff)) { + m->ce_ff = procfile_open(m->ce_count_filename, " \t", PROCFILE_FLAG_DEFAULT); + if(unlikely(!m->ce_ff)) + continue; + } + + m->ce_ff = procfile_readall(m->ce_ff); + if(unlikely(!m->ce_ff || procfile_lines(m->ce_ff) < 1 || procfile_linewords(m->ce_ff, 0) < 1)) + continue; + + m->ce_count = str2ull(procfile_lineword(m->ce_ff, 0, 0)); + ce_sum += m->ce_count; + m->ce_updated = 1; + } + } + } + + if(do_ue != CONFIG_ONDEMAND_NO) { + for(m = mc_root; m; m = m->next) { + if(m->ue_count_filename) { + m->ue_updated = 0; + + if(unlikely(!m->ue_ff)) { + m->ue_ff = procfile_open(m->ue_count_filename, " \t", PROCFILE_FLAG_DEFAULT); + if(unlikely(!m->ue_ff)) + continue; + } + + m->ue_ff = procfile_readall(m->ue_ff); + if(unlikely(!m->ue_ff || procfile_lines(m->ue_ff) < 1 || procfile_linewords(m->ue_ff, 0) < 1)) + continue; + + m->ue_count = str2ull(procfile_lineword(m->ue_ff, 0, 0)); + ue_sum += m->ue_count; + m->ue_updated = 1; + } + } + } + + // -------------------------------------------------------------------- + + if(do_ce == CONFIG_ONDEMAND_YES || (do_ce == CONFIG_ONDEMAND_ONDEMAND && ce_sum > 0)) { + do_ce = CONFIG_ONDEMAND_YES; + + static RRDSET *ce_st = NULL; + + if(unlikely(!ce_st)) { + ce_st = rrdset_find("mem.ecc_ce"); + if(unlikely(!ce_st)) + ce_st = rrdset_create("mem", "ecc_ce", NULL, "ecc", NULL, "ECC Memory Correctable Errors", "errors", + 6600, update_every, RRDSET_TYPE_LINE); + + for(m = mc_root; m; m = m->next) + if(m->ce_count_filename) + m->ce_rd = rrddim_add(ce_st, m->name, NULL, 1, 1, RRDDIM_INCREMENTAL); + } + else + rrdset_next(ce_st); + + for(m = mc_root; m; m = m->next) + if(m->ce_count_filename && m->ce_updated) + rrddim_set_by_pointer(ce_st, m->ce_rd, m->ce_count); + + rrdset_done(ce_st); + } + + // -------------------------------------------------------------------- + + if(do_ue == CONFIG_ONDEMAND_YES || (do_ue == CONFIG_ONDEMAND_ONDEMAND && ue_sum > 0)) { + do_ue = CONFIG_ONDEMAND_YES; + + static RRDSET *ue_st = NULL; + + if(unlikely(!ue_st)) { + ue_st = rrdset_find("mem.ecc_ue"); + + if(unlikely(!ue_st)) + ue_st = rrdset_create("mem", "ecc_ue", NULL, "ecc", NULL, "ECC Memory Uncorrectable Errors", "errors", + 6610, update_every, RRDSET_TYPE_LINE); + + for(m = mc_root; m; m = m->next) + if(m->ue_count_filename) + m->ue_rd = rrddim_add(ue_st, m->name, NULL, 1, 1, RRDDIM_INCREMENTAL); + } + else + rrdset_next(ue_st); + + for(m = mc_root; m; m = m->next) + if(m->ue_count_filename && m->ue_updated) + rrddim_set_by_pointer(ue_st, m->ue_rd, m->ue_count); + + rrdset_done(ue_st); + } + + return 0; +} diff --git a/src/sys_devices_system_node.c b/src/sys_devices_system_node.c new file mode 100644 index 000000000..18c3fcd3a --- /dev/null +++ b/src/sys_devices_system_node.c @@ -0,0 +1,127 @@ +#include "common.h" + +struct node { + char *name; + char *numastat_filename; + procfile *numastat_ff; + RRDSET *numastat_st; + struct node *next; +}; +static struct node *numa_root = NULL; + +static int find_all_nodes() { + int numa_node_count = 0; + char name[FILENAME_MAX + 1]; + snprintfz(name, FILENAME_MAX, "%s%s", global_host_prefix, "/sys/devices/system/node"); + char *dirname = config_get("plugin:proc:/sys/devices/system/node", "directory to monitor", name); + + DIR *dir = opendir(dirname); + if(!dir) { + error("Cannot read NUMA node directory '%s'", dirname); + return 0; + } + + struct dirent *de = NULL; + while((de = readdir(dir))) { + if(de->d_type != DT_DIR) + continue; + + if(strncmp(de->d_name, "node", 4) != 0) + continue; + + if(!isdigit(de->d_name[4])) + continue; + + numa_node_count++; + + struct node *m = callocz(1, sizeof(struct node)); + m->name = strdupz(de->d_name); + + struct stat st; + + snprintfz(name, FILENAME_MAX, "%s/%s/numastat", dirname, de->d_name); + if(stat(name, &st) == -1) { + freez(m->name); + freez(m); + continue; + } + + m->numastat_filename = strdupz(name); + + m->next = numa_root; + numa_root = m; + } + + closedir(dir); + + return numa_node_count; +} + +int do_proc_sys_devices_system_node(int update_every, usec_t dt) { + (void)dt; + + static int numa_node_count = 0; + + if(unlikely(numa_root == NULL)) { + numa_node_count = find_all_nodes(update_every); + if(unlikely(numa_root == NULL)) + return 1; + } + + static int do_numastat = -1; + struct node *m; + + if(unlikely(do_numastat == -1)) { + do_numastat = config_get_boolean_ondemand("plugin:proc:/sys/devices/system/node", "enable per-node numa metrics", CONFIG_ONDEMAND_ONDEMAND); + } + + if(do_numastat == CONFIG_ONDEMAND_YES || (do_numastat == CONFIG_ONDEMAND_ONDEMAND && numa_node_count >= 2)) { + for(m = numa_root; m; m = m->next) { + if(m->numastat_filename) { + if(unlikely(!m->numastat_ff)) { + m->numastat_ff = procfile_open(m->numastat_filename, " ", PROCFILE_FLAG_DEFAULT); + if(unlikely(!m->numastat_ff)) + continue; + } + + m->numastat_ff = procfile_readall(m->numastat_ff); + if(unlikely(!m->numastat_ff || procfile_lines(m->numastat_ff) < 1 || procfile_linewords(m->numastat_ff, 0) < 1)) + continue; + + procfile *ff = m->numastat_ff; + + RRDSET *st = m->numastat_st; + if(unlikely(!st)) { + st = rrdset_create("mem", m->name, NULL, "numa", NULL, "NUMA events", "events/s", 1000, update_every, RRDSET_TYPE_LINE); + st->isdetail = 1; + + rrddim_add(st, "local_node", "local", 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "numa_foreign", "foreign", 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "interleave_hit", "interleave", 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "other_node", "other", 1, 1, RRDDIM_INCREMENTAL); + + m->numastat_st = st; + } + else rrdset_next(st); + + uint32_t lines = procfile_lines(ff), l; + for(l = 0; l < lines; l++) { + uint32_t words = procfile_linewords(ff, l); + if(unlikely(words < 2)) { + if(unlikely(words)) error("Cannot read %s numastat line %u. Expected 2 params, read %u.", m->name, l, words); + continue; + } + + char *name = procfile_lineword(ff, l, 0); + char *value = procfile_lineword(ff, l, 1); + if (unlikely(!name || !*name || !value || !*value)) continue; + + rrddim_set(st, name, strtoull(value, NULL, 10)); + } + rrdset_done(st); + } + } + } + + return 0; +} diff --git a/src/sys_fs_cgroup.c b/src/sys_fs_cgroup.c index 298f38a38..2b7254f47 100644 --- a/src/sys_fs_cgroup.c +++ b/src/sys_fs_cgroup.c @@ -3,13 +3,38 @@ // ---------------------------------------------------------------------------- // cgroup globals +#define CHART_PRIORITY_SYSTEMD_SERVICES 19000 +#define CHART_PRIORITY_CONTAINERS 40000 + +static long system_page_size = 4096; // system will be queried via sysconf() in configuration() + static int cgroup_enable_cpuacct_stat = CONFIG_ONDEMAND_ONDEMAND; static int cgroup_enable_cpuacct_usage = CONFIG_ONDEMAND_ONDEMAND; static int cgroup_enable_memory = CONFIG_ONDEMAND_ONDEMAND; -static int cgroup_enable_devices = CONFIG_ONDEMAND_ONDEMAND; -static int cgroup_enable_blkio = CONFIG_ONDEMAND_ONDEMAND; +static int cgroup_enable_detailed_memory = CONFIG_ONDEMAND_ONDEMAND; +static int cgroup_enable_memory_failcnt = CONFIG_ONDEMAND_ONDEMAND; +static int cgroup_enable_swap = CONFIG_ONDEMAND_ONDEMAND; +static int cgroup_enable_blkio_io = CONFIG_ONDEMAND_ONDEMAND; +static int cgroup_enable_blkio_ops = CONFIG_ONDEMAND_ONDEMAND; +static int cgroup_enable_blkio_throttle_io = CONFIG_ONDEMAND_ONDEMAND; +static int cgroup_enable_blkio_throttle_ops = CONFIG_ONDEMAND_ONDEMAND; +static int cgroup_enable_blkio_merged_ops = CONFIG_ONDEMAND_ONDEMAND; +static int cgroup_enable_blkio_queued_ops = CONFIG_ONDEMAND_ONDEMAND; + +static int cgroup_enable_systemd_services = CONFIG_ONDEMAND_YES; +static int cgroup_enable_systemd_services_detailed_memory = CONFIG_ONDEMAND_NO; +static int cgroup_used_memory_without_cache = CONFIG_ONDEMAND_YES; + +static int cgroup_search_in_devices = 1; + static int cgroup_enable_new_cgroups_detected_at_runtime = 1; static int cgroup_check_for_new_every = 10; +static int cgroup_update_every = 1; + +static int cgroup_recheck_zero_blkio_every_iterations = 10; +static int cgroup_recheck_zero_mem_failcnt_every_iterations = 10; +static int cgroup_recheck_zero_mem_detailed_every_iterations = 10; + static char *cgroup_cpuacct_base = NULL; static char *cgroup_blkio_base = NULL; static char *cgroup_memory_base = NULL; @@ -19,16 +44,59 @@ static int cgroup_root_count = 0; static int cgroup_root_max = 500; static int cgroup_max_depth = 0; +static SIMPLE_PATTERN *enabled_cgroup_patterns = NULL; +static SIMPLE_PATTERN *enabled_cgroup_paths = NULL; +static SIMPLE_PATTERN *enabled_cgroup_renames = NULL; +static SIMPLE_PATTERN *systemd_services_cgroups = NULL; + +static char *cgroups_rename_script = PLUGINS_DIR "/cgroup-name.sh"; + +static uint32_t Read_hash = 0; +static uint32_t Write_hash = 0; +static uint32_t user_hash = 0; +static uint32_t system_hash = 0; + void read_cgroup_plugin_configuration() { - cgroup_check_for_new_every = config_get_number("plugin:cgroups", "check for new cgroups every", cgroup_check_for_new_every); + system_page_size = sysconf(_SC_PAGESIZE); + + Read_hash = simple_hash("Read"); + Write_hash = simple_hash("Write"); + user_hash = simple_hash("user"); + system_hash = simple_hash("system"); + + cgroup_update_every = (int)config_get_number("plugin:cgroups", "update every", rrd_update_every); + if(cgroup_update_every < rrd_update_every) + cgroup_update_every = rrd_update_every; + + cgroup_check_for_new_every = (int)config_get_number("plugin:cgroups", "check for new cgroups every", cgroup_check_for_new_every * cgroup_update_every); + if(cgroup_check_for_new_every < cgroup_update_every) + cgroup_check_for_new_every = cgroup_update_every; + + cgroup_enable_cpuacct_stat = config_get_boolean_ondemand("plugin:cgroups", "enable cpuacct stat (total CPU)", cgroup_enable_cpuacct_stat); + cgroup_enable_cpuacct_usage = config_get_boolean_ondemand("plugin:cgroups", "enable cpuacct usage (per core CPU)", cgroup_enable_cpuacct_usage); + + cgroup_enable_memory = config_get_boolean_ondemand("plugin:cgroups", "enable memory (used mem including cache)", cgroup_enable_memory); + cgroup_enable_detailed_memory = config_get_boolean_ondemand("plugin:cgroups", "enable detailed memory", cgroup_enable_detailed_memory); + cgroup_enable_memory_failcnt = config_get_boolean_ondemand("plugin:cgroups", "enable memory limits fail count", cgroup_enable_memory_failcnt); + cgroup_enable_swap = config_get_boolean_ondemand("plugin:cgroups", "enable swap memory", cgroup_enable_swap); + + cgroup_enable_blkio_io = config_get_boolean_ondemand("plugin:cgroups", "enable blkio bandwidth", cgroup_enable_blkio_io); + cgroup_enable_blkio_ops = config_get_boolean_ondemand("plugin:cgroups", "enable blkio operations", cgroup_enable_blkio_ops); + cgroup_enable_blkio_throttle_io = config_get_boolean_ondemand("plugin:cgroups", "enable blkio throttle bandwidth", cgroup_enable_blkio_throttle_io); + cgroup_enable_blkio_throttle_ops = config_get_boolean_ondemand("plugin:cgroups", "enable blkio throttle operations", cgroup_enable_blkio_throttle_ops); + cgroup_enable_blkio_queued_ops = config_get_boolean_ondemand("plugin:cgroups", "enable blkio queued operations", cgroup_enable_blkio_queued_ops); + cgroup_enable_blkio_merged_ops = config_get_boolean_ondemand("plugin:cgroups", "enable blkio merged operations", cgroup_enable_blkio_merged_ops); - cgroup_enable_cpuacct_stat = config_get_boolean_ondemand("plugin:cgroups", "enable cpuacct stat", cgroup_enable_cpuacct_stat); - cgroup_enable_cpuacct_usage = config_get_boolean_ondemand("plugin:cgroups", "enable cpuacct usage", cgroup_enable_cpuacct_usage); - cgroup_enable_memory = config_get_boolean_ondemand("plugin:cgroups", "enable memory", cgroup_enable_memory); - cgroup_enable_blkio = config_get_boolean_ondemand("plugin:cgroups", "enable blkio", cgroup_enable_blkio); + cgroup_recheck_zero_blkio_every_iterations = (int)config_get_number("plugin:cgroups", "recheck zero blkio every iterations", cgroup_recheck_zero_blkio_every_iterations); + cgroup_recheck_zero_mem_failcnt_every_iterations = (int)config_get_number("plugin:cgroups", "recheck zero memory failcnt every iterations", cgroup_recheck_zero_mem_failcnt_every_iterations); + cgroup_recheck_zero_mem_detailed_every_iterations = (int)config_get_number("plugin:cgroups", "recheck zero detailed memory every iterations", cgroup_recheck_zero_mem_detailed_every_iterations); + + cgroup_enable_systemd_services = config_get_boolean("plugin:cgroups", "enable systemd services", cgroup_enable_systemd_services); + cgroup_enable_systemd_services_detailed_memory = config_get_boolean("plugin:cgroups", "enable systemd services detailed memory", cgroup_enable_systemd_services_detailed_memory); + cgroup_used_memory_without_cache = config_get_boolean("plugin:cgroups", "report used memory without cache", cgroup_used_memory_without_cache); char filename[FILENAME_MAX + 1], *s; - struct mountinfo *mi, *root = mountinfo_read(); + struct mountinfo *mi, *root = mountinfo_read(0); mi = mountinfo_find_by_filesystem_super_option(root, "cgroup", "cpuacct"); if(!mi) mi = mountinfo_find_by_filesystem_mount_source(root, "cgroup", "cpuacct"); @@ -70,11 +138,67 @@ void read_cgroup_plugin_configuration() { snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, s); cgroup_devices_base = config_get("plugin:cgroups", "path to /sys/fs/cgroup/devices", filename); - cgroup_root_max = config_get_number("plugin:cgroups", "max cgroups to allow", cgroup_root_max); - cgroup_max_depth = config_get_number("plugin:cgroups", "max cgroups depth to monitor", cgroup_max_depth); + cgroup_root_max = (int)config_get_number("plugin:cgroups", "max cgroups to allow", cgroup_root_max); + cgroup_max_depth = (int)config_get_number("plugin:cgroups", "max cgroups depth to monitor", cgroup_max_depth); cgroup_enable_new_cgroups_detected_at_runtime = config_get_boolean("plugin:cgroups", "enable new cgroups detected at run time", cgroup_enable_new_cgroups_detected_at_runtime); + enabled_cgroup_patterns = simple_pattern_create( + config_get("plugin:cgroups", "enable by default cgroups matching", + " !*.mount " + " !*.partition " + " !*.scope " + " !*.service " + " !*.slice " + " !*.swap " + " !*.user " + " !/ " + " !/docker " + " !/libvirt " + " !/lxc " + " !/lxc/*/ns " // #1397 + " !/machine " + " !/qemu " + " !/system " + " !/systemd " + " !/user " + " * " // enable anything else + ), SIMPLE_PATTERN_EXACT); + + enabled_cgroup_paths = simple_pattern_create( + config_get("plugin:cgroups", "search for cgroups in subpaths matching", + " !*-qemu " // #345 + " !/init.scope " + " !/system " + " !/systemd " + " !/user " + " !/user.slice " + " * " + ), SIMPLE_PATTERN_EXACT); + + cgroups_rename_script = config_get("plugin:cgroups", "script to get cgroup names", cgroups_rename_script); + + enabled_cgroup_renames = simple_pattern_create( + config_get("plugin:cgroups", "run script to rename cgroups matching", + " !/ " + " !*.mount " + " !*.partition " + " !*.scope " + " !*.service " + " !*.slice " + " !*.swap " + " !*.user " + " * " + ), SIMPLE_PATTERN_EXACT); + + if(cgroup_enable_systemd_services) { + systemd_services_cgroups = simple_pattern_create( + config_get("plugin:cgroups", "cgroups to match as systemd services", + " !/system.slice/*/*.service " + " /system.slice/*.service " + ), SIMPLE_PATTERN_EXACT); + } + mountinfo_free(root); } @@ -83,6 +207,8 @@ void read_cgroup_plugin_configuration() { struct blkio { int updated; + int enabled; // CONFIG_ONDEMAND_YES or CONFIG_ONDEMAND_ONDEMAND + int delay_counter; char *filename; @@ -97,12 +223,32 @@ struct blkio { // https://www.kernel.org/doc/Documentation/cgroup-v1/memory.txt struct memory { - int updated; + ARL_BASE *arl_base; + ARL_ENTRY *arl_dirty; + ARL_ENTRY *arl_swap; - char *filename; + int updated_detailed; + int updated_usage_in_bytes; + int updated_msw_usage_in_bytes; + int updated_failcnt; + + int enabled_detailed; // CONFIG_ONDEMAND_YES or CONFIG_ONDEMAND_ONDEMAND + int enabled_usage_in_bytes; // CONFIG_ONDEMAND_YES or CONFIG_ONDEMAND_ONDEMAND + int enabled_msw_usage_in_bytes; // CONFIG_ONDEMAND_YES or CONFIG_ONDEMAND_ONDEMAND + int enabled_failcnt; // CONFIG_ONDEMAND_YES or CONFIG_ONDEMAND_ONDEMAND - int has_dirty_swap; + int delay_counter_detailed; + int delay_counter_failcnt; + char *filename_detailed; + char *filename_usage_in_bytes; + char *filename_msw_usage_in_bytes; + char *filename_failcnt; + + int detailed_has_dirty; + int detailed_has_swap; + + // detailed metrics unsigned long long cache; unsigned long long rss; unsigned long long rss_huge; @@ -139,22 +285,16 @@ struct memory { unsigned long long total_unevictable; */ - int usage_in_bytes_updated; - char *filename_usage_in_bytes; + // single file metrics unsigned long long usage_in_bytes; - - int msw_usage_in_bytes_updated; - char *filename_msw_usage_in_bytes; unsigned long long msw_usage_in_bytes; - - int failcnt_updated; - char *filename_failcnt; unsigned long long failcnt; }; // https://www.kernel.org/doc/Documentation/cgroup-v1/cpuacct.txt struct cpuacct_stat { int updated; + int enabled; // CONFIG_ONDEMAND_YES or CONFIG_ONDEMAND_ONDEMAND char *filename; @@ -165,6 +305,7 @@ struct cpuacct_stat { // https://www.kernel.org/doc/Documentation/cgroup-v1/cpuacct.txt struct cpuacct_usage { int updated; + int enabled; // CONFIG_ONDEMAND_YES or CONFIG_ONDEMAND_ONDEMAND char *filename; @@ -172,7 +313,8 @@ struct cpuacct_usage { unsigned long long *cpu_percpu; }; -#define CGROUP_OPTIONS_DISABLED_DUPLICATE 0x00000001 +#define CGROUP_OPTIONS_DISABLED_DUPLICATE 0x00000001 +#define CGROUP_OPTIONS_SYSTEM_SLICE_SERVICE 0x00000002 struct cgroup { uint32_t options; @@ -202,6 +344,51 @@ struct cgroup { struct blkio io_merged; // operations struct blkio io_queued; // operations + // per cgroup charts + RRDSET *st_cpu; + RRDSET *st_cpu_per_core; + RRDSET *st_mem; + RRDSET *st_writeback; + RRDSET *st_mem_activity; + RRDSET *st_pgfaults; + RRDSET *st_mem_usage; + RRDSET *st_mem_failcnt; + RRDSET *st_io; + RRDSET *st_serviced_ops; + RRDSET *st_throttle_io; + RRDSET *st_throttle_serviced_ops; + RRDSET *st_queued_ops; + RRDSET *st_merged_ops; + + // services + RRDDIM *rd_cpu; + RRDDIM *rd_mem_usage; + RRDDIM *rd_mem_failcnt; + RRDDIM *rd_swap_usage; + + RRDDIM *rd_mem_detailed_cache; + RRDDIM *rd_mem_detailed_rss; + RRDDIM *rd_mem_detailed_mapped; + RRDDIM *rd_mem_detailed_writeback; + RRDDIM *rd_mem_detailed_pgpgin; + RRDDIM *rd_mem_detailed_pgpgout; + RRDDIM *rd_mem_detailed_pgfault; + RRDDIM *rd_mem_detailed_pgmajfault; + + RRDDIM *rd_io_service_bytes_read; + RRDDIM *rd_io_serviced_read; + RRDDIM *rd_throttle_io_read; + RRDDIM *rd_throttle_io_serviced_read; + RRDDIM *rd_io_queued_read; + RRDDIM *rd_io_merged_read; + + RRDDIM *rd_io_service_bytes_write; + RRDDIM *rd_io_serviced_write; + RRDDIM *rd_throttle_io_write; + RRDDIM *rd_throttle_io_serviced_write; + RRDDIM *rd_io_queued_write; + RRDDIM *rd_io_merged_write; + struct cgroup *next; } *cgroup_root = NULL; @@ -209,29 +396,27 @@ struct cgroup { // ---------------------------------------------------------------------------- // read values from /sys -void cgroup_read_cpuacct_stat(struct cpuacct_stat *cp) { +static inline void cgroup_read_cpuacct_stat(struct cpuacct_stat *cp) { static procfile *ff = NULL; - static uint32_t user_hash = 0; - static uint32_t system_hash = 0; - - if(unlikely(user_hash == 0)) { - user_hash = simple_hash("user"); - system_hash = simple_hash("system"); - } - - cp->updated = 0; - if(cp->filename) { + if(likely(cp->filename)) { ff = procfile_reopen(ff, cp->filename, NULL, PROCFILE_FLAG_DEFAULT); - if(!ff) return; + if(unlikely(!ff)) { + cp->updated = 0; + return; + } ff = procfile_readall(ff); - if(!ff) return; + if(unlikely(!ff)) { + cp->updated = 0; + return; + } unsigned long i, lines = procfile_lines(ff); - if(lines < 1) { + if(unlikely(lines < 1)) { error("File '%s' should have 1+ lines.", cp->filename); + cp->updated = 0; return; } @@ -239,37 +424,47 @@ void cgroup_read_cpuacct_stat(struct cpuacct_stat *cp) { char *s = procfile_lineword(ff, i, 0); uint32_t hash = simple_hash(s); - if(hash == user_hash && !strcmp(s, "user")) - cp->user = strtoull(procfile_lineword(ff, i, 1), NULL, 10); + if(unlikely(hash == user_hash && !strcmp(s, "user"))) + cp->user = str2ull(procfile_lineword(ff, i, 1)); - else if(hash == system_hash && !strcmp(s, "system")) - cp->system = strtoull(procfile_lineword(ff, i, 1), NULL, 10); + else if(unlikely(hash == system_hash && !strcmp(s, "system"))) + cp->system = str2ull(procfile_lineword(ff, i, 1)); } cp->updated = 1; - // fprintf(stderr, "READ '%s': user: %llu, system: %llu\n", cp->filename, cp->user, cp->system); + if(unlikely(cp->enabled == CONFIG_ONDEMAND_ONDEMAND && (cp->user || cp->system))) + cp->enabled = CONFIG_ONDEMAND_YES; } } -void cgroup_read_cpuacct_usage(struct cpuacct_usage *ca) { +static inline void cgroup_read_cpuacct_usage(struct cpuacct_usage *ca) { static procfile *ff = NULL; - ca->updated = 0; - if(ca->filename) { + if(likely(ca->filename)) { ff = procfile_reopen(ff, ca->filename, NULL, PROCFILE_FLAG_DEFAULT); - if(!ff) return; + if(unlikely(!ff)) { + ca->updated = 0; + return; + } ff = procfile_readall(ff); - if(!ff) return; + if(unlikely(!ff)) { + ca->updated = 0; + return; + } - if(procfile_lines(ff) < 1) { - error("File '%s' should have 1+ lines but has %u.", ca->filename, procfile_lines(ff)); + if(unlikely(procfile_lines(ff) < 1)) { + error("File '%s' should have 1+ lines but has %zu.", ca->filename, procfile_lines(ff)); + ca->updated = 0; return; } unsigned long i = procfile_linewords(ff, 0); - if(i <= 0) return; + if(unlikely(i == 0)) { + return; + ca->updated = 0; + } // we may have 1 more CPU reported while(i > 0) { @@ -278,54 +473,52 @@ void cgroup_read_cpuacct_usage(struct cpuacct_usage *ca) { else break; } - if(i != ca->cpus) { + if(unlikely(i != ca->cpus)) { freez(ca->cpu_percpu); ca->cpu_percpu = mallocz(sizeof(unsigned long long) * i); ca->cpus = (unsigned int)i; } + unsigned long long total = 0; for(i = 0; i < ca->cpus ;i++) { - ca->cpu_percpu[i] = strtoull(procfile_lineword(ff, 0, i), NULL, 10); - // fprintf(stderr, "READ '%s': cpu%d/%d: %llu ('%s')\n", ca->filename, i, ca->cpus, ca->cpu_percpu[i], procfile_lineword(ff, 0, i)); + unsigned long long n = str2ull(procfile_lineword(ff, 0, i)); + ca->cpu_percpu[i] = n; + total += n; } ca->updated = 1; + + if(unlikely(ca->enabled == CONFIG_ONDEMAND_ONDEMAND && total)) + ca->enabled = CONFIG_ONDEMAND_YES; } } -void cgroup_read_blkio(struct blkio *io) { +static inline void cgroup_read_blkio(struct blkio *io) { static procfile *ff = NULL; - static uint32_t Read_hash = 0; - static uint32_t Write_hash = 0; -/* - static uint32_t Sync_hash = 0; - static uint32_t Async_hash = 0; - static uint32_t Total_hash = 0; -*/ - - if(unlikely(Read_hash == 0)) { - Read_hash = simple_hash("Read"); - Write_hash = simple_hash("Write"); -/* - Sync_hash = simple_hash("Sync"); - Async_hash = simple_hash("Async"); - Total_hash = simple_hash("Total"); -*/ + if(unlikely(io->enabled == CONFIG_ONDEMAND_ONDEMAND && io->delay_counter > 0)) { + io->delay_counter--; + return; } - io->updated = 0; - if(io->filename) { + if(likely(io->filename)) { ff = procfile_reopen(ff, io->filename, NULL, PROCFILE_FLAG_DEFAULT); - if(!ff) return; + if(unlikely(!ff)) { + io->updated = 0; + return; + } ff = procfile_readall(ff); - if(!ff) return; + if(unlikely(!ff)) { + io->updated = 0; + return; + } unsigned long i, lines = procfile_lines(ff); - if(lines < 1) { + if(unlikely(lines < 1)) { error("File '%s' should have 1+ lines.", io->filename); + io->updated = 0; return; } @@ -341,255 +534,142 @@ void cgroup_read_blkio(struct blkio *io) { char *s = procfile_lineword(ff, i, 1); uint32_t hash = simple_hash(s); - if(hash == Read_hash && !strcmp(s, "Read")) - io->Read += strtoull(procfile_lineword(ff, i, 2), NULL, 10); + if(unlikely(hash == Read_hash && !strcmp(s, "Read"))) + io->Read += str2ull(procfile_lineword(ff, i, 2)); - else if(hash == Write_hash && !strcmp(s, "Write")) - io->Write += strtoull(procfile_lineword(ff, i, 2), NULL, 10); + else if(unlikely(hash == Write_hash && !strcmp(s, "Write"))) + io->Write += str2ull(procfile_lineword(ff, i, 2)); /* - else if(hash == Sync_hash && !strcmp(s, "Sync")) - io->Sync += strtoull(procfile_lineword(ff, i, 2), NULL, 10); + else if(unlikely(hash == Sync_hash && !strcmp(s, "Sync"))) + io->Sync += str2ull(procfile_lineword(ff, i, 2)); - else if(hash == Async_hash && !strcmp(s, "Async")) - io->Async += strtoull(procfile_lineword(ff, i, 2), NULL, 10); + else if(unlikely(hash == Async_hash && !strcmp(s, "Async"))) + io->Async += str2ull(procfile_lineword(ff, i, 2)); - else if(hash == Total_hash && !strcmp(s, "Total")) - io->Total += strtoull(procfile_lineword(ff, i, 2), NULL, 10); + else if(unlikely(hash == Total_hash && !strcmp(s, "Total"))) + io->Total += str2ull(procfile_lineword(ff, i, 2)); */ } io->updated = 1; - // fprintf(stderr, "READ '%s': Read: %llu, Write: %llu, Sync: %llu, Async: %llu, Total: %llu\n", io->filename, io->Read, io->Write, io->Sync, io->Async, io->Total); + + if(unlikely(io->enabled == CONFIG_ONDEMAND_ONDEMAND)) { + if(unlikely(io->Read || io->Write)) + io->enabled = CONFIG_ONDEMAND_YES; + else + io->delay_counter = cgroup_recheck_zero_blkio_every_iterations; + } } } -void cgroup_read_memory(struct memory *mem) { +static inline void cgroup_read_memory(struct memory *mem) { static procfile *ff = NULL; - static uint32_t cache_hash = 0; - static uint32_t rss_hash = 0; - static uint32_t rss_huge_hash = 0; - static uint32_t mapped_file_hash = 0; - static uint32_t writeback_hash = 0; - static uint32_t dirty_hash = 0; - static uint32_t swap_hash = 0; - static uint32_t pgpgin_hash = 0; - static uint32_t pgpgout_hash = 0; - static uint32_t pgfault_hash = 0; - static uint32_t pgmajfault_hash = 0; -/* - static uint32_t inactive_anon_hash = 0; - static uint32_t active_anon_hash = 0; - static uint32_t inactive_file_hash = 0; - static uint32_t active_file_hash = 0; - static uint32_t unevictable_hash = 0; - static uint32_t hierarchical_memory_limit_hash = 0; - static uint32_t total_cache_hash = 0; - static uint32_t total_rss_hash = 0; - static uint32_t total_rss_huge_hash = 0; - static uint32_t total_mapped_file_hash = 0; - static uint32_t total_writeback_hash = 0; - static uint32_t total_dirty_hash = 0; - static uint32_t total_swap_hash = 0; - static uint32_t total_pgpgin_hash = 0; - static uint32_t total_pgpgout_hash = 0; - static uint32_t total_pgfault_hash = 0; - static uint32_t total_pgmajfault_hash = 0; - static uint32_t total_inactive_anon_hash = 0; - static uint32_t total_active_anon_hash = 0; - static uint32_t total_inactive_file_hash = 0; - static uint32_t total_active_file_hash = 0; - static uint32_t total_unevictable_hash = 0; -*/ - if(unlikely(cache_hash == 0)) { - cache_hash = simple_hash("cache"); - rss_hash = simple_hash("rss"); - rss_huge_hash = simple_hash("rss_huge"); - mapped_file_hash = simple_hash("mapped_file"); - writeback_hash = simple_hash("writeback"); - dirty_hash = simple_hash("dirty"); - swap_hash = simple_hash("swap"); - pgpgin_hash = simple_hash("pgpgin"); - pgpgout_hash = simple_hash("pgpgout"); - pgfault_hash = simple_hash("pgfault"); - pgmajfault_hash = simple_hash("pgmajfault"); -/* - inactive_anon_hash = simple_hash("inactive_anon"); - active_anon_hash = simple_hash("active_anon"); - inactive_file_hash = simple_hash("inactive_file"); - active_file_hash = simple_hash("active_file"); - unevictable_hash = simple_hash("unevictable"); - hierarchical_memory_limit_hash = simple_hash("hierarchical_memory_limit"); - total_cache_hash = simple_hash("total_cache"); - total_rss_hash = simple_hash("total_rss"); - total_rss_huge_hash = simple_hash("total_rss_huge"); - total_mapped_file_hash = simple_hash("total_mapped_file"); - total_writeback_hash = simple_hash("total_writeback"); - total_dirty_hash = simple_hash("total_dirty"); - total_swap_hash = simple_hash("total_swap"); - total_pgpgin_hash = simple_hash("total_pgpgin"); - total_pgpgout_hash = simple_hash("total_pgpgout"); - total_pgfault_hash = simple_hash("total_pgfault"); - total_pgmajfault_hash = simple_hash("total_pgmajfault"); - total_inactive_anon_hash = simple_hash("total_inactive_anon"); - total_active_anon_hash = simple_hash("total_active_anon"); - total_inactive_file_hash = simple_hash("total_inactive_file"); - total_active_file_hash = simple_hash("total_active_file"); - total_unevictable_hash = simple_hash("total_unevictable"); -*/ - } + // read detailed ram usage + if(likely(mem->filename_detailed)) { + if(unlikely(mem->enabled_detailed == CONFIG_ONDEMAND_ONDEMAND && mem->delay_counter_detailed > 0)) { + mem->delay_counter_detailed--; + goto memory_next; + } - mem->updated = 0; - if(mem->filename) { - ff = procfile_reopen(ff, mem->filename, NULL, PROCFILE_FLAG_DEFAULT); - if(!ff) return; + ff = procfile_reopen(ff, mem->filename_detailed, NULL, PROCFILE_FLAG_DEFAULT); + if(unlikely(!ff)) { + mem->updated_detailed = 0; + goto memory_next; + } ff = procfile_readall(ff); - if(!ff) return; + if(unlikely(!ff)) { + mem->updated_detailed = 0; + goto memory_next; + } unsigned long i, lines = procfile_lines(ff); - if(lines < 1) { - error("File '%s' should have 1+ lines.", mem->filename); - return; + if(unlikely(lines < 1)) { + error("File '%s' should have 1+ lines.", mem->filename_detailed); + mem->updated_detailed = 0; + goto memory_next; } - for(i = 0; i < lines ; i++) { - char *s = procfile_lineword(ff, i, 0); - uint32_t hash = simple_hash(s); - - if(hash == cache_hash && !strcmp(s, "cache")) - mem->cache = strtoull(procfile_lineword(ff, i, 1), NULL, 10); - - else if(hash == rss_hash && !strcmp(s, "rss")) - mem->rss = strtoull(procfile_lineword(ff, i, 1), NULL, 10); - - else if(hash == rss_huge_hash && !strcmp(s, "rss_huge")) - mem->rss_huge = strtoull(procfile_lineword(ff, i, 1), NULL, 10); - - else if(hash == mapped_file_hash && !strcmp(s, "mapped_file")) - mem->mapped_file = strtoull(procfile_lineword(ff, i, 1), NULL, 10); - - else if(hash == writeback_hash && !strcmp(s, "writeback")) - mem->writeback = strtoull(procfile_lineword(ff, i, 1), NULL, 10); - - else if(hash == dirty_hash && !strcmp(s, "dirty")) { - mem->dirty = strtoull(procfile_lineword(ff, i, 1), NULL, 10); - mem->has_dirty_swap = 1; - } - - else if(hash == swap_hash && !strcmp(s, "swap")) { - mem->swap = strtoull(procfile_lineword(ff, i, 1), NULL, 10); - mem->has_dirty_swap = 1; - } - - else if(hash == pgpgin_hash && !strcmp(s, "pgpgin")) - mem->pgpgin = strtoull(procfile_lineword(ff, i, 1), NULL, 10); - - else if(hash == pgpgout_hash && !strcmp(s, "pgpgout")) - mem->pgpgout = strtoull(procfile_lineword(ff, i, 1), NULL, 10); - - else if(hash == pgfault_hash && !strcmp(s, "pgfault")) - mem->pgfault = strtoull(procfile_lineword(ff, i, 1), NULL, 10); - - else if(hash == pgmajfault_hash && !strcmp(s, "pgmajfault")) - mem->pgmajfault = strtoull(procfile_lineword(ff, i, 1), NULL, 10); - -/* - else if(hash == inactive_anon_hash && !strcmp(s, "inactive_anon")) - mem->inactive_anon = strtoull(procfile_lineword(ff, i, 1), NULL, 10); - - else if(hash == active_anon_hash && !strcmp(s, "active_anon")) - mem->active_anon = strtoull(procfile_lineword(ff, i, 1), NULL, 10); - - else if(hash == inactive_file_hash && !strcmp(s, "inactive_file")) - mem->inactive_file = strtoull(procfile_lineword(ff, i, 1), NULL, 10); - - else if(hash == active_file_hash && !strcmp(s, "active_file")) - mem->active_file = strtoull(procfile_lineword(ff, i, 1), NULL, 10); - - else if(hash == unevictable_hash && !strcmp(s, "unevictable")) - mem->unevictable = strtoull(procfile_lineword(ff, i, 1), NULL, 10); - - else if(hash == hierarchical_memory_limit_hash && !strcmp(s, "hierarchical_memory_limit")) - mem->hierarchical_memory_limit = strtoull(procfile_lineword(ff, i, 1), NULL, 10); - - else if(hash == total_cache_hash && !strcmp(s, "total_cache")) - mem->total_cache = strtoull(procfile_lineword(ff, i, 1), NULL, 10); - - else if(hash == total_rss_hash && !strcmp(s, "total_rss")) - mem->total_rss = strtoull(procfile_lineword(ff, i, 1), NULL, 10); - - else if(hash == total_rss_huge_hash && !strcmp(s, "total_rss_huge")) - mem->total_rss_huge = strtoull(procfile_lineword(ff, i, 1), NULL, 10); - - else if(hash == total_mapped_file_hash && !strcmp(s, "total_mapped_file")) - mem->total_mapped_file = strtoull(procfile_lineword(ff, i, 1), NULL, 10); - - else if(hash == total_writeback_hash && !strcmp(s, "total_writeback")) - mem->total_writeback = strtoull(procfile_lineword(ff, i, 1), NULL, 10); - - else if(hash == total_dirty_hash && !strcmp(s, "total_dirty")) - mem->total_dirty = strtoull(procfile_lineword(ff, i, 1), NULL, 10); - - else if(hash == total_swap_hash && !strcmp(s, "total_swap")) - mem->total_swap = strtoull(procfile_lineword(ff, i, 1), NULL, 10); - - else if(hash == total_pgpgin_hash && !strcmp(s, "total_pgpgin")) - mem->total_pgpgin = strtoull(procfile_lineword(ff, i, 1), NULL, 10); - - else if(hash == total_pgpgout_hash && !strcmp(s, "total_pgpgout")) - mem->total_pgpgout = strtoull(procfile_lineword(ff, i, 1), NULL, 10); + if(unlikely(!mem->arl_base)) { + mem->arl_base = arl_create("cgroup/memory", NULL, 60); + + arl_expect(mem->arl_base, "cache", &mem->cache); + arl_expect(mem->arl_base, "rss", &mem->rss); + arl_expect(mem->arl_base, "rss_huge", &mem->rss_huge); + arl_expect(mem->arl_base, "mapped_file", &mem->mapped_file); + arl_expect(mem->arl_base, "writeback", &mem->writeback); + mem->arl_dirty = arl_expect(mem->arl_base, "dirty", &mem->dirty); + mem->arl_swap = arl_expect(mem->arl_base, "swap", &mem->swap); + arl_expect(mem->arl_base, "pgpgin", &mem->pgpgin); + arl_expect(mem->arl_base, "pgpgout", &mem->pgpgout); + arl_expect(mem->arl_base, "pgfault", &mem->pgfault); + arl_expect(mem->arl_base, "pgmajfault", &mem->pgmajfault); + } - else if(hash == total_pgfault_hash && !strcmp(s, "total_pgfault")) - mem->total_pgfault = strtoull(procfile_lineword(ff, i, 1), NULL, 10); + arl_begin(mem->arl_base); - else if(hash == total_pgmajfault_hash && !strcmp(s, "total_pgmajfault")) - mem->total_pgmajfault = strtoull(procfile_lineword(ff, i, 1), NULL, 10); + for(i = 0; i < lines ; i++) { + if(arl_check(mem->arl_base, + procfile_lineword(ff, i, 0), + procfile_lineword(ff, i, 1))) break; + } - else if(hash == total_inactive_anon_hash && !strcmp(s, "total_inactive_anon")) - mem->total_inactive_anon = strtoull(procfile_lineword(ff, i, 1), NULL, 10); + if(unlikely(mem->arl_dirty->flags & ARL_ENTRY_FLAG_FOUND)) + mem->detailed_has_dirty = 1; - else if(hash == total_active_anon_hash && !strcmp(s, "total_active_anon")) - mem->total_active_anon = strtoull(procfile_lineword(ff, i, 1), NULL, 10); + if(unlikely(mem->arl_swap->flags & ARL_ENTRY_FLAG_FOUND)) + mem->detailed_has_swap = 1; - else if(hash == total_inactive_file_hash && !strcmp(s, "total_inactive_file")) - mem->total_inactive_file = strtoull(procfile_lineword(ff, i, 1), NULL, 10); + // fprintf(stderr, "READ: '%s', cache: %llu, rss: %llu, rss_huge: %llu, mapped_file: %llu, writeback: %llu, dirty: %llu, swap: %llu, pgpgin: %llu, pgpgout: %llu, pgfault: %llu, pgmajfault: %llu, inactive_anon: %llu, active_anon: %llu, inactive_file: %llu, active_file: %llu, unevictable: %llu, hierarchical_memory_limit: %llu, total_cache: %llu, total_rss: %llu, total_rss_huge: %llu, total_mapped_file: %llu, total_writeback: %llu, total_dirty: %llu, total_swap: %llu, total_pgpgin: %llu, total_pgpgout: %llu, total_pgfault: %llu, total_pgmajfault: %llu, total_inactive_anon: %llu, total_active_anon: %llu, total_inactive_file: %llu, total_active_file: %llu, total_unevictable: %llu\n", mem->filename, mem->cache, mem->rss, mem->rss_huge, mem->mapped_file, mem->writeback, mem->dirty, mem->swap, mem->pgpgin, mem->pgpgout, mem->pgfault, mem->pgmajfault, mem->inactive_anon, mem->active_anon, mem->inactive_file, mem->active_file, mem->unevictable, mem->hierarchical_memory_limit, mem->total_cache, mem->total_rss, mem->total_rss_huge, mem->total_mapped_file, mem->total_writeback, mem->total_dirty, mem->total_swap, mem->total_pgpgin, mem->total_pgpgout, mem->total_pgfault, mem->total_pgmajfault, mem->total_inactive_anon, mem->total_active_anon, mem->total_inactive_file, mem->total_active_file, mem->total_unevictable); - else if(hash == total_active_file_hash && !strcmp(s, "total_active_file")) - mem->total_active_file = strtoull(procfile_lineword(ff, i, 1), NULL, 10); + mem->updated_detailed = 1; - else if(hash == total_unevictable_hash && !strcmp(s, "total_unevictable")) - mem->total_unevictable = strtoull(procfile_lineword(ff, i, 1), NULL, 10); -*/ + if(unlikely(mem->enabled_detailed == CONFIG_ONDEMAND_ONDEMAND)) { + if(mem->cache || mem->dirty || mem->rss || mem->rss_huge || mem->mapped_file || mem->writeback || mem->swap || mem->pgpgin || mem->pgpgout || mem->pgfault || mem->pgmajfault) + mem->enabled_detailed = CONFIG_ONDEMAND_YES; + else + mem->delay_counter_detailed = cgroup_recheck_zero_mem_detailed_every_iterations; } - - // fprintf(stderr, "READ: '%s', cache: %llu, rss: %llu, rss_huge: %llu, mapped_file: %llu, writeback: %llu, dirty: %llu, swap: %llu, pgpgin: %llu, pgpgout: %llu, pgfault: %llu, pgmajfault: %llu, inactive_anon: %llu, active_anon: %llu, inactive_file: %llu, active_file: %llu, unevictable: %llu, hierarchical_memory_limit: %llu, total_cache: %llu, total_rss: %llu, total_rss_huge: %llu, total_mapped_file: %llu, total_writeback: %llu, total_dirty: %llu, total_swap: %llu, total_pgpgin: %llu, total_pgpgout: %llu, total_pgfault: %llu, total_pgmajfault: %llu, total_inactive_anon: %llu, total_active_anon: %llu, total_inactive_file: %llu, total_active_file: %llu, total_unevictable: %llu\n", mem->filename, mem->cache, mem->rss, mem->rss_huge, mem->mapped_file, mem->writeback, mem->dirty, mem->swap, mem->pgpgin, mem->pgpgout, mem->pgfault, mem->pgmajfault, mem->inactive_anon, mem->active_anon, mem->inactive_file, mem->active_file, mem->unevictable, mem->hierarchical_memory_limit, mem->total_cache, mem->total_rss, mem->total_rss_huge, mem->total_mapped_file, mem->total_writeback, mem->total_dirty, mem->total_swap, mem->total_pgpgin, mem->total_pgpgout, mem->total_pgfault, mem->total_pgmajfault, mem->total_inactive_anon, mem->total_active_anon, mem->total_inactive_file, mem->total_active_file, mem->total_unevictable); - - mem->updated = 1; } - mem->usage_in_bytes_updated = 0; - if(mem->filename_usage_in_bytes) { - if(likely(!read_single_number_file(mem->filename_usage_in_bytes, &mem->usage_in_bytes))) - mem->usage_in_bytes_updated = 1; +memory_next: + + // read usage_in_bytes + if(likely(mem->filename_usage_in_bytes)) { + mem->updated_usage_in_bytes = !read_single_number_file(mem->filename_usage_in_bytes, &mem->usage_in_bytes); + if(unlikely(mem->updated_usage_in_bytes && mem->enabled_usage_in_bytes == CONFIG_ONDEMAND_ONDEMAND && mem->usage_in_bytes)) + mem->enabled_usage_in_bytes = CONFIG_ONDEMAND_YES; } - mem->msw_usage_in_bytes_updated = 0; - if(mem->filename_msw_usage_in_bytes) { - if(likely(!read_single_number_file(mem->filename_msw_usage_in_bytes, &mem->msw_usage_in_bytes))) - mem->msw_usage_in_bytes_updated = 1; + // read msw_usage_in_bytes + if(likely(mem->filename_msw_usage_in_bytes)) { + mem->updated_msw_usage_in_bytes = !read_single_number_file(mem->filename_msw_usage_in_bytes, &mem->msw_usage_in_bytes); + if(unlikely(mem->updated_msw_usage_in_bytes && mem->enabled_msw_usage_in_bytes == CONFIG_ONDEMAND_ONDEMAND && mem->msw_usage_in_bytes)) + mem->enabled_msw_usage_in_bytes = CONFIG_ONDEMAND_YES; } - mem->failcnt_updated = 0; - if(mem->filename_failcnt) { - if(likely(!read_single_number_file(mem->filename_failcnt, &mem->failcnt))) - mem->failcnt_updated = 1; + // read failcnt + if(likely(mem->filename_failcnt)) { + if(unlikely(mem->enabled_failcnt == CONFIG_ONDEMAND_ONDEMAND && mem->delay_counter_failcnt > 0)) { + mem->updated_failcnt = 0; + mem->delay_counter_failcnt--; + } + else { + mem->updated_failcnt = !read_single_number_file(mem->filename_failcnt, &mem->failcnt); + if(unlikely(mem->updated_failcnt && mem->enabled_failcnt == CONFIG_ONDEMAND_ONDEMAND)) { + if(unlikely(!mem->failcnt)) + mem->delay_counter_failcnt = cgroup_recheck_zero_mem_failcnt_every_iterations; + else + mem->enabled_failcnt = CONFIG_ONDEMAND_YES; + } + } } } -void cgroup_read(struct cgroup *cg) { +static inline void cgroup_read(struct cgroup *cg) { debug(D_CGROUP, "reading metrics for cgroups '%s'", cg->id); cgroup_read_cpuacct_stat(&cg->cpuacct_stat); @@ -603,7 +683,7 @@ void cgroup_read(struct cgroup *cg) { cgroup_read_blkio(&cg->io_queued); } -void read_all_cgroups(struct cgroup *root) { +static inline void read_all_cgroups(struct cgroup *root) { debug(D_CGROUP, "reading metrics for all cgroups"); struct cgroup *cg; @@ -618,108 +698,81 @@ void read_all_cgroups(struct cgroup *root) { #define CGROUP_CHARTID_LINE_MAX 1024 -void cgroup_get_chart_id(struct cgroup *cg) { - debug(D_CGROUP, "getting the name of cgroup '%s'", cg->id); +static inline char *cgroup_title_strdupz(const char *s) { + if(!s || !*s) s = "/"; + + if(*s == '/' && s[1] != '\0') s++; + + char *r = strdupz(s); + netdata_fix_chart_name(r); + + return r; +} + +static inline char *cgroup_chart_id_strdupz(const char *s) { + if(!s || !*s) s = "/"; + + if(*s == '/' && s[1] != '\0') s++; + + char *r = strdupz(s); + netdata_fix_chart_id(r); + + return r; +} + +static inline void cgroup_get_chart_name(struct cgroup *cg) { + debug(D_CGROUP, "looking for the name of cgroup '%s' with chart id '%s' and title '%s'", cg->id, cg->chart_id, cg->chart_title); pid_t cgroup_pid; char buffer[CGROUP_CHARTID_LINE_MAX + 1]; - snprintfz(buffer, CGROUP_CHARTID_LINE_MAX, "exec %s '%s'", - config_get("plugin:cgroups", "script to get cgroup names", PLUGINS_DIR "/cgroup-name.sh"), cg->chart_id); + snprintfz(buffer, CGROUP_CHARTID_LINE_MAX, "exec %s '%s'", cgroups_rename_script, cg->chart_id); debug(D_CGROUP, "executing command '%s' for cgroup '%s'", buffer, cg->id); FILE *fp = mypopen(buffer, &cgroup_pid); - if(!fp) { - error("CGROUP: Cannot popen(\"%s\", \"r\").", buffer); - return; - } - debug(D_CGROUP, "reading from command '%s' for cgroup '%s'", buffer, cg->id); - char *s = fgets(buffer, CGROUP_CHARTID_LINE_MAX, fp); - debug(D_CGROUP, "closing command for cgroup '%s'", cg->id); - mypclose(fp, cgroup_pid); - debug(D_CGROUP, "closed command for cgroup '%s'", cg->id); + if(fp) { + // debug(D_CGROUP, "reading from command '%s' for cgroup '%s'", buffer, cg->id); + char *s = fgets(buffer, CGROUP_CHARTID_LINE_MAX, fp); + // debug(D_CGROUP, "closing command for cgroup '%s'", cg->id); + mypclose(fp, cgroup_pid); + // debug(D_CGROUP, "closed command for cgroup '%s'", cg->id); - if(s && *s && *s != '\n') { - debug(D_CGROUP, "cgroup '%s' should be renamed to '%s'", cg->id, s); + if(s && *s && *s != '\n') { + debug(D_CGROUP, "cgroup '%s' should be renamed to '%s'", cg->id, s); - trim(s); + trim(s); - freez(cg->chart_title); - cg->chart_title = strdupz(s); - netdata_fix_chart_name(cg->chart_title); + freez(cg->chart_title); + cg->chart_title = cgroup_title_strdupz(s); - freez(cg->chart_id); - cg->chart_id = strdupz(s); - netdata_fix_chart_id(cg->chart_id); - cg->hash_chart = simple_hash(cg->chart_id); - - debug(D_CGROUP, "cgroup '%s' renamed to '%s' (title: '%s')", cg->id, cg->chart_id, cg->chart_title); + freez(cg->chart_id); + cg->chart_id = cgroup_chart_id_strdupz(s); + cg->hash_chart = simple_hash(cg->chart_id); + } } - else debug(D_CGROUP, "cgroup '%s' is not to be renamed (will be shown as '%s')", cg->id, cg->chart_id); + else + error("CGROUP: Cannot popen(\"%s\", \"r\").", buffer); } -struct cgroup *cgroup_add(const char *id) { - debug(D_CGROUP, "adding cgroup '%s'", id); +static inline struct cgroup *cgroup_add(const char *id) { + if(!id || !*id) id = "/"; + debug(D_CGROUP, "adding to list, cgroup with id '%s'", id); if(cgroup_root_count >= cgroup_root_max) { info("Maximum number of cgroups reached (%d). Not adding cgroup '%s'", cgroup_root_count, id); return NULL; } - int def = cgroup_enable_new_cgroups_detected_at_runtime; - const char *chart_id = id; - if(!*chart_id) { - chart_id = "/"; - - // disable by default the root cgroup - def = 0; - debug(D_CGROUP, "cgroup '%s' is the root container (by default %s)", id, (def)?"enabled":"disabled"); - } - else { - if(*chart_id == '/') chart_id++; - - size_t len = strlen(chart_id); - - // disable by default the parent cgroup - // for known cgroup managers - if(!strcmp(chart_id, "lxc") || - !strcmp(chart_id, "docker") || - !strcmp(chart_id, "libvirt") || - !strcmp(chart_id, "qemu") || - !strcmp(chart_id, "systemd") || - !strcmp(chart_id, "system.slice") || - !strcmp(chart_id, "machine.slice") || - !strcmp(chart_id, "init.scope") || - !strcmp(chart_id, "user") || - !strcmp(chart_id, "system") || - !strcmp(chart_id, "machine") || - // starts with them - (len > 6 && !strncmp(chart_id, "user/", 6)) || - (len > 11 && !strncmp(chart_id, "user.slice/", 11)) || - // ends with them - (len > 5 && !strncmp(&chart_id[len - 5], ".user", 5)) || - (len > 5 && !strncmp(&chart_id[len - 5], ".swap", 5)) || - (len > 6 && !strncmp(&chart_id[len - 6], ".slice", 6)) || - (len > 6 && !strncmp(&chart_id[len - 6], ".mount", 6)) || - (len > 8 && !strncmp(&chart_id[len - 8], ".session", 8)) || - (len > 8 && !strncmp(&chart_id[len - 8], ".service", 8)) || - (len > 10 && !strncmp(&chart_id[len - 10], ".partition", 10)) - ) { - def = 0; - debug(D_CGROUP, "cgroup '%s' is %s (by default)", id, (def)?"enabled":"disabled"); - } - } - + int def = simple_pattern_matches(enabled_cgroup_patterns, id)?cgroup_enable_new_cgroups_detected_at_runtime:0; struct cgroup *cg = callocz(1, sizeof(struct cgroup)); cg->id = strdupz(id); cg->hash = simple_hash(cg->id); - cg->chart_id = strdupz(chart_id); - netdata_fix_chart_id(cg->chart_id); - cg->hash_chart = simple_hash(cg->chart_id); + cg->chart_title = cgroup_title_strdupz(id); - cg->chart_title = strdupz(chart_id); + cg->chart_id = cgroup_chart_id_strdupz(id); + cg->hash_chart = simple_hash(cg->chart_id); if(!cgroup_root) cgroup_root = cg; @@ -732,15 +785,64 @@ struct cgroup *cgroup_add(const char *id) { cgroup_root_count++; - // fix the name by calling the external script - cgroup_get_chart_id(cg); + // fix the chart_id and title by calling the external script + if(simple_pattern_matches(enabled_cgroup_renames, cg->id)) { + + cgroup_get_chart_name(cg); + + debug(D_CGROUP, "cgroup '%s' renamed to '%s' (title: '%s')", cg->id, cg->chart_id, cg->chart_title); + } + else + debug(D_CGROUP, "cgroup '%s' will not be renamed - it matches the list of disabled cgroup renames (will be shown as '%s')", cg->id, cg->chart_id); + + int user_configurable = 1; + + // check if this cgroup should be a systemd service + if(cgroup_enable_systemd_services) { + if(simple_pattern_matches(systemd_services_cgroups, cg->id) || + simple_pattern_matches(systemd_services_cgroups, cg->chart_id)) { + debug(D_CGROUP, "cgroup '%s' with chart id '%s' (title: '%s') matches systemd services cgroups", cg->id, cg->chart_id, cg->chart_title); + + char buffer[CGROUP_CHARTID_LINE_MAX + 1]; + cg->options |= CGROUP_OPTIONS_SYSTEM_SLICE_SERVICE; + + strncpy(buffer, cg->id, CGROUP_CHARTID_LINE_MAX); + char *s = buffer; + + //freez(cg->chart_id); + //cg->chart_id = cgroup_chart_id_strdupz(s); + //cg->hash_chart = simple_hash(cg->chart_id); + + // skip to the last slash + size_t len = strlen(s); + while(len--) if(unlikely(s[len] == '/')) break; + if(len) s = &s[len + 1]; + + // remove extension + len = strlen(s); + while(len--) if(unlikely(s[len] == '.')) break; + if(len) s[len] = '\0'; - debug(D_CGROUP, "adding cgroup '%s' with chart id '%s'", id, chart_id); + freez(cg->chart_title); + cg->chart_title = cgroup_title_strdupz(s); - char option[FILENAME_MAX + 1]; - snprintfz(option, FILENAME_MAX, "enable cgroup %s", cg->chart_title); - cg->enabled = config_get_boolean("plugin:cgroups", option, def); + cg->enabled = 1; + user_configurable = 0; + debug(D_CGROUP, "cgroup '%s' renamed to '%s' (title: '%s')", cg->id, cg->chart_id, cg->chart_title); + } + else + debug(D_CGROUP, "cgroup '%s' with chart id '%s' (title: '%s') does not match systemd services groups", cg->id, cg->chart_id, cg->chart_title); + } + + if(user_configurable) { + // allow the user to enable/disable this individualy + char option[FILENAME_MAX + 1]; + snprintfz(option, FILENAME_MAX, "enable cgroup %s", cg->chart_title); + cg->enabled = (char) config_get_boolean("plugin:cgroups", option, def); + } + + // detect duplicate cgroups if(cg->enabled) { struct cgroup *t; for (t = cgroup_root; t; t = t->next) { @@ -767,36 +869,45 @@ struct cgroup *cgroup_add(const char *id) { } } - debug(D_CGROUP, "Added cgroup '%s' with chart id '%s' and title '%s' as %s (default was %s)", cg->id, cg->chart_id, cg->chart_title, (cg->enabled)?"enabled":"disabled", (def)?"enabled":"disabled"); + debug(D_CGROUP, "ADDED CGROUP: '%s' with chart id '%s' and title '%s' as %s (default was %s)", cg->id, cg->chart_id, cg->chart_title, (cg->enabled)?"enabled":"disabled", (def)?"enabled":"disabled"); return cg; } -void cgroup_free(struct cgroup *cg) { +static inline void cgroup_free(struct cgroup *cg) { debug(D_CGROUP, "Removing cgroup '%s' with chart id '%s' (was %s and %s)", cg->id, cg->chart_id, (cg->enabled)?"enabled":"disabled", (cg->available)?"available":"not available"); freez(cg->cpuacct_usage.cpu_percpu); freez(cg->cpuacct_stat.filename); freez(cg->cpuacct_usage.filename); - freez(cg->memory.filename); + + arl_free(cg->memory.arl_base); + freez(cg->memory.filename_detailed); + freez(cg->memory.filename_failcnt); + freez(cg->memory.filename_usage_in_bytes); + freez(cg->memory.filename_msw_usage_in_bytes); + freez(cg->io_service_bytes.filename); freez(cg->io_serviced.filename); + freez(cg->throttle_io_service_bytes.filename); freez(cg->throttle_io_serviced.filename); + freez(cg->io_merged.filename); freez(cg->io_queued.filename); freez(cg->id); freez(cg->chart_id); freez(cg->chart_title); + freez(cg); cgroup_root_count--; } // find if a given cgroup exists -struct cgroup *cgroup_find(const char *id) { +static inline struct cgroup *cgroup_find(const char *id) { debug(D_CGROUP, "searching for cgroup '%s'", id); uint32_t hash = simple_hash(id); @@ -807,7 +918,7 @@ struct cgroup *cgroup_find(const char *id) { break; } - debug(D_CGROUP, "cgroup_find('%s') %s", id, (cg)?"found":"not found"); + debug(D_CGROUP, "cgroup '%s' %s in memory", id, (cg)?"found":"not found"); return cg; } @@ -815,7 +926,7 @@ struct cgroup *cgroup_find(const char *id) { // detect running cgroups // callback for find_file_in_subdirs() -void found_subdir_in_dir(const char *dir) { +static inline void found_subdir_in_dir(const char *dir) { debug(D_CGROUP, "examining cgroup dir '%s'", dir); struct cgroup *cg = cgroup_find(dir); @@ -833,21 +944,24 @@ void found_subdir_in_dir(const char *dir) { return; } } - debug(D_CGROUP, "will add dir '%s' as cgroup", dir); + // debug(D_CGROUP, "will add dir '%s' as cgroup", dir); cg = cgroup_add(dir); } if(cg) cg->available = 1; } -int find_dir_in_subdirs(const char *base, const char *this, void (*callback)(const char *)) { - debug(D_CGROUP, "searching for directories in '%s'", base); +static inline int find_dir_in_subdirs(const char *base, const char *this, void (*callback)(const char *)) { + if(!this) this = base; + debug(D_CGROUP, "searching for directories in '%s' (base '%s')", this?this:"", base); + + size_t dirlen = strlen(this), baselen = strlen(base); int ret = -1; int enabled = -1; - if(!this) this = base; - size_t dirlen = strlen(this), baselen = strlen(base); + const char *relative_path = &this[baselen]; + if(!*relative_path) relative_path = "/"; DIR *dir = opendir(this); if(!dir) { @@ -867,13 +981,13 @@ int find_dir_in_subdirs(const char *base, const char *this, void (*callback)(con )) continue; - debug(D_CGROUP, "examining '%s/%s'", this, de->d_name); - if(de->d_type == DT_DIR) { if(enabled == -1) { const char *r = relative_path; if(*r == '\0') r = "/"; - else if (*r == '/') r++; + + // do not decent in directories we are not interested + int def = simple_pattern_matches(enabled_cgroup_paths, r); // we check for this option here // so that the config will not have settings @@ -881,7 +995,7 @@ int find_dir_in_subdirs(const char *base, const char *this, void (*callback)(con char option[FILENAME_MAX + 1]; snprintfz(option, FILENAME_MAX, "search for cgroups under %s", r); option[FILENAME_MAX] = '\0'; - enabled = config_get_boolean("plugin:cgroups", option, 1); + enabled = config_get_boolean("plugin:cgroups", option, def); } if(enabled) { @@ -900,7 +1014,7 @@ int find_dir_in_subdirs(const char *base, const char *this, void (*callback)(con return ret; } -void mark_all_cgroups_as_not_available() { +static inline void mark_all_cgroups_as_not_available() { debug(D_CGROUP, "marking all cgroups as not available"); struct cgroup *cg; @@ -911,7 +1025,7 @@ void mark_all_cgroups_as_not_available() { } } -void cleanup_all_cgroups() { +static inline void cleanup_all_cgroups() { struct cgroup *cg = cgroup_root, *last = NULL; for(; cg ;) { @@ -948,36 +1062,45 @@ void cleanup_all_cgroups() { } } -void find_all_cgroups() { +static inline void find_all_cgroups() { debug(D_CGROUP, "searching for cgroups"); mark_all_cgroups_as_not_available(); if(cgroup_enable_cpuacct_stat || cgroup_enable_cpuacct_usage) { - if (find_dir_in_subdirs(cgroup_cpuacct_base, NULL, found_subdir_in_dir) == -1) { - cgroup_enable_cpuacct_stat = cgroup_enable_cpuacct_usage = 0; - error("disabled cgroup cpu statistics."); + if(find_dir_in_subdirs(cgroup_cpuacct_base, NULL, found_subdir_in_dir) == -1) { + cgroup_enable_cpuacct_stat = + cgroup_enable_cpuacct_usage = CONFIG_ONDEMAND_NO; + error("disabled CGROUP cpu statistics."); } } - if(cgroup_enable_blkio) { - if (find_dir_in_subdirs(cgroup_blkio_base, NULL, found_subdir_in_dir) == -1) { - cgroup_enable_blkio = 0; - error("disabled cgroup blkio statistics."); + if(cgroup_enable_blkio_io || cgroup_enable_blkio_ops || cgroup_enable_blkio_throttle_io || cgroup_enable_blkio_throttle_ops || cgroup_enable_blkio_merged_ops || cgroup_enable_blkio_queued_ops) { + if(find_dir_in_subdirs(cgroup_blkio_base, NULL, found_subdir_in_dir) == -1) { + cgroup_enable_blkio_io = + cgroup_enable_blkio_ops = + cgroup_enable_blkio_throttle_io = + cgroup_enable_blkio_throttle_ops = + cgroup_enable_blkio_merged_ops = + cgroup_enable_blkio_queued_ops = CONFIG_ONDEMAND_NO; + error("disabled CGROUP blkio statistics."); } } - if(cgroup_enable_memory) { + if(cgroup_enable_memory || cgroup_enable_detailed_memory || cgroup_enable_swap || cgroup_enable_memory_failcnt) { if(find_dir_in_subdirs(cgroup_memory_base, NULL, found_subdir_in_dir) == -1) { - cgroup_enable_memory = 0; - error("disabled cgroup memory statistics."); + cgroup_enable_memory = + cgroup_enable_detailed_memory = + cgroup_enable_swap = + cgroup_enable_memory_failcnt = CONFIG_ONDEMAND_NO; + error("disabled CGROUP memory statistics."); } } - if(cgroup_enable_devices) { + if(cgroup_search_in_devices) { if(find_dir_in_subdirs(cgroup_devices_base, NULL, found_subdir_in_dir) == -1) { - cgroup_enable_devices = 0; - error("disabled cgroup devices statistics."); + cgroup_search_in_devices = 0; + error("disabled CGROUP devices statistics."); } } @@ -997,100 +1120,136 @@ void find_all_cgroups() { // check for newly added cgroups // and update the filenames they read char filename[FILENAME_MAX + 1]; - if(cgroup_enable_cpuacct_stat && !cg->cpuacct_stat.filename) { + if(unlikely(cgroup_enable_cpuacct_stat && !cg->cpuacct_stat.filename)) { snprintfz(filename, FILENAME_MAX, "%s%s/cpuacct.stat", cgroup_cpuacct_base, cg->id); - if(stat(filename, &buf) != -1) { + if(likely(stat(filename, &buf) != -1)) { cg->cpuacct_stat.filename = strdupz(filename); + cg->cpuacct_stat.enabled = cgroup_enable_cpuacct_stat; debug(D_CGROUP, "cpuacct.stat filename for cgroup '%s': '%s'", cg->id, cg->cpuacct_stat.filename); } - else debug(D_CGROUP, "cpuacct.stat file for cgroup '%s': '%s' does not exist.", cg->id, filename); + else + debug(D_CGROUP, "cpuacct.stat file for cgroup '%s': '%s' does not exist.", cg->id, filename); } - if(cgroup_enable_cpuacct_usage && !cg->cpuacct_usage.filename) { + + if(unlikely(cgroup_enable_cpuacct_usage && !cg->cpuacct_usage.filename && !(cg->options & CGROUP_OPTIONS_SYSTEM_SLICE_SERVICE))) { snprintfz(filename, FILENAME_MAX, "%s%s/cpuacct.usage_percpu", cgroup_cpuacct_base, cg->id); - if(stat(filename, &buf) != -1) { + if(likely(stat(filename, &buf) != -1)) { cg->cpuacct_usage.filename = strdupz(filename); + cg->cpuacct_usage.enabled = cgroup_enable_cpuacct_usage; debug(D_CGROUP, "cpuacct.usage_percpu filename for cgroup '%s': '%s'", cg->id, cg->cpuacct_usage.filename); } - else debug(D_CGROUP, "cpuacct.usage_percpu file for cgroup '%s': '%s' does not exist.", cg->id, filename); + else + debug(D_CGROUP, "cpuacct.usage_percpu file for cgroup '%s': '%s' does not exist.", cg->id, filename); } - if(cgroup_enable_memory && !cg->memory.filename) { + + if(unlikely((cgroup_enable_detailed_memory || cgroup_used_memory_without_cache) && !cg->memory.filename_detailed && (cgroup_used_memory_without_cache || cgroup_enable_systemd_services_detailed_memory || !(cg->options & CGROUP_OPTIONS_SYSTEM_SLICE_SERVICE)))) { snprintfz(filename, FILENAME_MAX, "%s%s/memory.stat", cgroup_memory_base, cg->id); - if(stat(filename, &buf) != -1) { - cg->memory.filename = strdupz(filename); - debug(D_CGROUP, "memory.stat filename for cgroup '%s': '%s'", cg->id, cg->memory.filename); + if(likely(stat(filename, &buf) != -1)) { + cg->memory.filename_detailed = strdupz(filename); + cg->memory.enabled_detailed = (cgroup_enable_detailed_memory == CONFIG_ONDEMAND_YES)?CONFIG_ONDEMAND_YES:CONFIG_ONDEMAND_ONDEMAND; + debug(D_CGROUP, "memory.stat filename for cgroup '%s': '%s'", cg->id, cg->memory.filename_detailed); } - else debug(D_CGROUP, "memory.stat file for cgroup '%s': '%s' does not exist.", cg->id, filename); + else + debug(D_CGROUP, "memory.stat file for cgroup '%s': '%s' does not exist.", cg->id, filename); + } + if(unlikely(cgroup_enable_memory && !cg->memory.filename_usage_in_bytes)) { snprintfz(filename, FILENAME_MAX, "%s%s/memory.usage_in_bytes", cgroup_memory_base, cg->id); - if(stat(filename, &buf) != -1) { + if(likely(stat(filename, &buf) != -1)) { cg->memory.filename_usage_in_bytes = strdupz(filename); + cg->memory.enabled_usage_in_bytes = cgroup_enable_memory; debug(D_CGROUP, "memory.usage_in_bytes filename for cgroup '%s': '%s'", cg->id, cg->memory.filename_usage_in_bytes); } - else debug(D_CGROUP, "memory.usage_in_bytes file for cgroup '%s': '%s' does not exist.", cg->id, filename); + else + debug(D_CGROUP, "memory.usage_in_bytes file for cgroup '%s': '%s' does not exist.", cg->id, filename); + } + if(unlikely(cgroup_enable_swap && !cg->memory.filename_msw_usage_in_bytes)) { snprintfz(filename, FILENAME_MAX, "%s%s/memory.msw_usage_in_bytes", cgroup_memory_base, cg->id); - if(stat(filename, &buf) != -1) { + if(likely(stat(filename, &buf) != -1)) { cg->memory.filename_msw_usage_in_bytes = strdupz(filename); + cg->memory.enabled_msw_usage_in_bytes = cgroup_enable_swap; debug(D_CGROUP, "memory.msw_usage_in_bytes filename for cgroup '%s': '%s'", cg->id, cg->memory.filename_msw_usage_in_bytes); } - else debug(D_CGROUP, "memory.msw_usage_in_bytes file for cgroup '%s': '%s' does not exist.", cg->id, filename); + else + debug(D_CGROUP, "memory.msw_usage_in_bytes file for cgroup '%s': '%s' does not exist.", cg->id, filename); + } + if(unlikely(cgroup_enable_memory_failcnt && !cg->memory.filename_failcnt)) { snprintfz(filename, FILENAME_MAX, "%s%s/memory.failcnt", cgroup_memory_base, cg->id); - if(stat(filename, &buf) != -1) { + if(likely(stat(filename, &buf) != -1)) { cg->memory.filename_failcnt = strdupz(filename); + cg->memory.enabled_failcnt = cgroup_enable_memory_failcnt; debug(D_CGROUP, "memory.failcnt filename for cgroup '%s': '%s'", cg->id, cg->memory.filename_failcnt); } - else debug(D_CGROUP, "memory.failcnt file for cgroup '%s': '%s' does not exist.", cg->id, filename); - } - if(cgroup_enable_blkio) { - if(!cg->io_service_bytes.filename) { - snprintfz(filename, FILENAME_MAX, "%s%s/blkio.io_service_bytes", cgroup_blkio_base, cg->id); - if(stat(filename, &buf) != -1) { - cg->io_service_bytes.filename = strdupz(filename); - debug(D_CGROUP, "io_service_bytes filename for cgroup '%s': '%s'", cg->id, cg->io_service_bytes.filename); - } - else debug(D_CGROUP, "io_service_bytes file for cgroup '%s': '%s' does not exist.", cg->id, filename); + else + debug(D_CGROUP, "memory.failcnt file for cgroup '%s': '%s' does not exist.", cg->id, filename); + } + + if(unlikely(cgroup_enable_blkio_io && !cg->io_service_bytes.filename)) { + snprintfz(filename, FILENAME_MAX, "%s%s/blkio.io_service_bytes", cgroup_blkio_base, cg->id); + if(likely(stat(filename, &buf) != -1)) { + cg->io_service_bytes.filename = strdupz(filename); + cg->io_service_bytes.enabled = cgroup_enable_blkio_io; + debug(D_CGROUP, "io_service_bytes filename for cgroup '%s': '%s'", cg->id, cg->io_service_bytes.filename); } - if(!cg->io_serviced.filename) { - snprintfz(filename, FILENAME_MAX, "%s%s/blkio.io_serviced", cgroup_blkio_base, cg->id); - if(stat(filename, &buf) != -1) { - cg->io_serviced.filename = strdupz(filename); - debug(D_CGROUP, "io_serviced filename for cgroup '%s': '%s'", cg->id, cg->io_serviced.filename); - } - else debug(D_CGROUP, "io_serviced file for cgroup '%s': '%s' does not exist.", cg->id, filename); + else + debug(D_CGROUP, "io_service_bytes file for cgroup '%s': '%s' does not exist.", cg->id, filename); + } + + if(unlikely(cgroup_enable_blkio_ops && !cg->io_serviced.filename)) { + snprintfz(filename, FILENAME_MAX, "%s%s/blkio.io_serviced", cgroup_blkio_base, cg->id); + if(likely(stat(filename, &buf) != -1)) { + cg->io_serviced.filename = strdupz(filename); + cg->io_serviced.enabled = cgroup_enable_blkio_ops; + debug(D_CGROUP, "io_serviced filename for cgroup '%s': '%s'", cg->id, cg->io_serviced.filename); } - if(!cg->throttle_io_service_bytes.filename) { - snprintfz(filename, FILENAME_MAX, "%s%s/blkio.throttle.io_service_bytes", cgroup_blkio_base, cg->id); - if(stat(filename, &buf) != -1) { - cg->throttle_io_service_bytes.filename = strdupz(filename); - debug(D_CGROUP, "throttle_io_service_bytes filename for cgroup '%s': '%s'", cg->id, cg->throttle_io_service_bytes.filename); - } - else debug(D_CGROUP, "throttle_io_service_bytes file for cgroup '%s': '%s' does not exist.", cg->id, filename); + else + debug(D_CGROUP, "io_serviced file for cgroup '%s': '%s' does not exist.", cg->id, filename); + } + + if(unlikely(cgroup_enable_blkio_throttle_io && !cg->throttle_io_service_bytes.filename)) { + snprintfz(filename, FILENAME_MAX, "%s%s/blkio.throttle.io_service_bytes", cgroup_blkio_base, cg->id); + if(likely(stat(filename, &buf) != -1)) { + cg->throttle_io_service_bytes.filename = strdupz(filename); + cg->throttle_io_service_bytes.enabled = cgroup_enable_blkio_throttle_io; + debug(D_CGROUP, "throttle_io_service_bytes filename for cgroup '%s': '%s'", cg->id, cg->throttle_io_service_bytes.filename); } - if(!cg->throttle_io_serviced.filename) { - snprintfz(filename, FILENAME_MAX, "%s%s/blkio.throttle.io_serviced", cgroup_blkio_base, cg->id); - if(stat(filename, &buf) != -1) { - cg->throttle_io_serviced.filename = strdupz(filename); - debug(D_CGROUP, "throttle_io_serviced filename for cgroup '%s': '%s'", cg->id, cg->throttle_io_serviced.filename); - } - else debug(D_CGROUP, "throttle_io_serviced file for cgroup '%s': '%s' does not exist.", cg->id, filename); + else + debug(D_CGROUP, "throttle_io_service_bytes file for cgroup '%s': '%s' does not exist.", cg->id, filename); + } + + if(unlikely(cgroup_enable_blkio_throttle_ops && !cg->throttle_io_serviced.filename)) { + snprintfz(filename, FILENAME_MAX, "%s%s/blkio.throttle.io_serviced", cgroup_blkio_base, cg->id); + if(likely(stat(filename, &buf) != -1)) { + cg->throttle_io_serviced.filename = strdupz(filename); + cg->throttle_io_serviced.enabled = cgroup_enable_blkio_throttle_ops; + debug(D_CGROUP, "throttle_io_serviced filename for cgroup '%s': '%s'", cg->id, cg->throttle_io_serviced.filename); } - if(!cg->io_merged.filename) { - snprintfz(filename, FILENAME_MAX, "%s%s/blkio.io_merged", cgroup_blkio_base, cg->id); - if(stat(filename, &buf) != -1) { - cg->io_merged.filename = strdupz(filename); - debug(D_CGROUP, "io_merged filename for cgroup '%s': '%s'", cg->id, cg->io_merged.filename); - } - else debug(D_CGROUP, "io_merged file for cgroup '%s': '%s' does not exist.", cg->id, filename); + else + debug(D_CGROUP, "throttle_io_serviced file for cgroup '%s': '%s' does not exist.", cg->id, filename); + } + + if(unlikely(cgroup_enable_blkio_merged_ops && !cg->io_merged.filename)) { + snprintfz(filename, FILENAME_MAX, "%s%s/blkio.io_merged", cgroup_blkio_base, cg->id); + if(likely(stat(filename, &buf) != -1)) { + cg->io_merged.filename = strdupz(filename); + cg->io_merged.enabled = cgroup_enable_blkio_merged_ops; + debug(D_CGROUP, "io_merged filename for cgroup '%s': '%s'", cg->id, cg->io_merged.filename); } - if(!cg->io_queued.filename) { - snprintfz(filename, FILENAME_MAX, "%s%s/blkio.io_queued", cgroup_blkio_base, cg->id); - if(stat(filename, &buf) != -1) { - cg->io_queued.filename = strdupz(filename); - debug(D_CGROUP, "io_queued filename for cgroup '%s': '%s'", cg->id, cg->io_queued.filename); - } - else debug(D_CGROUP, "io_queued file for cgroup '%s': '%s' does not exist.", cg->id, filename); + else + debug(D_CGROUP, "io_merged file for cgroup '%s': '%s' does not exist.", cg->id, filename); + } + + if(unlikely(cgroup_enable_blkio_queued_ops && !cg->io_queued.filename)) { + snprintfz(filename, FILENAME_MAX, "%s%s/blkio.io_queued", cgroup_blkio_base, cg->id); + if(likely(stat(filename, &buf) != -1)) { + cg->io_queued.filename = strdupz(filename); + cg->io_queued.enabled = cgroup_enable_blkio_queued_ops; + debug(D_CGROUP, "io_queued filename for cgroup '%s': '%s'", cg->id, cg->io_queued.filename); } + else + debug(D_CGROUP, "io_queued file for cgroup '%s': '%s' does not exist.", cg->id, filename); } } @@ -1103,313 +1262,805 @@ void find_all_cgroups() { #define CHART_TITLE_MAX 300 +void update_services_charts(int update_every, + int do_cpu, + int do_mem_usage, + int do_mem_detailed, + int do_mem_failcnt, + int do_swap_usage, + int do_io, + int do_io_ops, + int do_throttle_io, + int do_throttle_ops, + int do_queued_ops, + int do_merged_ops +) { + static RRDSET + *st_cpu = NULL, + *st_mem_usage = NULL, + *st_mem_failcnt = NULL, + *st_swap_usage = NULL, + + *st_mem_detailed_cache = NULL, + *st_mem_detailed_rss = NULL, + *st_mem_detailed_mapped = NULL, + *st_mem_detailed_writeback = NULL, + *st_mem_detailed_pgfault = NULL, + *st_mem_detailed_pgmajfault = NULL, + *st_mem_detailed_pgpgin = NULL, + *st_mem_detailed_pgpgout = NULL, + + *st_io_read = NULL, + *st_io_serviced_read = NULL, + *st_throttle_io_read = NULL, + *st_throttle_ops_read = NULL, + *st_queued_ops_read = NULL, + *st_merged_ops_read = NULL, + + *st_io_write = NULL, + *st_io_serviced_write = NULL, + *st_throttle_io_write = NULL, + *st_throttle_ops_write = NULL, + *st_queued_ops_write = NULL, + *st_merged_ops_write = NULL; + + // create the charts + + if(likely(do_cpu)) { + if(unlikely(!st_cpu)) { + char title[CHART_TITLE_MAX + 1]; + + st_cpu = rrdset_find_bytype("services", "cpu"); + if(likely(!st_cpu)) { + snprintfz(title, CHART_TITLE_MAX, "Systemd Services CPU utilization (%d%% = %d core%s)", (processors * 100), processors, (processors > 1) ? "s" : ""); + st_cpu = rrdset_create("services", "cpu", NULL, "cpu", "services.cpu", title, "%", CHART_PRIORITY_SYSTEMD_SERVICES, update_every, RRDSET_TYPE_STACKED); + } + } + else + rrdset_next(st_cpu); + } + + if(likely(do_mem_usage)) { + if(unlikely(!st_mem_usage)) { + st_mem_usage = rrdset_find_bytype("services", "mem_usage"); + if(likely(!st_mem_usage)) + st_mem_usage = rrdset_create("services", "mem_usage", NULL, "mem", "services.mem_usage", (cgroup_used_memory_without_cache)?"Systemd Services Used Memory without Cache":"Systemd Services Used Memory", "MB", CHART_PRIORITY_SYSTEMD_SERVICES + 10, update_every, RRDSET_TYPE_STACKED); + } + else + rrdset_next(st_mem_usage); + } + + if(likely(do_mem_detailed)) { + if(unlikely(!st_mem_detailed_rss)) { + st_mem_detailed_rss = rrdset_find_bytype("services", "mem_rss"); + if(likely(!st_mem_detailed_rss)) + st_mem_detailed_rss = rrdset_create("services", "mem_rss", NULL, "mem", "services.mem_rss", "Systemd Services RSS Memory", "MB", CHART_PRIORITY_SYSTEMD_SERVICES + 20, update_every, RRDSET_TYPE_STACKED); + } + else + rrdset_next(st_mem_detailed_rss); + + if(unlikely(!st_mem_detailed_mapped)) { + st_mem_detailed_mapped = rrdset_find_bytype("services", "mem_mapped"); + if(likely(!st_mem_detailed_mapped)) + st_mem_detailed_mapped = rrdset_create("services", "mem_mapped", NULL, "mem", "services.mem_mapped", "Systemd Services Mapped Memory", "MB", CHART_PRIORITY_SYSTEMD_SERVICES + 30, update_every, RRDSET_TYPE_STACKED); + } + else + rrdset_next(st_mem_detailed_mapped); + + if(unlikely(!st_mem_detailed_cache)) { + st_mem_detailed_cache = rrdset_find_bytype("services", "mem_cache"); + if(likely(!st_mem_detailed_cache)) + st_mem_detailed_cache = rrdset_create("services", "mem_cache", NULL, "mem", "services.mem_cache", "Systemd Services Cache Memory", "MB", CHART_PRIORITY_SYSTEMD_SERVICES + 40, update_every, RRDSET_TYPE_STACKED); + } + else + rrdset_next(st_mem_detailed_cache); + + if(unlikely(!st_mem_detailed_writeback)) { + st_mem_detailed_writeback = rrdset_find_bytype("services", "mem_writeback"); + if(likely(!st_mem_detailed_writeback)) + st_mem_detailed_writeback = rrdset_create("services", "mem_writeback", NULL, "mem", "services.mem_writeback", "Systemd Services Writeback Memory", "MB", CHART_PRIORITY_SYSTEMD_SERVICES + 50, update_every, RRDSET_TYPE_STACKED); + } + else + rrdset_next(st_mem_detailed_writeback); + + if(unlikely(!st_mem_detailed_pgfault)) { + st_mem_detailed_pgfault = rrdset_find_bytype("services", "mem_pgfault"); + if(likely(!st_mem_detailed_pgfault)) + st_mem_detailed_pgfault = rrdset_create("services", "mem_pgfault", NULL, "mem", "services.mem_pgfault", "Systemd Services Memory Minor Page Faults", "MB/s", CHART_PRIORITY_SYSTEMD_SERVICES + 60, update_every, RRDSET_TYPE_STACKED); + } + else + rrdset_next(st_mem_detailed_pgfault); + + if(unlikely(!st_mem_detailed_pgmajfault)) { + st_mem_detailed_pgmajfault = rrdset_find_bytype("services", "mem_pgmajfault"); + if(likely(!st_mem_detailed_pgmajfault)) + st_mem_detailed_pgmajfault = rrdset_create("services", "mem_pgmajfault", NULL, "mem", "services.mem_pgmajfault", "Systemd Services Memory Major Page Faults", "MB/s", CHART_PRIORITY_SYSTEMD_SERVICES + 70, update_every, RRDSET_TYPE_STACKED); + } + else + rrdset_next(st_mem_detailed_pgmajfault); + + if(unlikely(!st_mem_detailed_pgpgin)) { + st_mem_detailed_pgpgin = rrdset_find_bytype("services", "mem_pgpgin"); + if(likely(!st_mem_detailed_pgpgin)) + st_mem_detailed_pgpgin = rrdset_create("services", "mem_pgpgin", NULL, "mem", "services.mem_pgpgin", "Systemd Services Memory Charging Activity", "MB/s", CHART_PRIORITY_SYSTEMD_SERVICES + 80, update_every, RRDSET_TYPE_STACKED); + } + else + rrdset_next(st_mem_detailed_pgpgin); + + if(unlikely(!st_mem_detailed_pgpgout)) { + st_mem_detailed_pgpgout = rrdset_find_bytype("services", "mem_pgpgout"); + if(likely(!st_mem_detailed_pgpgout)) + st_mem_detailed_pgpgout = rrdset_create("services", "mem_pgpgout", NULL, "mem", "services.mem_pgpgout", "Systemd Services Memory Uncharging Activity", "MB/s", CHART_PRIORITY_SYSTEMD_SERVICES + 90, update_every, RRDSET_TYPE_STACKED); + } + else + rrdset_next(st_mem_detailed_pgpgout); + } + + if(likely(do_mem_failcnt)) { + if(unlikely(!st_mem_failcnt)) { + st_mem_failcnt = rrdset_find_bytype("services", "mem_failcnt"); + if(likely(!st_mem_failcnt)) + st_mem_failcnt = rrdset_create("services", "mem_failcnt", NULL, "mem", "services.mem_failcnt", "Systemd Services Memory Limit Failures", "MB", CHART_PRIORITY_SYSTEMD_SERVICES + 110, update_every, RRDSET_TYPE_STACKED); + } + else + rrdset_next(st_mem_failcnt); + } + + if(likely(do_swap_usage)) { + if(unlikely(!st_swap_usage)) { + st_swap_usage = rrdset_find_bytype("services", "swap_usage"); + if(likely(!st_swap_usage)) + st_swap_usage = rrdset_create("services", "swap_usage", NULL, "swap", "services.swap_usage", "Systemd Services Swap Memory Used", "MB", CHART_PRIORITY_SYSTEMD_SERVICES + 100, update_every, RRDSET_TYPE_STACKED); + } + else + rrdset_next(st_swap_usage); + } + + if(likely(do_io)) { + if(unlikely(!st_io_read)) { + st_io_read = rrdset_find_bytype("services", "io_read"); + if(likely(!st_io_read)) + st_io_read = rrdset_create("services", "io_read", NULL, "disk", "services.io_read", "Systemd Services Disk Read Bandwidth", "KB/s", CHART_PRIORITY_SYSTEMD_SERVICES + 120, update_every, RRDSET_TYPE_STACKED); + } + else + rrdset_next(st_io_read); + + if(unlikely(!st_io_write)) { + st_io_write = rrdset_find_bytype("services", "io_write"); + if(likely(!st_io_write)) + st_io_write = rrdset_create("services", "io_write", NULL, "disk", "services.io_write", "Systemd Services Disk Write Bandwidth", "KB/s", CHART_PRIORITY_SYSTEMD_SERVICES + 130, update_every, RRDSET_TYPE_STACKED); + } + else + rrdset_next(st_io_write); + } + + if(likely(do_io_ops)) { + if(unlikely(!st_io_serviced_read)) { + st_io_serviced_read = rrdset_find_bytype("services", "io_ops_read"); + if(likely(!st_io_serviced_read)) + st_io_serviced_read = rrdset_create("services", "io_ops_read", NULL, "disk", "services.io_ops_read", "Systemd Services Disk Read Operations", "operations/s", CHART_PRIORITY_SYSTEMD_SERVICES + 140, update_every, RRDSET_TYPE_STACKED); + } + else + rrdset_next(st_io_serviced_read); + + if(unlikely(!st_io_serviced_write)) { + st_io_serviced_write = rrdset_find_bytype("services", "io_ops_write"); + if(likely(!st_io_serviced_write)) + st_io_serviced_write = rrdset_create("services", "io_ops_write", NULL, "disk", "services.io_ops_write", "Systemd Services Disk Write Operations", "operations/s", CHART_PRIORITY_SYSTEMD_SERVICES + 150, update_every, RRDSET_TYPE_STACKED); + } + else + rrdset_next(st_io_serviced_write); + } + + if(likely(do_throttle_io)) { + if(unlikely(!st_throttle_io_read)) { + st_throttle_io_read = rrdset_find_bytype("services", "throttle_io_read"); + if(likely(!st_throttle_io_read)) + st_throttle_io_read = rrdset_create("services", "throttle_io_read", NULL, "disk", "services.throttle_io_read", "Systemd Services Throttle Disk Read Bandwidth", "KB/s", CHART_PRIORITY_SYSTEMD_SERVICES + 160, update_every, RRDSET_TYPE_STACKED); + } + else + rrdset_next(st_throttle_io_read); + + if(unlikely(!st_throttle_io_write)) { + st_throttle_io_write = rrdset_find_bytype("services", "throttle_io_write"); + if(likely(!st_throttle_io_write)) + st_throttle_io_write = rrdset_create("services", "throttle_io_write", NULL, "disk", "services.throttle_io_write", "Systemd Services Throttle Disk Write Bandwidth", "KB/s", CHART_PRIORITY_SYSTEMD_SERVICES + 170, update_every, RRDSET_TYPE_STACKED); + } + else + rrdset_next(st_throttle_io_write); + } + + if(likely(do_throttle_ops)) { + if(unlikely(!st_throttle_ops_read)) { + st_throttle_ops_read = rrdset_find_bytype("services", "throttle_io_ops_read"); + if(likely(!st_throttle_ops_read)) + st_throttle_ops_read = rrdset_create("services", "throttle_io_ops_read", NULL, "disk", "services.throttle_io_ops_read", "Systemd Services Throttle Disk Read Operations", "operations/s", CHART_PRIORITY_SYSTEMD_SERVICES + 180, update_every, RRDSET_TYPE_STACKED); + } + else + rrdset_next(st_throttle_ops_read); + + if(unlikely(!st_throttle_ops_write)) { + st_throttle_ops_write = rrdset_find_bytype("services", "throttle_io_ops_write"); + if(likely(!st_throttle_ops_write)) + st_throttle_ops_write = rrdset_create("services", "throttle_io_ops_write", NULL, "disk", "services.throttle_io_ops_write", "Systemd Services Throttle Disk Write Operations", "operations/s", CHART_PRIORITY_SYSTEMD_SERVICES + 190, update_every, RRDSET_TYPE_STACKED); + } + else + rrdset_next(st_throttle_ops_write); + } + + if(likely(do_queued_ops)) { + if(unlikely(!st_queued_ops_read)) { + st_queued_ops_read = rrdset_find_bytype("services", "queued_io_ops_read"); + if(likely(!st_queued_ops_read)) + st_queued_ops_read = rrdset_create("services", "queued_io_ops_read", NULL, "disk", "services.queued_io_ops_read", "Systemd Services Queued Disk Read Operations", "operations/s", CHART_PRIORITY_SYSTEMD_SERVICES + 200, update_every, RRDSET_TYPE_STACKED); + } + else + rrdset_next(st_queued_ops_read); + + if(unlikely(!st_queued_ops_write)) { + st_queued_ops_write = rrdset_find_bytype("services", "queued_io_ops_write"); + if(likely(!st_queued_ops_write)) + st_queued_ops_write = rrdset_create("services", "queued_io_ops_write", NULL, "disk", "services.queued_io_ops_write", "Systemd Services Queued Disk Write Operations", "operations/s", CHART_PRIORITY_SYSTEMD_SERVICES + 210, update_every, RRDSET_TYPE_STACKED); + } + else + rrdset_next(st_queued_ops_write); + } + + if(likely(do_merged_ops)) { + if(unlikely(!st_merged_ops_read)) { + st_merged_ops_read = rrdset_find_bytype("services", "merged_io_ops_read"); + if(likely(!st_merged_ops_read)) + st_merged_ops_read = rrdset_create("services", "merged_io_ops_read", NULL, "disk", "services.merged_io_ops_read", "Systemd Services Merged Disk Read Operations", "operations/s", CHART_PRIORITY_SYSTEMD_SERVICES + 220, update_every, RRDSET_TYPE_STACKED); + } + else + rrdset_next(st_merged_ops_read); + + if(unlikely(!st_merged_ops_write)) { + st_merged_ops_write = rrdset_find_bytype("services", "merged_io_ops_write"); + if(likely(!st_merged_ops_write)) + st_merged_ops_write = rrdset_create("services", "merged_io_ops_write", NULL, "disk", "services.merged_io_ops_write", "Systemd Services Merged Disk Write Operations", "operations/s", CHART_PRIORITY_SYSTEMD_SERVICES + 230, update_every, RRDSET_TYPE_STACKED); + } + else + rrdset_next(st_merged_ops_write); + } + + // update the values + struct cgroup *cg; + for(cg = cgroup_root; cg ; cg = cg->next) { + if(unlikely(!cg->available || !cg->enabled || !(cg->options & CGROUP_OPTIONS_SYSTEM_SLICE_SERVICE))) + continue; + + if(likely(do_cpu && cg->cpuacct_stat.updated)) { + if(unlikely(!cg->rd_cpu)) + cg->rd_cpu = rrddim_add(st_cpu, cg->chart_id, cg->chart_title, 100, hz, RRDDIM_INCREMENTAL); + + rrddim_set_by_pointer(st_cpu, cg->rd_cpu, cg->cpuacct_stat.user + cg->cpuacct_stat.system); + } + + if(likely(do_mem_usage && cg->memory.updated_usage_in_bytes)) { + if(unlikely(!cg->rd_mem_usage)) + cg->rd_mem_usage = rrddim_add(st_mem_usage, cg->chart_id, cg->chart_title, 1, 1024 * 1024, RRDDIM_ABSOLUTE); + + rrddim_set_by_pointer(st_mem_usage, cg->rd_mem_usage, cg->memory.usage_in_bytes - ((cgroup_used_memory_without_cache)?cg->memory.cache:0)); + } + + if(likely(do_mem_detailed && cg->memory.updated_detailed)) { + if(unlikely(!cg->rd_mem_detailed_rss)) + cg->rd_mem_detailed_rss = rrddim_add(st_mem_detailed_rss, cg->chart_id, cg->chart_title, 1, 1024 * 1024, RRDDIM_ABSOLUTE); + rrddim_set_by_pointer(st_mem_detailed_rss, cg->rd_mem_detailed_rss, cg->memory.rss + cg->memory.rss_huge); + + if(unlikely(!cg->rd_mem_detailed_mapped)) + cg->rd_mem_detailed_mapped = rrddim_add(st_mem_detailed_mapped, cg->chart_id, cg->chart_title, 1, 1024 * 1024, RRDDIM_ABSOLUTE); + rrddim_set_by_pointer(st_mem_detailed_mapped, cg->rd_mem_detailed_mapped, cg->memory.mapped_file); + + if(unlikely(!cg->rd_mem_detailed_cache)) + cg->rd_mem_detailed_cache = rrddim_add(st_mem_detailed_cache, cg->chart_id, cg->chart_title, 1, 1024 * 1024, RRDDIM_ABSOLUTE); + rrddim_set_by_pointer(st_mem_detailed_cache, cg->rd_mem_detailed_cache, cg->memory.cache); + + if(unlikely(!cg->rd_mem_detailed_writeback)) + cg->rd_mem_detailed_writeback = rrddim_add(st_mem_detailed_writeback, cg->chart_id, cg->chart_title, 1, 1024 * 1024, RRDDIM_ABSOLUTE); + rrddim_set_by_pointer(st_mem_detailed_writeback, cg->rd_mem_detailed_writeback, cg->memory.writeback); + + if(unlikely(!cg->rd_mem_detailed_pgfault)) + cg->rd_mem_detailed_pgfault = rrddim_add(st_mem_detailed_pgfault, cg->chart_id, cg->chart_title, system_page_size, 1024 * 1024, RRDDIM_INCREMENTAL); + rrddim_set_by_pointer(st_mem_detailed_pgfault, cg->rd_mem_detailed_pgfault, cg->memory.pgfault); + + if(unlikely(!cg->rd_mem_detailed_pgmajfault)) + cg->rd_mem_detailed_pgmajfault = rrddim_add(st_mem_detailed_pgmajfault, cg->chart_id, cg->chart_title, system_page_size, 1024 * 1024, RRDDIM_INCREMENTAL); + rrddim_set_by_pointer(st_mem_detailed_pgmajfault, cg->rd_mem_detailed_pgmajfault, cg->memory.pgmajfault); + + if(unlikely(!cg->rd_mem_detailed_pgpgin)) + cg->rd_mem_detailed_pgpgin = rrddim_add(st_mem_detailed_pgpgin, cg->chart_id, cg->chart_title, system_page_size, 1024 * 1024, RRDDIM_INCREMENTAL); + rrddim_set_by_pointer(st_mem_detailed_pgpgin, cg->rd_mem_detailed_pgpgin, cg->memory.pgpgin); + + if(unlikely(!cg->rd_mem_detailed_pgpgout)) + cg->rd_mem_detailed_pgpgout = rrddim_add(st_mem_detailed_pgpgout, cg->chart_id, cg->chart_title, system_page_size, 1024 * 1024, RRDDIM_INCREMENTAL); + rrddim_set_by_pointer(st_mem_detailed_pgpgout, cg->rd_mem_detailed_pgpgout, cg->memory.pgpgout); + } + + if(likely(do_mem_failcnt && cg->memory.updated_failcnt)) { + if(unlikely(!cg->rd_mem_failcnt)) + cg->rd_mem_failcnt = rrddim_add(st_mem_failcnt, cg->chart_id, cg->chart_title, 1, 1, RRDDIM_INCREMENTAL); + + rrddim_set_by_pointer(st_mem_failcnt, cg->rd_mem_failcnt, cg->memory.failcnt); + } + + if(likely(do_swap_usage && cg->memory.updated_msw_usage_in_bytes)) { + if(unlikely(!cg->rd_swap_usage)) + cg->rd_swap_usage = rrddim_add(st_swap_usage, cg->chart_id, cg->chart_title, 1, 1024 * 1024, RRDDIM_ABSOLUTE); + + rrddim_set_by_pointer(st_swap_usage, cg->rd_swap_usage, cg->memory.msw_usage_in_bytes); + } + + if(likely(do_io && cg->io_service_bytes.updated)) { + if(unlikely(!cg->rd_io_service_bytes_read)) + cg->rd_io_service_bytes_read = rrddim_add(st_io_read, cg->chart_id, cg->chart_title, 1, 1024, RRDDIM_INCREMENTAL); + + rrddim_set_by_pointer(st_io_read, cg->rd_io_service_bytes_read, cg->io_service_bytes.Read); + + if(unlikely(!cg->rd_io_service_bytes_write)) + cg->rd_io_service_bytes_write = rrddim_add(st_io_write, cg->chart_id, cg->chart_title, 1, 1024, RRDDIM_INCREMENTAL); + + rrddim_set_by_pointer(st_io_write, cg->rd_io_service_bytes_write, cg->io_service_bytes.Write); + } + + if(likely(do_io_ops && cg->io_serviced.updated)) { + if(unlikely(!cg->rd_io_serviced_read)) + cg->rd_io_serviced_read = rrddim_add(st_io_serviced_read, cg->chart_id, cg->chart_title, 1, 1, RRDDIM_INCREMENTAL); + + rrddim_set_by_pointer(st_io_serviced_read, cg->rd_io_serviced_read, cg->io_serviced.Read); + + if(unlikely(!cg->rd_io_serviced_write)) + cg->rd_io_serviced_write = rrddim_add(st_io_serviced_write, cg->chart_id, cg->chart_title, 1, 1, RRDDIM_INCREMENTAL); + + rrddim_set_by_pointer(st_io_serviced_write, cg->rd_io_serviced_write, cg->io_serviced.Write); + } + + if(likely(do_throttle_io && cg->throttle_io_service_bytes.updated)) { + if(unlikely(!cg->rd_throttle_io_read)) + cg->rd_throttle_io_read = rrddim_add(st_throttle_io_read, cg->chart_id, cg->chart_title, 1, 1024, RRDDIM_INCREMENTAL); + + rrddim_set_by_pointer(st_throttle_io_read, cg->rd_throttle_io_read, cg->throttle_io_service_bytes.Read); + + if(unlikely(!cg->rd_throttle_io_write)) + cg->rd_throttle_io_write = rrddim_add(st_throttle_io_write, cg->chart_id, cg->chart_title, 1, 1024, RRDDIM_INCREMENTAL); + + rrddim_set_by_pointer(st_throttle_io_write, cg->rd_throttle_io_write, cg->throttle_io_service_bytes.Write); + } + + if(likely(do_throttle_ops && cg->throttle_io_serviced.updated)) { + if(unlikely(!cg->rd_throttle_io_serviced_read)) + cg->rd_throttle_io_serviced_read = rrddim_add(st_throttle_ops_read, cg->chart_id, cg->chart_title, 1, 1, RRDDIM_INCREMENTAL); + + rrddim_set_by_pointer(st_throttle_ops_read, cg->rd_throttle_io_serviced_read, cg->throttle_io_serviced.Read); + + if(unlikely(!cg->rd_throttle_io_serviced_write)) + cg->rd_throttle_io_serviced_write = rrddim_add(st_throttle_ops_write, cg->chart_id, cg->chart_title, 1, 1, RRDDIM_INCREMENTAL); + + rrddim_set_by_pointer(st_throttle_ops_write, cg->rd_throttle_io_serviced_write, cg->throttle_io_serviced.Write); + } + + if(likely(do_queued_ops && cg->io_queued.updated)) { + if(unlikely(!cg->rd_io_queued_read)) + cg->rd_io_queued_read = rrddim_add(st_queued_ops_read, cg->chart_id, cg->chart_title, 1, 1, RRDDIM_INCREMENTAL); + + rrddim_set_by_pointer(st_queued_ops_read, cg->rd_io_queued_read, cg->io_queued.Read); + + if(unlikely(!cg->rd_io_queued_write)) + cg->rd_io_queued_write = rrddim_add(st_queued_ops_write, cg->chart_id, cg->chart_title, 1, 1, RRDDIM_INCREMENTAL); + + rrddim_set_by_pointer(st_queued_ops_write, cg->rd_io_queued_write, cg->io_queued.Write); + } + + if(likely(do_merged_ops && cg->io_merged.updated)) { + if(unlikely(!cg->rd_io_merged_read)) + cg->rd_io_merged_read = rrddim_add(st_merged_ops_read, cg->chart_id, cg->chart_title, 1, 1, RRDDIM_INCREMENTAL); + + rrddim_set_by_pointer(st_merged_ops_read, cg->rd_io_merged_read, cg->io_merged.Read); + + if(unlikely(!cg->rd_io_merged_write)) + cg->rd_io_merged_write = rrddim_add(st_merged_ops_write, cg->chart_id, cg->chart_title, 1, 1, RRDDIM_INCREMENTAL); + + rrddim_set_by_pointer(st_merged_ops_write, cg->rd_io_merged_write, cg->io_merged.Write); + } + } + + // complete the iteration + if(likely(do_cpu)) + rrdset_done(st_cpu); + + if(likely(do_mem_usage)) + rrdset_done(st_mem_usage); + + if(unlikely(do_mem_detailed)) { + rrdset_done(st_mem_detailed_cache); + rrdset_done(st_mem_detailed_rss); + rrdset_done(st_mem_detailed_mapped); + rrdset_done(st_mem_detailed_writeback); + rrdset_done(st_mem_detailed_pgfault); + rrdset_done(st_mem_detailed_pgmajfault); + rrdset_done(st_mem_detailed_pgpgin); + rrdset_done(st_mem_detailed_pgpgout); + } + + if(likely(do_mem_failcnt)) + rrdset_done(st_mem_failcnt); + + if(likely(do_swap_usage)) + rrdset_done(st_swap_usage); + + if(likely(do_io)) { + rrdset_done(st_io_read); + rrdset_done(st_io_write); + } + + if(likely(do_io_ops)) { + rrdset_done(st_io_serviced_read); + rrdset_done(st_io_serviced_write); + } + + if(likely(do_throttle_io)) { + rrdset_done(st_throttle_io_read); + rrdset_done(st_throttle_io_write); + } + + if(likely(do_throttle_ops)) { + rrdset_done(st_throttle_ops_read); + rrdset_done(st_throttle_ops_write); + } + + if(likely(do_queued_ops)) { + rrdset_done(st_queued_ops_read); + rrdset_done(st_queued_ops_write); + } + + if(likely(do_merged_ops)) { + rrdset_done(st_merged_ops_read); + rrdset_done(st_merged_ops_write); + } +} + +static inline char *cgroup_chart_type(char *buffer, const char *id, size_t len) { + if(buffer[0]) return buffer; + + if(id[0] == '\0' || (id[0] == '/' && id[1] == '\0')) + strncpy(buffer, "cgroup_root", len); + else + snprintfz(buffer, len, "cgroup_%s", id); + + netdata_fix_chart_id(buffer); + return buffer; +} + void update_cgroup_charts(int update_every) { debug(D_CGROUP, "updating cgroups charts"); char type[RRD_ID_LENGTH_MAX + 1]; char title[CHART_TITLE_MAX + 1]; - struct cgroup *cg; - RRDSET *st; + int services_do_cpu = 0, + services_do_mem_usage = 0, + services_do_mem_detailed = 0, + services_do_mem_failcnt = 0, + services_do_swap_usage = 0, + services_do_io = 0, + services_do_io_ops = 0, + services_do_throttle_io = 0, + services_do_throttle_ops = 0, + services_do_queued_ops = 0, + services_do_merged_ops = 0; + struct cgroup *cg; for(cg = cgroup_root; cg ; cg = cg->next) { - if(!cg->available || !cg->enabled) + if(unlikely(!cg->available || !cg->enabled)) continue; - if(cg->id[0] == '\0') - strcpy(type, "cgroup_root"); - else if(cg->id[0] == '/') - snprintfz(type, RRD_ID_LENGTH_MAX, "cgroup_%s", cg->chart_id); - else - snprintfz(type, RRD_ID_LENGTH_MAX, "cgroup_%s", cg->chart_id); + if(likely(cgroup_enable_systemd_services && cg->options & CGROUP_OPTIONS_SYSTEM_SLICE_SERVICE)) { + if(cg->cpuacct_stat.updated && cg->cpuacct_stat.enabled == CONFIG_ONDEMAND_YES) services_do_cpu++; - netdata_fix_chart_id(type); + if(cgroup_enable_systemd_services_detailed_memory && cg->memory.updated_detailed && cg->memory.enabled_detailed) services_do_mem_detailed++; + if(cg->memory.updated_usage_in_bytes && cg->memory.enabled_usage_in_bytes == CONFIG_ONDEMAND_YES) services_do_mem_usage++; + if(cg->memory.updated_failcnt && cg->memory.enabled_failcnt == CONFIG_ONDEMAND_YES) services_do_mem_failcnt++; + if(cg->memory.updated_msw_usage_in_bytes && cg->memory.enabled_msw_usage_in_bytes == CONFIG_ONDEMAND_YES) services_do_swap_usage++; - if(cg->cpuacct_stat.updated) { - st = rrdset_find_bytype(type, "cpu"); - if(!st) { - snprintfz(title, CHART_TITLE_MAX, "CPU Usage (%d%% = %d core%s) for cgroup %s", (processors * 100), processors, (processors>1)?"s":"", cg->chart_title); - st = rrdset_create(type, "cpu", NULL, "cpu", "cgroup.cpu", title, "%", 40000, update_every, RRDSET_TYPE_STACKED); + if(cg->io_service_bytes.updated && cg->io_service_bytes.enabled == CONFIG_ONDEMAND_YES) services_do_io++; + if(cg->io_serviced.updated && cg->io_serviced.enabled == CONFIG_ONDEMAND_YES) services_do_io_ops++; + if(cg->throttle_io_service_bytes.updated && cg->throttle_io_service_bytes.enabled == CONFIG_ONDEMAND_YES) services_do_throttle_io++; + if(cg->throttle_io_serviced.updated && cg->throttle_io_serviced.enabled == CONFIG_ONDEMAND_YES) services_do_throttle_ops++; + if(cg->io_queued.updated && cg->io_queued.enabled == CONFIG_ONDEMAND_YES) services_do_queued_ops++; + if(cg->io_merged.updated && cg->io_merged.enabled == CONFIG_ONDEMAND_YES) services_do_merged_ops++; + continue; + } + + type[0] = '\0'; - rrddim_add(st, "user", NULL, 100, hz, RRDDIM_INCREMENTAL); - rrddim_add(st, "system", NULL, 100, hz, RRDDIM_INCREMENTAL); + if(likely(cg->cpuacct_stat.updated && cg->cpuacct_stat.enabled == CONFIG_ONDEMAND_YES)) { + if(unlikely(!cg->st_cpu)) { + cg->st_cpu = rrdset_find_bytype(cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX), "cpu"); + if(likely(!cg->st_cpu)) { + snprintfz(title, CHART_TITLE_MAX, "CPU Usage (%d%% = %d core%s) for cgroup %s", (processors * 100), processors, (processors > 1) ? "s" : "", cg->chart_title); + cg->st_cpu = rrdset_create(type, "cpu", NULL, "cpu", "cgroup.cpu", title, "%", CHART_PRIORITY_CONTAINERS, update_every, RRDSET_TYPE_STACKED); + } + rrddim_add(cg->st_cpu, "user", NULL, 100, hz, RRDDIM_INCREMENTAL); + rrddim_add(cg->st_cpu, "system", NULL, 100, hz, RRDDIM_INCREMENTAL); } - else rrdset_next(st); + else + rrdset_next(cg->st_cpu); - rrddim_set(st, "user", cg->cpuacct_stat.user); - rrddim_set(st, "system", cg->cpuacct_stat.system); - rrdset_done(st); + rrddim_set(cg->st_cpu, "user", cg->cpuacct_stat.user); + rrddim_set(cg->st_cpu, "system", cg->cpuacct_stat.system); + rrdset_done(cg->st_cpu); } - if(cg->cpuacct_usage.updated) { + if(likely(cg->cpuacct_usage.updated && cg->cpuacct_usage.enabled == CONFIG_ONDEMAND_YES)) { char id[RRD_ID_LENGTH_MAX + 1]; unsigned int i; - st = rrdset_find_bytype(type, "cpu_per_core"); - if(!st) { - snprintfz(title, CHART_TITLE_MAX, "CPU Usage (%d%% = %d core%s) Per Core for cgroup %s", (processors * 100), processors, (processors>1)?"s":"", cg->chart_title); - st = rrdset_create(type, "cpu_per_core", NULL, "cpu", "cgroup.cpu_per_core", title, "%", 40100, update_every, RRDSET_TYPE_STACKED); - - for(i = 0; i < cg->cpuacct_usage.cpus ;i++) { + if(unlikely(!cg->st_cpu_per_core)) { + cg->st_cpu_per_core = rrdset_find_bytype(cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX), "cpu_per_core"); + if(likely(!cg->st_cpu_per_core)) { + snprintfz(title, CHART_TITLE_MAX, "CPU Usage (%d%% = %d core%s) Per Core for cgroup %s", (processors * 100), processors, (processors > 1) ? "s" : "", cg->chart_title); + cg->st_cpu_per_core = rrdset_create(type, "cpu_per_core", NULL, "cpu", "cgroup.cpu_per_core", title, "%", CHART_PRIORITY_CONTAINERS + 100, update_every, RRDSET_TYPE_STACKED); + } + for(i = 0; i < cg->cpuacct_usage.cpus; i++) { snprintfz(id, CHART_TITLE_MAX, "cpu%u", i); - rrddim_add(st, id, NULL, 100, 1000000000, RRDDIM_INCREMENTAL); + rrddim_add(cg->st_cpu_per_core, id, NULL, 100, 1000000000, RRDDIM_INCREMENTAL); } } - else rrdset_next(st); + else + rrdset_next(cg->st_cpu_per_core); for(i = 0; i < cg->cpuacct_usage.cpus ;i++) { snprintfz(id, CHART_TITLE_MAX, "cpu%u", i); - rrddim_set(st, id, cg->cpuacct_usage.cpu_percpu[i]); + rrddim_set(cg->st_cpu_per_core, id, cg->cpuacct_usage.cpu_percpu[i]); } - rrdset_done(st); + rrdset_done(cg->st_cpu_per_core); } - if(cg->memory.updated) { - if(cg->memory.cache + cg->memory.rss + cg->memory.rss_huge + cg->memory.mapped_file > 0) { - st = rrdset_find_bytype(type, "mem"); - if(!st) { + if(likely(cg->memory.updated_detailed && cg->memory.enabled_detailed == CONFIG_ONDEMAND_YES)) { + if(unlikely(!cg->st_mem)) { + cg->st_mem = rrdset_find_bytype(cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX), "mem"); + if(likely(!cg->st_mem)) { snprintfz(title, CHART_TITLE_MAX, "Memory Usage for cgroup %s", cg->chart_title); - st = rrdset_create(type, "mem", NULL, "mem", "cgroup.mem", title, "MB", 40210, update_every, - RRDSET_TYPE_STACKED); - - rrddim_add(st, "cache", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE); - rrddim_add(st, "rss", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE); - if(cg->memory.has_dirty_swap) - rrddim_add(st, "swap", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE); - rrddim_add(st, "rss_huge", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE); - rrddim_add(st, "mapped_file", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE); + cg->st_mem = rrdset_create(type, "mem", NULL, "mem", "cgroup.mem", title, "MB", CHART_PRIORITY_CONTAINERS + 210, update_every, RRDSET_TYPE_STACKED); } - else rrdset_next(st); - - rrddim_set(st, "cache", cg->memory.cache); - rrddim_set(st, "rss", cg->memory.rss); - if(cg->memory.has_dirty_swap) - rrddim_set(st, "swap", cg->memory.swap); - rrddim_set(st, "rss_huge", cg->memory.rss_huge); - rrddim_set(st, "mapped_file", cg->memory.mapped_file); - rrdset_done(st); - } - st = rrdset_find_bytype(type, "writeback"); - if(!st) { - snprintfz(title, CHART_TITLE_MAX, "Writeback Memory for cgroup %s", cg->chart_title); - st = rrdset_create(type, "writeback", NULL, "mem", "cgroup.writeback", title, "MB", 40300, - update_every, RRDSET_TYPE_AREA); + rrddim_add(cg->st_mem, "cache", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE); + rrddim_add(cg->st_mem, "rss", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE); + if(cg->memory.detailed_has_swap) + rrddim_add(cg->st_mem, "swap", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE); + rrddim_add(cg->st_mem, "rss_huge", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE); + rrddim_add(cg->st_mem, "mapped_file", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE); + } + else + rrdset_next(cg->st_mem); + + rrddim_set(cg->st_mem, "cache", cg->memory.cache); + rrddim_set(cg->st_mem, "rss", cg->memory.rss); + if(cg->memory.detailed_has_swap) + rrddim_set(cg->st_mem, "swap", cg->memory.swap); + rrddim_set(cg->st_mem, "rss_huge", cg->memory.rss_huge); + rrddim_set(cg->st_mem, "mapped_file", cg->memory.mapped_file); + rrdset_done(cg->st_mem); + + if(unlikely(!cg->st_writeback)) { + cg->st_writeback = rrdset_find_bytype(cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX), "writeback"); + if(likely(!cg->st_writeback)) { + snprintfz(title, CHART_TITLE_MAX, "Writeback Memory for cgroup %s", cg->chart_title); + cg->st_writeback = rrdset_create(type, "writeback", NULL, "mem", "cgroup.writeback", title, "MB", CHART_PRIORITY_CONTAINERS + 300, update_every, RRDSET_TYPE_AREA); + } - if(cg->memory.has_dirty_swap) - rrddim_add(st, "dirty", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE); - rrddim_add(st, "writeback", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE); + if(cg->memory.detailed_has_dirty) + rrddim_add(cg->st_writeback, "dirty", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE); + rrddim_add(cg->st_writeback, "writeback", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE); } - else rrdset_next(st); + else + rrdset_next(cg->st_writeback); - if(cg->memory.has_dirty_swap) - rrddim_set(st, "dirty", cg->memory.dirty); - rrddim_set(st, "writeback", cg->memory.writeback); - rrdset_done(st); + if(cg->memory.detailed_has_dirty) + rrddim_set(cg->st_writeback, "dirty", cg->memory.dirty); + rrddim_set(cg->st_writeback, "writeback", cg->memory.writeback); + rrdset_done(cg->st_writeback); - if(cg->memory.pgpgin + cg->memory.pgpgout > 0) { - st = rrdset_find_bytype(type, "mem_activity"); - if(!st) { + if(unlikely(!cg->st_mem_activity)) { + cg->st_mem_activity = rrdset_find_bytype(cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX), "mem_activity"); + if(likely(!cg->st_mem_activity)) { snprintfz(title, CHART_TITLE_MAX, "Memory Activity for cgroup %s", cg->chart_title); - st = rrdset_create(type, "mem_activity", NULL, "mem", "cgroup.mem_activity", title, "MB/s", - 40400, update_every, RRDSET_TYPE_LINE); - - rrddim_add(st, "pgpgin", "in", sysconf(_SC_PAGESIZE), 1024 * 1024, RRDDIM_INCREMENTAL); - rrddim_add(st, "pgpgout", "out", -sysconf(_SC_PAGESIZE), 1024 * 1024, RRDDIM_INCREMENTAL); + cg->st_mem_activity = rrdset_create(type, "mem_activity", NULL, "mem", "cgroup.mem_activity", title, "MB/s", CHART_PRIORITY_CONTAINERS + 400, update_every, RRDSET_TYPE_LINE); } - else rrdset_next(st); - - rrddim_set(st, "pgpgin", cg->memory.pgpgin); - rrddim_set(st, "pgpgout", cg->memory.pgpgout); - rrdset_done(st); + rrddim_add(cg->st_mem_activity, "pgpgin", "in", system_page_size, 1024 * 1024, RRDDIM_INCREMENTAL); + rrddim_add(cg->st_mem_activity, "pgpgout", "out", -system_page_size, 1024 * 1024, RRDDIM_INCREMENTAL); } + else + rrdset_next(cg->st_mem_activity); - if(cg->memory.pgfault + cg->memory.pgmajfault > 0) { - st = rrdset_find_bytype(type, "pgfaults"); - if(!st) { - snprintfz(title, CHART_TITLE_MAX, "Memory Page Faults for cgroup %s", cg->chart_title); - st = rrdset_create(type, "pgfaults", NULL, "mem", "cgroup.pgfaults", title, "MB/s", 40500, - update_every, RRDSET_TYPE_LINE); + rrddim_set(cg->st_mem_activity, "pgpgin", cg->memory.pgpgin); + rrddim_set(cg->st_mem_activity, "pgpgout", cg->memory.pgpgout); + rrdset_done(cg->st_mem_activity); - rrddim_add(st, "pgfault", NULL, sysconf(_SC_PAGESIZE), 1024 * 1024, RRDDIM_INCREMENTAL); - rrddim_add(st, "pgmajfault", "swap", -sysconf(_SC_PAGESIZE), 1024 * 1024, RRDDIM_INCREMENTAL); + if(unlikely(!cg->st_pgfaults)) { + cg->st_pgfaults = rrdset_find_bytype(cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX), "pgfaults"); + if(likely(!cg->st_pgfaults)) { + snprintfz(title, CHART_TITLE_MAX, "Memory Page Faults for cgroup %s", cg->chart_title); + cg->st_pgfaults = rrdset_create(type, "pgfaults", NULL, "mem", "cgroup.pgfaults", title, "MB/s", CHART_PRIORITY_CONTAINERS + 500, update_every, RRDSET_TYPE_LINE); } - else rrdset_next(st); - - rrddim_set(st, "pgfault", cg->memory.pgfault); - rrddim_set(st, "pgmajfault", cg->memory.pgmajfault); - rrdset_done(st); + rrddim_add(cg->st_pgfaults, "pgfault", NULL, system_page_size, 1024 * 1024, RRDDIM_INCREMENTAL); + rrddim_add(cg->st_pgfaults, "pgmajfault", "swap", -system_page_size, 1024 * 1024, RRDDIM_INCREMENTAL); } - } + else + rrdset_next(cg->st_pgfaults); - if(cg->memory.usage_in_bytes_updated) { - st = rrdset_find_bytype(type, "mem_usage"); - if(!st) { - snprintfz(title, CHART_TITLE_MAX, "Total Memory for cgroup %s", cg->chart_title); - st = rrdset_create(type, "mem_usage", NULL, "mem", "cgroup.mem_usage", title, "MB", 40200, - update_every, RRDSET_TYPE_STACKED); + rrddim_set(cg->st_pgfaults, "pgfault", cg->memory.pgfault); + rrddim_set(cg->st_pgfaults, "pgmajfault", cg->memory.pgmajfault); + rrdset_done(cg->st_pgfaults); + } - rrddim_add(st, "ram", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE); - rrddim_add(st, "swap", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE); + if(likely(cg->memory.updated_usage_in_bytes && cg->memory.enabled_usage_in_bytes == CONFIG_ONDEMAND_YES)) { + if(unlikely(!cg->st_mem_usage)) { + cg->st_mem_usage = rrdset_find_bytype(cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX), "mem_usage"); + if(likely(!cg->st_mem_usage)) { + snprintfz(title, CHART_TITLE_MAX, "Used Memory %sfor cgroup %s", (cgroup_used_memory_without_cache && cg->memory.updated_detailed)?"without Cache ":"", cg->chart_title); + cg->st_mem_usage = rrdset_create(type, "mem_usage", NULL, "mem", "cgroup.mem_usage", title, "MB", CHART_PRIORITY_CONTAINERS + 200, update_every, RRDSET_TYPE_STACKED); + } + rrddim_add(cg->st_mem_usage, "ram", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE); + rrddim_add(cg->st_mem_usage, "swap", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE); } - else rrdset_next(st); + else + rrdset_next(cg->st_mem_usage); - rrddim_set(st, "ram", cg->memory.usage_in_bytes); - rrddim_set(st, "swap", (cg->memory.msw_usage_in_bytes > cg->memory.usage_in_bytes)?cg->memory.msw_usage_in_bytes - cg->memory.usage_in_bytes:0); - rrdset_done(st); + rrddim_set(cg->st_mem_usage, "ram", cg->memory.usage_in_bytes - ((cgroup_used_memory_without_cache)?cg->memory.cache:0)); + rrddim_set(cg->st_mem_usage, "swap", (cg->memory.msw_usage_in_bytes > cg->memory.usage_in_bytes)?cg->memory.msw_usage_in_bytes - cg->memory.usage_in_bytes:0); + rrdset_done(cg->st_mem_usage); } - if(cg->memory.failcnt_updated && cg->memory.failcnt > 0) { - st = rrdset_find_bytype(type, "mem_failcnt"); - if(!st) { - snprintfz(title, CHART_TITLE_MAX, "Memory Limit Failures for cgroup %s", cg->chart_title); - st = rrdset_create(type, "mem_failcnt", NULL, "mem", "cgroup.mem_failcnt", title, "MB", 40250, - update_every, RRDSET_TYPE_LINE); - - rrddim_add(st, "failures", NULL, 1, 1, RRDDIM_INCREMENTAL); + if(likely(cg->memory.updated_failcnt && cg->memory.enabled_failcnt == CONFIG_ONDEMAND_YES)) { + if(unlikely(!cg->st_mem_failcnt)) { + cg->st_mem_failcnt = rrdset_find_bytype(cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX), "mem_failcnt"); + if(likely(!cg->st_mem_failcnt)) { + snprintfz(title, CHART_TITLE_MAX, "Memory Limit Failures for cgroup %s", cg->chart_title); + cg->st_mem_failcnt = rrdset_create(type, "mem_failcnt", NULL, "mem", "cgroup.mem_failcnt", title, "count", CHART_PRIORITY_CONTAINERS + 250, update_every, RRDSET_TYPE_LINE); + } + rrddim_add(cg->st_mem_failcnt, "failures", NULL, 1, 1, RRDDIM_INCREMENTAL); } - else rrdset_next(st); + else + rrdset_next(cg->st_mem_failcnt); - rrddim_set(st, "failures", cg->memory.failcnt); - rrdset_done(st); + rrddim_set(cg->st_mem_failcnt, "failures", cg->memory.failcnt); + rrdset_done(cg->st_mem_failcnt); } - if(cg->io_service_bytes.updated && cg->io_service_bytes.Read + cg->io_service_bytes.Write > 0) { - st = rrdset_find_bytype(type, "io"); - if(!st) { - snprintfz(title, CHART_TITLE_MAX, "I/O Bandwidth (all disks) for cgroup %s", cg->chart_title); - st = rrdset_create(type, "io", NULL, "disk", "cgroup.io", title, "KB/s", 41200, - update_every, RRDSET_TYPE_LINE); - - rrddim_add(st, "read", NULL, 1, 1024, RRDDIM_INCREMENTAL); - rrddim_add(st, "write", NULL, -1, 1024, RRDDIM_INCREMENTAL); + if(likely(cg->io_service_bytes.updated && cg->io_service_bytes.enabled == CONFIG_ONDEMAND_YES)) { + if(unlikely(!cg->st_io)) { + cg->st_io = rrdset_find_bytype(cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX), "io"); + if(likely(!cg->st_io)) { + snprintfz(title, CHART_TITLE_MAX, "I/O Bandwidth (all disks) for cgroup %s", cg->chart_title); + cg->st_io = rrdset_create(type, "io", NULL, "disk", "cgroup.io", title, "KB/s", CHART_PRIORITY_CONTAINERS + 1200, update_every, RRDSET_TYPE_AREA); + } + rrddim_add(cg->st_io, "read", NULL, 1, 1024, RRDDIM_INCREMENTAL); + rrddim_add(cg->st_io, "write", NULL, -1, 1024, RRDDIM_INCREMENTAL); } - else rrdset_next(st); + else + rrdset_next(cg->st_io); - rrddim_set(st, "read", cg->io_service_bytes.Read); - rrddim_set(st, "write", cg->io_service_bytes.Write); - rrdset_done(st); + rrddim_set(cg->st_io, "read", cg->io_service_bytes.Read); + rrddim_set(cg->st_io, "write", cg->io_service_bytes.Write); + rrdset_done(cg->st_io); } - if(cg->io_serviced.updated && cg->io_serviced.Read + cg->io_serviced.Write > 0) { - st = rrdset_find_bytype(type, "serviced_ops"); - if(!st) { - snprintfz(title, CHART_TITLE_MAX, "Serviced I/O Operations (all disks) for cgroup %s", cg->chart_title); - st = rrdset_create(type, "serviced_ops", NULL, "disk", "cgroup.serviced_ops", title, "operations/s", 41200, - update_every, RRDSET_TYPE_LINE); - - rrddim_add(st, "read", NULL, 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "write", NULL, -1, 1, RRDDIM_INCREMENTAL); + if(likely(cg->io_serviced.updated && cg->io_serviced.enabled == CONFIG_ONDEMAND_YES)) { + if(unlikely(!cg->st_serviced_ops)) { + cg->st_serviced_ops = rrdset_find_bytype(cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX), "serviced_ops"); + if(likely(!cg->st_serviced_ops)) { + snprintfz(title, CHART_TITLE_MAX, "Serviced I/O Operations (all disks) for cgroup %s", cg->chart_title); + cg->st_serviced_ops = rrdset_create(type, "serviced_ops", NULL, "disk", "cgroup.serviced_ops", title, "operations/s", CHART_PRIORITY_CONTAINERS + 1200, update_every, RRDSET_TYPE_LINE); + } + rrddim_add(cg->st_serviced_ops, "read", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(cg->st_serviced_ops, "write", NULL, -1, 1, RRDDIM_INCREMENTAL); } - else rrdset_next(st); + else + rrdset_next(cg->st_serviced_ops); - rrddim_set(st, "read", cg->io_serviced.Read); - rrddim_set(st, "write", cg->io_serviced.Write); - rrdset_done(st); + rrddim_set(cg->st_serviced_ops, "read", cg->io_serviced.Read); + rrddim_set(cg->st_serviced_ops, "write", cg->io_serviced.Write); + rrdset_done(cg->st_serviced_ops); } - if(cg->throttle_io_service_bytes.updated && cg->throttle_io_service_bytes.Read + cg->throttle_io_service_bytes.Write > 0) { - st = rrdset_find_bytype(type, "io"); - if(!st) { - snprintfz(title, CHART_TITLE_MAX, "Throttle I/O Bandwidth (all disks) for cgroup %s", cg->chart_title); - st = rrdset_create(type, "io", NULL, "disk", "cgroup.io", title, "KB/s", 41200, - update_every, RRDSET_TYPE_LINE); - - rrddim_add(st, "read", NULL, 1, 1024, RRDDIM_INCREMENTAL); - rrddim_add(st, "write", NULL, -1, 1024, RRDDIM_INCREMENTAL); + if(likely(cg->throttle_io_service_bytes.updated && cg->throttle_io_service_bytes.enabled == CONFIG_ONDEMAND_YES)) { + if(unlikely(!cg->st_throttle_io)) { + cg->st_throttle_io = rrdset_find_bytype(cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX), "throttle_io"); + if(likely(!cg->st_throttle_io)) { + snprintfz(title, CHART_TITLE_MAX, "Throttle I/O Bandwidth (all disks) for cgroup %s", cg->chart_title); + cg->st_throttle_io = rrdset_create(type, "throttle_io", NULL, "disk", "cgroup.throttle_io", title, "KB/s", CHART_PRIORITY_CONTAINERS + 1200, update_every, RRDSET_TYPE_AREA); + } + rrddim_add(cg->st_throttle_io, "read", NULL, 1, 1024, RRDDIM_INCREMENTAL); + rrddim_add(cg->st_throttle_io, "write", NULL, -1, 1024, RRDDIM_INCREMENTAL); } - else rrdset_next(st); + else + rrdset_next(cg->st_throttle_io); - rrddim_set(st, "read", cg->throttle_io_service_bytes.Read); - rrddim_set(st, "write", cg->throttle_io_service_bytes.Write); - rrdset_done(st); + rrddim_set(cg->st_throttle_io, "read", cg->throttle_io_service_bytes.Read); + rrddim_set(cg->st_throttle_io, "write", cg->throttle_io_service_bytes.Write); + rrdset_done(cg->st_throttle_io); } - - if(cg->throttle_io_serviced.updated && cg->throttle_io_serviced.Read + cg->throttle_io_serviced.Write > 0) { - st = rrdset_find_bytype(type, "throttle_serviced_ops"); - if(!st) { - snprintfz(title, CHART_TITLE_MAX, "Throttle Serviced I/O Operations (all disks) for cgroup %s", cg->chart_title); - st = rrdset_create(type, "throttle_serviced_ops", NULL, "disk", "cgroup.throttle_serviced_ops", title, "operations/s", 41200, - update_every, RRDSET_TYPE_LINE); - - rrddim_add(st, "read", NULL, 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "write", NULL, -1, 1, RRDDIM_INCREMENTAL); + if(likely(cg->throttle_io_serviced.updated && cg->throttle_io_serviced.enabled == CONFIG_ONDEMAND_YES)) { + if(unlikely(!cg->st_throttle_serviced_ops)) { + cg->st_throttle_serviced_ops = rrdset_find_bytype(cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX), "throttle_serviced_ops"); + if(likely(!cg->st_throttle_serviced_ops)) { + snprintfz(title, CHART_TITLE_MAX, "Throttle Serviced I/O Operations (all disks) for cgroup %s", cg->chart_title); + cg->st_throttle_serviced_ops = rrdset_create(type, "throttle_serviced_ops", NULL, "disk", "cgroup.throttle_serviced_ops", title, "operations/s", CHART_PRIORITY_CONTAINERS + 1200, update_every, RRDSET_TYPE_LINE); + } + rrddim_add(cg->st_throttle_serviced_ops, "read", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(cg->st_throttle_serviced_ops, "write", NULL, -1, 1, RRDDIM_INCREMENTAL); } - else rrdset_next(st); + else + rrdset_next(cg->st_throttle_serviced_ops); - rrddim_set(st, "read", cg->throttle_io_serviced.Read); - rrddim_set(st, "write", cg->throttle_io_serviced.Write); - rrdset_done(st); + rrddim_set(cg->st_throttle_serviced_ops, "read", cg->throttle_io_serviced.Read); + rrddim_set(cg->st_throttle_serviced_ops, "write", cg->throttle_io_serviced.Write); + rrdset_done(cg->st_throttle_serviced_ops); } - if(cg->io_queued.updated) { - st = rrdset_find_bytype(type, "queued_ops"); - if(!st) { - snprintfz(title, CHART_TITLE_MAX, "Queued I/O Operations (all disks) for cgroup %s", cg->chart_title); - st = rrdset_create(type, "queued_ops", NULL, "disk", "cgroup.queued_ops", title, "operations", 42000, - update_every, RRDSET_TYPE_LINE); - - rrddim_add(st, "read", NULL, 1, 1, RRDDIM_ABSOLUTE); - rrddim_add(st, "write", NULL, -1, 1, RRDDIM_ABSOLUTE); + if(likely(cg->io_queued.updated && cg->io_queued.enabled == CONFIG_ONDEMAND_YES)) { + if(unlikely(!cg->st_queued_ops)) { + cg->st_queued_ops = rrdset_find_bytype(cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX), "queued_ops"); + if(likely(!cg->st_queued_ops)) { + snprintfz(title, CHART_TITLE_MAX, "Queued I/O Operations (all disks) for cgroup %s", cg->chart_title); + cg->st_queued_ops = rrdset_create(type, "queued_ops", NULL, "disk", "cgroup.queued_ops", title, "operations", CHART_PRIORITY_CONTAINERS + 2000, update_every, RRDSET_TYPE_LINE); + } + rrddim_add(cg->st_queued_ops, "read", NULL, 1, 1, RRDDIM_ABSOLUTE); + rrddim_add(cg->st_queued_ops, "write", NULL, -1, 1, RRDDIM_ABSOLUTE); } - else rrdset_next(st); + else + rrdset_next(cg->st_queued_ops); - rrddim_set(st, "read", cg->io_queued.Read); - rrddim_set(st, "write", cg->io_queued.Write); - rrdset_done(st); + rrddim_set(cg->st_queued_ops, "read", cg->io_queued.Read); + rrddim_set(cg->st_queued_ops, "write", cg->io_queued.Write); + rrdset_done(cg->st_queued_ops); } - if(cg->io_merged.updated && cg->io_merged.Read + cg->io_merged.Write > 0) { - st = rrdset_find_bytype(type, "merged_ops"); - if(!st) { - snprintfz(title, CHART_TITLE_MAX, "Merged I/O Operations (all disks) for cgroup %s", cg->chart_title); - st = rrdset_create(type, "merged_ops", NULL, "disk", "cgroup.merged_ops", title, "operations/s", 42100, - update_every, RRDSET_TYPE_LINE); - - rrddim_add(st, "read", NULL, 1, 1024, RRDDIM_INCREMENTAL); - rrddim_add(st, "write", NULL, -1, 1024, RRDDIM_INCREMENTAL); + if(likely(cg->io_merged.updated && cg->io_merged.enabled == CONFIG_ONDEMAND_YES)) { + if(unlikely(!cg->st_merged_ops)) { + cg->st_merged_ops = rrdset_find_bytype(cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX), "merged_ops"); + if(likely(!cg->st_merged_ops)) { + snprintfz(title, CHART_TITLE_MAX, "Merged I/O Operations (all disks) for cgroup %s", cg->chart_title); + cg->st_merged_ops = rrdset_create(type, "merged_ops", NULL, "disk", "cgroup.merged_ops", title, "operations/s", CHART_PRIORITY_CONTAINERS + 2100, update_every, RRDSET_TYPE_LINE); + } + rrddim_add(cg->st_merged_ops, "read", NULL, 1, 1024, RRDDIM_INCREMENTAL); + rrddim_add(cg->st_merged_ops, "write", NULL, -1, 1024, RRDDIM_INCREMENTAL); } - else rrdset_next(st); + else + rrdset_next(cg->st_merged_ops); - rrddim_set(st, "read", cg->io_merged.Read); - rrddim_set(st, "write", cg->io_merged.Write); - rrdset_done(st); + rrddim_set(cg->st_merged_ops, "read", cg->io_merged.Read); + rrddim_set(cg->st_merged_ops, "write", cg->io_merged.Write); + rrdset_done(cg->st_merged_ops); } } + if(likely(cgroup_enable_systemd_services)) + update_services_charts(update_every, + services_do_cpu, + services_do_mem_usage, + services_do_mem_detailed, + services_do_mem_failcnt, + services_do_swap_usage, + services_do_io, + services_do_io_ops, + services_do_throttle_io, + services_do_throttle_ops, + services_do_queued_ops, + services_do_merged_ops + ); + debug(D_CGROUP, "done updating cgroups charts"); } // ---------------------------------------------------------------------------- // cgroups main -int do_sys_fs_cgroup(int update_every, unsigned long long dt) { - (void)dt; - - static int cgroup_global_config_read = 0; - static time_t last_run = 0; - time_t now = time(NULL); - - if(unlikely(!cgroup_global_config_read)) { - read_cgroup_plugin_configuration(); - cgroup_global_config_read = 1; - } - - if(unlikely(cgroup_enable_new_cgroups_detected_at_runtime && now - last_run > cgroup_check_for_new_every)) { - find_all_cgroups(); - last_run = now; - } - - read_all_cgroups(cgroup_root); - update_cgroup_charts(update_every); - - return 0; -} - -void *cgroups_main(void *ptr) -{ - (void)ptr; +void *cgroups_main(void *ptr) { + struct netdata_static_thread *static_thread = (struct netdata_static_thread *)ptr; info("CGROUP Plugin thread created with task id %d", gettid()); @@ -1422,56 +2073,52 @@ void *cgroups_main(void *ptr) struct rusage thread; // when ZERO, attempt to do it - int vdo_sys_fs_cgroup = 0; - int vdo_cpu_netdata = !config_get_boolean("plugin:cgroups", "cgroups plugin resources", 1); - - // keep track of the time each module was called - unsigned long long sutime_sys_fs_cgroup = 0ULL; + int vdo_cpu_netdata = config_get_boolean("plugin:cgroups", "cgroups plugin resource charts", 1); - // the next time we will run - aligned properly - unsigned long long sunext = (time(NULL) - (time(NULL) % rrd_update_every) + rrd_update_every) * 1000000ULL; - unsigned long long sunow; + read_cgroup_plugin_configuration(); RRDSET *stcpu_thread = NULL; + usec_t step = cgroup_update_every * USEC_PER_SEC; + usec_t find_every = cgroup_check_for_new_every * USEC_PER_SEC, find_next = 0; for(;;) { - if(unlikely(netdata_exit)) break; - - // delay until it is our time to run - while((sunow = time_usec()) < sunext) - sleep_usec(sunext - sunow); + usec_t now = now_monotonic_usec(); + usec_t next = now - (now % step) + step; - // find the next time we need to run - while(time_usec() > sunext) - sunext += rrd_update_every * 1000000ULL; + while(now < next) { + sleep_usec(next - now); + now = now_monotonic_usec(); + } if(unlikely(netdata_exit)) break; // BEGIN -- the job to be done - if(!vdo_sys_fs_cgroup) { - debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling do_sys_fs_cgroup()."); - sunow = time_usec(); - vdo_sys_fs_cgroup = do_sys_fs_cgroup(rrd_update_every, (sutime_sys_fs_cgroup > 0)?sunow - sutime_sys_fs_cgroup:0ULL); - sutime_sys_fs_cgroup = sunow; + if(unlikely(now >= find_next)) { + find_all_cgroups(); + find_next = now + find_every; } - if(unlikely(netdata_exit)) break; + + read_all_cgroups(cgroup_root); + update_cgroup_charts(cgroup_update_every); // END -- the job is done // -------------------------------------------------------------------- - if(!vdo_cpu_netdata) { + if(vdo_cpu_netdata) { getrusage(RUSAGE_THREAD, &thread); - if(!stcpu_thread) stcpu_thread = rrdset_find("netdata.plugin_cgroups_cpu"); - if(!stcpu_thread) { - stcpu_thread = rrdset_create("netdata", "plugin_cgroups_cpu", NULL, "proc.internal", NULL, "NetData CGroups Plugin CPU usage", "milliseconds/s", 132000, rrd_update_every, RRDSET_TYPE_STACKED); + if(unlikely(!stcpu_thread)) { + stcpu_thread = rrdset_find("netdata.plugin_cgroups_cpu"); + if(unlikely(!stcpu_thread)) + stcpu_thread = rrdset_create("netdata", "plugin_cgroups_cpu", NULL, "cgroups", NULL, "NetData CGroups Plugin CPU usage", "milliseconds/s", 132000, cgroup_update_every, RRDSET_TYPE_STACKED); rrddim_add(stcpu_thread, "user", NULL, 1, 1000, RRDDIM_INCREMENTAL); rrddim_add(stcpu_thread, "system", NULL, 1, 1000, RRDDIM_INCREMENTAL); } - else rrdset_next(stcpu_thread); + else + rrdset_next(stcpu_thread); rrddim_set(stcpu_thread, "user" , thread.ru_utime.tv_sec * 1000000ULL + thread.ru_utime.tv_usec); rrddim_set(stcpu_thread, "system", thread.ru_stime.tv_sec * 1000000ULL + thread.ru_stime.tv_usec); @@ -1481,6 +2128,7 @@ void *cgroups_main(void *ptr) info("CGROUP thread exiting"); + static_thread->enabled = 0; pthread_exit(NULL); return NULL; } diff --git a/src/sys_kernel_mm_ksm.c b/src/sys_kernel_mm_ksm.c index 8c51be1df..83da74429 100644 --- a/src/sys_kernel_mm_ksm.c +++ b/src/sys_kernel_mm_ksm.c @@ -19,7 +19,7 @@ KSM_NAME_VALUE values[] = { [PAGES_TO_SCAN] = { "/sys/kernel/mm/ksm/pages_to_scan", 0ULL }, }; -int do_sys_kernel_mm_ksm(int update_every, unsigned long long dt) { +int do_sys_kernel_mm_ksm(int update_every, usec_t dt) { static procfile *ff_pages_shared = NULL, *ff_pages_sharing = NULL, *ff_pages_unshared = NULL, *ff_pages_volatile = NULL, *ff_pages_to_scan = NULL; static long page_size = -1; @@ -64,23 +64,23 @@ int do_sys_kernel_mm_ksm(int update_every, unsigned long long dt) { ff_pages_shared = procfile_readall(ff_pages_shared); if(!ff_pages_shared) return 0; // we return 0, so that we will retry to open it next time - pages_shared = strtoull(procfile_lineword(ff_pages_shared, 0, 0), NULL, 10); + pages_shared = str2ull(procfile_lineword(ff_pages_shared, 0, 0)); ff_pages_sharing = procfile_readall(ff_pages_sharing); if(!ff_pages_sharing) return 0; // we return 0, so that we will retry to open it next time - pages_sharing = strtoull(procfile_lineword(ff_pages_sharing, 0, 0), NULL, 10); + pages_sharing = str2ull(procfile_lineword(ff_pages_sharing, 0, 0)); ff_pages_unshared = procfile_readall(ff_pages_unshared); if(!ff_pages_unshared) return 0; // we return 0, so that we will retry to open it next time - pages_unshared = strtoull(procfile_lineword(ff_pages_unshared, 0, 0), NULL, 10); + pages_unshared = str2ull(procfile_lineword(ff_pages_unshared, 0, 0)); ff_pages_volatile = procfile_readall(ff_pages_volatile); if(!ff_pages_volatile) return 0; // we return 0, so that we will retry to open it next time - pages_volatile = strtoull(procfile_lineword(ff_pages_volatile, 0, 0), NULL, 10); + pages_volatile = str2ull(procfile_lineword(ff_pages_volatile, 0, 0)); ff_pages_to_scan = procfile_readall(ff_pages_to_scan); if(!ff_pages_to_scan) return 0; // we return 0, so that we will retry to open it next time - pages_to_scan = strtoull(procfile_lineword(ff_pages_to_scan, 0, 0), NULL, 10); + pages_to_scan = str2ull(procfile_lineword(ff_pages_to_scan, 0, 0)); offered = pages_sharing + pages_shared + pages_unshared + pages_volatile; saved = pages_sharing - pages_shared; diff --git a/src/unit_test.c b/src/unit_test.c index d699707a4..4e2f10c0a 100644 --- a/src/unit_test.c +++ b/src/unit_test.c @@ -18,7 +18,7 @@ int check_storage_number(calculated_number n, int debug) { if(dcdiff < 0) dcdiff = -dcdiff; size_t len = print_calculated_number(buffer, d); - calculated_number p = strtold(buffer, NULL); + calculated_number p = str2l(buffer); calculated_number pdiff = n - p; calculated_number pcdiff = pdiff * 100.0 / n; if(pcdiff < 0) pcdiff = -pcdiff; @@ -901,7 +901,7 @@ int run_test(struct test *test) st->debug = 1; // feed it with the test data - time_t time_now = 0, time_start = time(NULL); + time_t time_now = 0, time_start = now_realtime_sec(); unsigned long c; collected_number last = 0; for(c = 0; c < test->feed_entries; c++) { @@ -915,7 +915,7 @@ int run_test(struct test *test) (float)time_now / 1000000.0, ((calculated_number)test->feed[c].value - (calculated_number)last) * (calculated_number)test->multiplier / (calculated_number)test->divisor, (((calculated_number)test->feed[c].value - (calculated_number)last) * (calculated_number)test->multiplier / (calculated_number)test->divisor) / (calculated_number)test->feed[c].microseconds * (calculated_number)1000000); - rrdset_next_usec(st, test->feed[c].microseconds); + rrdset_next_usec_unfiltered(st, test->feed[c].microseconds); } else { fprintf(stderr, " > %s: feeding position %lu\n", test->name, c+1); @@ -975,8 +975,55 @@ int run_test(struct test *test) return errors; } +static int test_variable_renames(void) { + fprintf(stderr, "Creating chart\n"); + RRDSET *st = rrdset_create("chart", "ID", NULL, "family", "context", "Unit Testing", "a value", 1, 1, RRDSET_TYPE_LINE); + fprintf(stderr, "Created chart with id '%s', name '%s'\n", st->id, st->name); + + fprintf(stderr, "Creating dimension DIM1\n"); + RRDDIM *rd1 = rrddim_add(st, "DIM1", NULL, 1, 1, RRDDIM_INCREMENTAL); + fprintf(stderr, "Created dimension with id '%s', name '%s'\n", rd1->id, rd1->name); + + fprintf(stderr, "Creating dimension DIM2\n"); + RRDDIM *rd2 = rrddim_add(st, "DIM2", NULL, 1, 1, RRDDIM_INCREMENTAL); + fprintf(stderr, "Created dimension with id '%s', name '%s'\n", rd2->id, rd2->name); + + fprintf(stderr, "Renaming chart to CHARTNAME1\n"); + rrdset_set_name(st, "CHARTNAME1"); + fprintf(stderr, "Renamed chart with id '%s' to name '%s'\n", st->id, st->name); + + fprintf(stderr, "Renaming chart to CHARTNAME2\n"); + rrdset_set_name(st, "CHARTNAME2"); + fprintf(stderr, "Renamed chart with id '%s' to name '%s'\n", st->id, st->name); + + fprintf(stderr, "Renaming dimension DIM1 to DIM1NAME1\n"); + rrddim_set_name(st, rd1, "DIM1NAME1"); + fprintf(stderr, "Renamed dimension with id '%s' to name '%s'\n", rd1->id, rd1->name); + + fprintf(stderr, "Renaming dimension DIM1 to DIM1NAME2\n"); + rrddim_set_name(st, rd1, "DIM1NAME2"); + fprintf(stderr, "Renamed dimension with id '%s' to name '%s'\n", rd1->id, rd1->name); + + fprintf(stderr, "Renaming dimension DIM2 to DIM2NAME1\n"); + rrddim_set_name(st, rd2, "DIM2NAME1"); + fprintf(stderr, "Renamed dimension with id '%s' to name '%s'\n", rd2->id, rd2->name); + + fprintf(stderr, "Renaming dimension DIM2 to DIM2NAME2\n"); + rrddim_set_name(st, rd2, "DIM2NAME2"); + fprintf(stderr, "Renamed dimension with id '%s' to name '%s'\n", rd2->id, rd2->name); + + BUFFER *buf = buffer_create(1); + health_api_v1_chart_variables2json(st, buf); + fprintf(stderr, "%s", buffer_tostring(buf)); + buffer_free(buf); + return 1; +} + int run_all_mockup_tests(void) { + if(!test_variable_renames()) + return 1; + if(run_test(&test1)) return 1; @@ -1028,6 +1075,8 @@ int run_all_mockup_tests(void) if(run_test(&test15)) return 1; + + return 0; } @@ -1073,7 +1122,7 @@ int unit_test(long delay, long shift) fprintf(stderr, "\n\nLOOP = %lu, DELAY = %ld, VALUE = " COLLECTED_NUMBER_FORMAT "\n", c, delay, i); if(c) { - rrdset_next_usec(st, delay); + rrdset_next_usec_unfiltered(st, delay); } if(do_abs) rrddim_set(st, "absolute", i); if(do_inc) rrddim_set(st, "incremental", i); @@ -1081,7 +1130,7 @@ int unit_test(long delay, long shift) if(do_absi) rrddim_set(st, "percentage-of-incremental-row", i); if(!c) { - gettimeofday(&st->last_collected_time, NULL); + now_realtime_timeval(&st->last_collected_time); st->last_collected_time.tv_usec = shift; } diff --git a/src/web_buffer.c b/src/web_buffer.c index 93ba782af..6203db0f7 100644 --- a/src/web_buffer.c +++ b/src/web_buffer.c @@ -113,6 +113,8 @@ void buffer_print_llu(BUFFER *wb, unsigned long long uvalue) void buffer_strcat(BUFFER *wb, const char *txt) { + // buffer_sprintf(wb, "%s", txt); + if(unlikely(!txt || !*txt)) return; buffer_need_bytes(wb, 1); @@ -143,6 +145,26 @@ void buffer_strcat(BUFFER *wb, const char *txt) } } +void buffer_strcat_htmlescape(BUFFER *wb, const char *txt) +{ + char b[2] = { [0] = '\0', [1] = '\0' }; + + while(*txt) { + switch(*txt) { + case '&': buffer_strcat(wb, "&"); break; + case '<': buffer_strcat(wb, "<"); break; + case '>': buffer_strcat(wb, ">"); break; + case '"': buffer_strcat(wb, """); break; + case '/': buffer_strcat(wb, "/"); break; + case '\'': buffer_strcat(wb, "'"); break; + default: { + b[0] = *txt; + buffer_strcat(wb, b); + } + } + txt++; + } +} void buffer_snprintf(BUFFER *wb, size_t len, const char *fmt, ...) { diff --git a/src/web_buffer.h b/src/web_buffer.h index ee611209b..8f0d29cd2 100644 --- a/src/web_buffer.h +++ b/src/web_buffer.h @@ -39,6 +39,7 @@ typedef struct web_buffer { #define CT_IMAGE_XICON 19 #define CT_IMAGE_ICNS 20 #define CT_IMAGE_BMP 21 +#define CT_PROMETHEUS 22 #define buffer_cacheable(wb) do { (wb)->options |= WB_CONTENT_CACHEABLE; if((wb)->options & WB_CONTENT_NO_CACHEABLE) (wb)->options &= ~WB_CONTENT_NO_CACHEABLE; } while(0) #define buffer_no_cacheable(wb) do { (wb)->options |= WB_CONTENT_NO_CACHEABLE; if((wb)->options & WB_CONTENT_CACHEABLE) (wb)->options &= ~WB_CONTENT_CACHEABLE; (wb)->expires = 0; } while(0) @@ -61,9 +62,10 @@ extern BUFFER *buffer_create(size_t size); extern void buffer_free(BUFFER *b); extern void buffer_increase(BUFFER *b, size_t free_size_required); -extern void buffer_snprintf(BUFFER *wb, size_t len, const char *fmt, ...) __attribute__ (( format (printf, 3, 4))); +extern void buffer_snprintf(BUFFER *wb, size_t len, const char *fmt, ...) PRINTFLIKE(3, 4); extern void buffer_vsprintf(BUFFER *wb, const char *fmt, va_list args); -extern void buffer_sprintf(BUFFER *wb, const char *fmt, ...) __attribute__ (( format (printf, 2, 3))); +extern void buffer_sprintf(BUFFER *wb, const char *fmt, ...) PRINTFLIKE(2,3); +extern void buffer_strcat_htmlescape(BUFFER *wb, const char *txt); extern void buffer_char_replace(BUFFER *wb, char from, char to); diff --git a/src/web_buffer_svg.c b/src/web_buffer_svg.c index 3e847b5d9..cac365ab1 100644 --- a/src/web_buffer_svg.c +++ b/src/web_buffer_svg.c @@ -290,7 +290,7 @@ static inline int verdana11_width(char *s) { *d = '\0'; w -= VERDANA_KERNING; w += VERDANA_PADDING; - return ceil(w); + return (int)ceil(w); } static inline size_t escape_xmlz(char *dst, const char *src, size_t len) { @@ -470,7 +470,7 @@ static inline void calc_colorz(const char *color, char *final, size_t len, calcu break; } else { - calculated_number v = strtold(value_buffer, NULL); + calculated_number v = str2l(value_buffer); if(comparison == '<' && value < v) break; else if(comparison == '(' && value <= v) break; @@ -525,7 +525,49 @@ void buffer_svg(BUFFER *wb, const char *label, calculated_number value, const ch if(unlikely(isalnum(*units))) separator = " "; - if(unlikely(value_is_null)) + if(unlikely(!strcmp(units, "seconds"))) { + size_t s = (size_t)value; + size_t d = s / 86400; + s = s % 86400; + + size_t h = s / 3600; + s = s % 3600; + + size_t m = s / 60; + s = s % 60; + + if(d) + snprintfz(value_string, VALUE_STRING_SIZE, "%zu %s %02zu:%02zu:%02zu", d, (d == 1)?"day":"days", h, m, s); + else + snprintfz(value_string, VALUE_STRING_SIZE, "%02zu:%02zu:%02zu", h, m, s); + } + + else if(unlikely(!strcmp(units, "minutes"))) { + size_t m = (size_t)value; + size_t d = m / (60 * 24); + m = m % (60 * 24); + + size_t h = m / 60; + m = m % 60; + + if(d) + snprintfz(value_string, VALUE_STRING_SIZE, "%zud %02zuh %02zum", d, h, m); + else + snprintfz(value_string, VALUE_STRING_SIZE, "%zuh %zum", h, m); + } + + else if(unlikely(!strcmp(units, "hours"))) { + size_t h = (size_t)value; + size_t d = h / 24; + h = h % 24; + + if(d) + snprintfz(value_string, VALUE_STRING_SIZE, "%zud %zuh", d, h); + else + snprintfz(value_string, VALUE_STRING_SIZE, "%zuh", h); + } + + else if(unlikely(value_is_null)) strcpy(value_string, "-"); else if(precision < 0) { diff --git a/src/web_client.c b/src/web_client.c index 0cf9eeb6a..4b6ccf646 100644 --- a/src/web_client.c +++ b/src/web_client.c @@ -14,7 +14,7 @@ int web_enable_gzip = 1, web_gzip_level = 3, web_gzip_strategy = Z_DEFAULT_STRAT struct web_client *web_clients = NULL; unsigned long long web_clients_count = 0; -inline int web_client_crock_socket(struct web_client *w) { +static inline int web_client_crock_socket(struct web_client *w) { #ifdef TCP_CORK if(likely(!w->tcp_cork && w->ofd != -1)) { w->tcp_cork = 1; @@ -29,7 +29,7 @@ inline int web_client_crock_socket(struct web_client *w) { return 0; } -inline int web_client_uncrock_socket(struct web_client *w) { +static inline int web_client_uncrock_socket(struct web_client *w) { #ifdef TCP_CORK if(likely(w->tcp_cork && w->ofd != -1)) { w->tcp_cork = 0; @@ -121,11 +121,11 @@ struct web_client *web_client_create(int listener) void web_client_reset(struct web_client *w) { web_client_uncrock_socket(w); - debug(D_WEB_CLIENT, "%llu: Reseting client.", w->id); + debug(D_WEB_CLIENT, "%llu: Resetting client.", w->id); if(likely(w->last_url[0])) { struct timeval tv; - gettimeofday(&tv, NULL); + now_realtime_timeval(&tv); size_t size = (w->mode == WEB_CLIENT_MODE_FILECOPY)?w->response.rlen:w->response.data->len; size_t sent = size; @@ -136,7 +136,7 @@ void web_client_reset(struct web_client *w) { // -------------------------------------------------------------------- // global statistics - finished_web_request_statistics(usec_dt(&tv, &w->tv_in), + finished_web_request_statistics(dt_usec(&tv, &w->tv_in), w->stats_received_bytes, w->stats_sent_bytes, size, @@ -152,9 +152,9 @@ void web_client_reset(struct web_client *w) { log_access("%llu: (sent/all = %zu/%zu bytes %0.0f%%, prep/sent/total = %0.2f/%0.2f/%0.2f ms) %s: %d '%s'", w->id, sent, size, -((size > 0) ? ((size - sent) / (double) size * 100.0) : 0.0), - usec_dt(&w->tv_ready, &w->tv_in) / 1000.0, - usec_dt(&tv, &w->tv_ready) / 1000.0, - usec_dt(&tv, &w->tv_in) / 1000.0, + dt_usec(&w->tv_ready, &w->tv_in) / 1000.0, + dt_usec(&tv, &w->tv_ready) / 1000.0, + dt_usec(&tv, &w->tv_in) / 1000.0, (w->mode == WEB_CLIENT_MODE_FILECOPY) ? "filecopy" : ((w->mode == WEB_CLIENT_MODE_OPTIONS) ? "options" : "data"), w->response.code, @@ -308,7 +308,8 @@ int mysendfile(struct web_client *w, char *filename) for(s = filename; *s ;s++) { if( !isalnum(*s) && *s != '/' && *s != '.' && *s != '-' && *s != '_') { debug(D_WEB_CLIENT_ACCESS, "%llu: File '%s' is not acceptable.", w->id, filename); - buffer_sprintf(w->response.data, "File '%s' cannot be served. Filename contains invalid character '%c'", filename, *s); + buffer_sprintf(w->response.data, "Filename contains invalid characters: "); + buffer_strcat_htmlescape(w->response.data, filename); return 400; } } @@ -316,7 +317,8 @@ int mysendfile(struct web_client *w, char *filename) // if the filename contains a .. refuse to serve it if(strstr(filename, "..") != 0) { debug(D_WEB_CLIENT_ACCESS, "%llu: File '%s' is not acceptable.", w->id, filename); - buffer_sprintf(w->response.data, "File '%s' cannot be served. Relative filenames with '..' in them are not supported.", filename); + buffer_strcat(w->response.data, "Relative filenames are not supported: "); + buffer_strcat_htmlescape(w->response.data, filename); return 400; } @@ -328,21 +330,24 @@ int mysendfile(struct web_client *w, char *filename) struct stat stat; if(lstat(webfilename, &stat) != 0) { debug(D_WEB_CLIENT_ACCESS, "%llu: File '%s' is not found.", w->id, webfilename); - buffer_sprintf(w->response.data, "File '%s' does not exist, or is not accessible.", webfilename); + buffer_strcat(w->response.data, "File does not exist, or is not accessible: "); + buffer_strcat_htmlescape(w->response.data, webfilename); return 404; } // check if the file is owned by expected user if(stat.st_uid != web_files_uid()) { error("%llu: File '%s' is owned by user %u (expected user %u). Access Denied.", w->id, webfilename, stat.st_uid, web_files_uid()); - buffer_sprintf(w->response.data, "Access to file '%s' is not permitted.", webfilename); + buffer_strcat(w->response.data, "Access to file is not permitted: "); + buffer_strcat_htmlescape(w->response.data, webfilename); return 403; } // check if the file is owned by expected group if(stat.st_gid != web_files_gid()) { error("%llu: File '%s' is owned by group %u (expected group %u). Access Denied.", w->id, webfilename, stat.st_gid, web_files_gid()); - buffer_sprintf(w->response.data, "Access to file '%s' is not permitted.", webfilename); + buffer_strcat(w->response.data, "Access to file is not permitted: "); + buffer_strcat_htmlescape(w->response.data, webfilename); return 403; } @@ -353,7 +358,8 @@ int mysendfile(struct web_client *w, char *filename) if((stat.st_mode & S_IFMT) != S_IFREG) { error("%llu: File '%s' is not a regular file. Access Denied.", w->id, webfilename); - buffer_sprintf(w->response.data, "Access to file '%s' is not permitted.", webfilename); + buffer_strcat(w->response.data, "Access to file is not permitted: "); + buffer_strcat_htmlescape(w->response.data, webfilename); return 403; } @@ -365,12 +371,14 @@ int mysendfile(struct web_client *w, char *filename) if(errno == EBUSY || errno == EAGAIN) { error("%llu: File '%s' is busy, sending 307 Moved Temporarily to force retry.", w->id, webfilename); buffer_sprintf(w->response.header, "Location: /" WEB_PATH_FILE "/%s\r\n", filename); - buffer_sprintf(w->response.data, "The file '%s' is currently busy. Please try again later.", webfilename); + buffer_strcat(w->response.data, "File is currently busy, please try again later: "); + buffer_strcat_htmlescape(w->response.data, webfilename); return 307; } else { error("%llu: Cannot open file '%s'.", w->id, webfilename); - buffer_sprintf(w->response.data, "Cannot open file '%s'.", webfilename); + buffer_strcat(w->response.data, "Cannot open file: "); + buffer_strcat_htmlescape(w->response.data, webfilename); return 404; } } @@ -406,7 +414,11 @@ int mysendfile(struct web_client *w, char *filename) w->wait_send = 0; buffer_flush(w->response.data); w->response.rlen = stat.st_size; +#ifdef __APPLE__ + w->response.data->date = stat.st_mtimespec.tv_sec; +#else w->response.data->date = stat.st_mtim.tv_sec; +#endif /* __APPLE__ */ buffer_cacheable(w->response.data); return 200; @@ -416,7 +428,7 @@ int mysendfile(struct web_client *w, char *filename) #ifdef NETDATA_WITH_ZLIB void web_client_enable_deflate(struct web_client *w, int gzip) { if(unlikely(w->response.zinitialized)) { - error("%llu: Compression has already be initialized for this client.", w->id); + debug(D_DEFLATE, "%llu: Compression has already be initialized for this client.", w->id); return; } @@ -490,7 +502,7 @@ void buffer_data_options2string(BUFFER *wb, uint32_t options) { if(options & RRDR_OPTION_ABSOLUTE) { if(count++) buffer_strcat(wb, " "); - buffer_strcat(wb, "abs"); + buffer_strcat(wb, "absolute"); } if(options & RRDR_OPTION_SECONDS) { @@ -700,17 +712,7 @@ int web_client_api_request_v1_alarm_log(struct web_client *w, char *url) return 200; } -int web_client_api_request_v1_charts(struct web_client *w, char *url) -{ - (void)url; - - buffer_flush(w->response.data); - w->response.data->contenttype = CT_APPLICATION_JSON; - rrd_stats_api_v1_charts(w->response.data); - return 200; -} - -int web_client_api_request_v1_chart(struct web_client *w, char *url) +int web_client_api_request_single_chart(struct web_client *w, char *url, void callback(RRDSET *st, BUFFER *buf)) { int ret = 400; char *chart = NULL; @@ -743,19 +745,83 @@ int web_client_api_request_v1_chart(struct web_client *w, char *url) RRDSET *st = rrdset_find(chart); if(!st) st = rrdset_find_byname(chart); if(!st) { - buffer_sprintf(w->response.data, "Chart '%s' is not found.", chart); + buffer_strcat(w->response.data, "Chart is not found: "); + buffer_strcat_htmlescape(w->response.data, chart); ret = 404; goto cleanup; } w->response.data->contenttype = CT_APPLICATION_JSON; - rrd_stats_api_v1_chart(st, w->response.data); + callback(st, w->response.data); return 200; -cleanup: + cleanup: return ret; } +int web_client_api_request_v1_alarm_variables(struct web_client *w, char *url) +{ + return web_client_api_request_single_chart(w, url, health_api_v1_chart_variables2json); +} + +int web_client_api_request_v1_charts(struct web_client *w, char *url) +{ + (void)url; + + buffer_flush(w->response.data); + w->response.data->contenttype = CT_APPLICATION_JSON; + rrd_stats_api_v1_charts(w->response.data); + return 200; +} + +int web_client_api_request_v1_allmetrics(struct web_client *w, char *url) +{ + int format = ALLMETRICS_SHELL; + + while(url) { + char *value = mystrsep(&url, "?&"); + if (!value || !*value) continue; + + char *name = mystrsep(&value, "="); + if(!name || !*name) continue; + if(!value || !*value) continue; + + if(!strcmp(name, "format")) { + if(!strcmp(value, ALLMETRICS_FORMAT_SHELL)) + format = ALLMETRICS_SHELL; + else if(!strcmp(value, ALLMETRICS_FORMAT_PROMETHEUS)) + format = ALLMETRICS_PROMETHEUS; + else + format = 0; + } + } + + buffer_flush(w->response.data); + buffer_no_cacheable(w->response.data); + + switch(format) { + case ALLMETRICS_SHELL: + w->response.data->contenttype = CT_TEXT_PLAIN; + rrd_stats_api_v1_charts_allmetrics_shell(w->response.data); + return 200; + + case ALLMETRICS_PROMETHEUS: + w->response.data->contenttype = CT_PROMETHEUS; + rrd_stats_api_v1_charts_allmetrics_prometheus(w->response.data); + return 200; + + default: + w->response.data->contenttype = CT_TEXT_PLAIN; + buffer_strcat(w->response.data, "Which format? Only '" ALLMETRICS_FORMAT_SHELL "' and '" ALLMETRICS_FORMAT_PROMETHEUS "' is currently supported."); + return 400; + } +} + +int web_client_api_request_v1_chart(struct web_client *w, char *url) +{ + return web_client_api_request_single_chart(w, url, rrd_stats_api_v1_chart); +} + int web_client_api_request_v1_badge(struct web_client *w, char *url) { int ret = 400; buffer_flush(w->response.data); @@ -846,12 +912,12 @@ int web_client_api_request_v1_badge(struct web_client *w, char *url) { } } - long long multiply = (multiply_str && *multiply_str )?atol(multiply_str):1; - long long divide = (divide_str && *divide_str )?atol(divide_str):1; - long long before = (before_str && *before_str )?atol(before_str):0; - long long after = (after_str && *after_str )?atol(after_str):-st->update_every; - int points = (points_str && *points_str )?atoi(points_str):1; - int precision = (precision_str && *precision_str)?atoi(precision_str):-1; + long long multiply = (multiply_str && *multiply_str )?str2l(multiply_str):1; + long long divide = (divide_str && *divide_str )?str2l(divide_str):1; + long long before = (before_str && *before_str )?str2l(before_str):0; + long long after = (after_str && *after_str )?str2l(after_str):-st->update_every; + int points = (points_str && *points_str )?str2i(points_str):1; + int precision = (precision_str && *precision_str)?str2i(precision_str):-1; if(!multiply) multiply = 1; if(!divide) divide = 1; @@ -868,7 +934,7 @@ int web_client_api_request_v1_badge(struct web_client *w, char *url) { } } else { - refresh = atoi(refresh_str); + refresh = str2i(refresh_str); if(refresh < 0) refresh = -refresh; } } @@ -921,7 +987,7 @@ int web_client_api_request_v1_badge(struct web_client *w, char *url) { if (refresh > 0) { buffer_sprintf(w->response.header, "Refresh: %d\r\n", refresh); - w->response.data->expires = time(NULL) + refresh; + w->response.data->expires = now_realtime_sec() + refresh; } else buffer_no_cacheable(w->response.data); @@ -970,7 +1036,7 @@ int web_client_api_request_v1_badge(struct web_client *w, char *url) { ret = 500; // if the collected value is too old, don't calculate its value - if (rrdset_last_entry_t(st) >= (time(NULL) - (st->update_every * st->gap_when_lost_iterations_above))) + if (rrdset_last_entry_t(st) >= (now_realtime_sec() - (st->update_every * st->gap_when_lost_iterations_above))) ret = rrd2value(st, w->response.data, &n, @@ -993,7 +1059,7 @@ int web_client_api_request_v1_badge(struct web_client *w, char *url) { } else if (refresh > 0) { buffer_sprintf(w->response.header, "Refresh: %d\r\n", refresh); - w->response.data->expires = time(NULL) + refresh; + w->response.data->expires = now_realtime_sec() + refresh; } else buffer_no_cacheable(w->response.data); @@ -1120,14 +1186,15 @@ int web_client_api_request_v1_data(struct web_client *w, char *url) RRDSET *st = rrdset_find(chart); if(!st) st = rrdset_find_byname(chart); if(!st) { - buffer_sprintf(w->response.data, "Chart '%s' is not found.", chart); + buffer_strcat(w->response.data, "Chart is not found: "); + buffer_strcat_htmlescape(w->response.data, chart); ret = 404; goto cleanup; } - long long before = (before_str && *before_str)?atol(before_str):0; - long long after = (after_str && *after_str) ?atol(after_str):0; - int points = (points_str && *points_str)?atoi(points_str):0; + long long before = (before_str && *before_str)?str2l(before_str):0; + long long after = (after_str && *after_str) ?str2l(after_str):0; + int points = (points_str && *points_str)?str2i(points_str):0; debug(D_WEB_CLIENT, "%llu: API command 'data' for chart '%s', dimensions '%s', after '%lld', before '%lld', points '%d', group '%d', format '%u', options '0x%08x'" , w->id @@ -1189,8 +1256,6 @@ cleanup: } -#define REGISTRY_VERIFY_COOKIES_GUID "give-me-back-this-cookie-now--please" - int web_client_api_request_v1_registry(struct web_client *w, char *url) { static uint32_t hash_action = 0, hash_access = 0, hash_hello = 0, hash_delete = 0, hash_search = 0, @@ -1215,7 +1280,7 @@ int web_client_api_request_v1_registry(struct web_client *w, char *url) */ } - char person_guid[36 + 1] = ""; + char person_guid[GUID_LEN + 1] = ""; debug(D_WEB_CLIENT, "%llu: API v1 registry with URL '%s'", w->id, url); @@ -1299,100 +1364,50 @@ int web_client_api_request_v1_registry(struct web_client *w, char *url) } if(action == 'A' && (!machine_guid || !machine_url || !url_name)) { + error("Invalid registry request - access requires these parameters: machine ('%s'), url ('%s'), name ('%s')", + machine_guid?machine_guid:"UNSET", machine_url?machine_url:"UNSET", url_name?url_name:"UNSET"); buffer_flush(w->response.data); - buffer_sprintf(w->response.data, "Invalid registry request - access requires these parameters: machine ('%s'), url ('%s'), name ('%s')", - machine_guid?machine_guid:"UNSET", machine_url?machine_url:"UNSET", url_name?url_name:"UNSET"); + buffer_strcat(w->response.data, "Invalid registry Access request."); return 400; } else if(action == 'D' && (!machine_guid || !machine_url || !delete_url)) { + error("Invalid registry request - delete requires these parameters: machine ('%s'), url ('%s'), delete_url ('%s')", + machine_guid?machine_guid:"UNSET", machine_url?machine_url:"UNSET", delete_url?delete_url:"UNSET"); buffer_flush(w->response.data); - buffer_sprintf(w->response.data, "Invalid registry request - delete requires these parameters: machine ('%s'), url ('%s'), delete_url ('%s')", - machine_guid?machine_guid:"UNSET", machine_url?machine_url:"UNSET", delete_url?delete_url:"UNSET"); + buffer_strcat(w->response.data, "Invalid registry Delete request."); return 400; } else if(action == 'S' && (!machine_guid || !machine_url || !search_machine_guid)) { + error("Invalid registry request - search requires these parameters: machine ('%s'), url ('%s'), for ('%s')", + machine_guid?machine_guid:"UNSET", machine_url?machine_url:"UNSET", search_machine_guid?search_machine_guid:"UNSET"); buffer_flush(w->response.data); - buffer_sprintf(w->response.data, "Invalid registry request - search requires these parameters: machine ('%s'), url ('%s'), for ('%s')", - machine_guid?machine_guid:"UNSET", machine_url?machine_url:"UNSET", search_machine_guid?search_machine_guid:"UNSET"); + buffer_strcat(w->response.data, "Invalid registry Search request."); return 400; } else if(action == 'W' && (!machine_guid || !machine_url || !to_person_guid)) { + error("Invalid registry request - switching identity requires these parameters: machine ('%s'), url ('%s'), to ('%s')", + machine_guid?machine_guid:"UNSET", machine_url?machine_url:"UNSET", to_person_guid?to_person_guid:"UNSET"); buffer_flush(w->response.data); - buffer_sprintf(w->response.data, "Invalid registry request - switching identity requires these parameters: machine ('%s'), url ('%s'), to ('%s')", - machine_guid?machine_guid:"UNSET", machine_url?machine_url:"UNSET", to_person_guid?to_person_guid:"UNSET"); + buffer_strcat(w->response.data, "Invalid registry Switch request."); return 400; } switch(action) { case 'A': w->tracking_required = 1; - if(registry_verify_cookies_redirects() > 0 && (!cookie || !person_guid[0])) { - buffer_flush(w->response.data); - registry_set_cookie(w, REGISTRY_VERIFY_COOKIES_GUID); - w->response.data->contenttype = CT_APPLICATION_JSON; - buffer_sprintf(w->response.data, "{ \"status\": \"redirect\", \"registry\": \"%s\" }", registry_to_announce()); - return 200; - -/* - * it seems that web browsers are ignoring 307 (Moved Temporarily) - * under certain conditions, when using CORS - * so this is commented and we use application level redirects instead - * - redirects++; - - if(redirects > registry_verify_cookies_redirects()) { - buffer_flush(w->response.data); - buffer_sprintf(w->response.data, "Your browser does not support cookies"); - return 400; - } - - char *encoded_url = url_encode(machine_url); - if(!encoded_url) { - error("%llu: Cannot URL encode string '%s'", w->id, machine_url); - return 500; - } - - char *encoded_name = url_encode(url_name); - if(!encoded_name) { - free(encoded_url); - error("%llu: Cannot URL encode string '%s'", w->id, url_name); - return 500; - } - - char *encoded_guid = url_encode(machine_guid); - if(!encoded_guid) { - free(encoded_url); - free(encoded_name); - error("%llu: Cannot URL encode string '%s'", w->id, machine_guid); - return 500; - } - - buffer_sprintf(w->response.header, "Location: %s/api/v1/registry?action=access&machine=%s&name=%s&url=%s&redirects=%d\r\n", - registry_to_announce(), encoded_guid, encoded_name, encoded_url, redirects); - - free(encoded_guid); - free(encoded_name); - free(encoded_url); - return 307 -*/ - } - - if(unlikely(cookie && person_guid[0] && !strcmp(person_guid, REGISTRY_VERIFY_COOKIES_GUID))) - person_guid[0] = '\0'; - - return registry_request_access_json(w, person_guid, machine_guid, machine_url, url_name, time(NULL)); + return registry_request_access_json(w, person_guid, machine_guid, machine_url, url_name, now_realtime_sec()); case 'D': w->tracking_required = 1; - return registry_request_delete_json(w, person_guid, machine_guid, machine_url, delete_url, time(NULL)); + return registry_request_delete_json(w, person_guid, machine_guid, machine_url, delete_url, now_realtime_sec()); case 'S': w->tracking_required = 1; - return registry_request_search_json(w, person_guid, machine_guid, machine_url, search_machine_guid, time(NULL)); + return registry_request_search_json(w, person_guid, machine_guid, machine_url, search_machine_guid, now_realtime_sec()); case 'W': w->tracking_required = 1; - return registry_request_switch_json(w, person_guid, machine_guid, machine_url, to_person_guid, time(NULL)); + return registry_request_switch_json(w, person_guid, machine_guid, machine_url, to_person_guid, now_realtime_sec()); case 'H': return registry_request_hello_json(w); @@ -1405,7 +1420,7 @@ int web_client_api_request_v1_registry(struct web_client *w, char *url) } int web_client_api_request_v1(struct web_client *w, char *url) { - static uint32_t hash_data = 0, hash_chart = 0, hash_charts = 0, hash_registry = 0, hash_badge = 0, hash_alarms = 0, hash_alarm_log = 0; + static uint32_t hash_data = 0, hash_chart = 0, hash_charts = 0, hash_registry = 0, hash_badge = 0, hash_alarms = 0, hash_alarm_log = 0, hash_alarm_variables = 0, hash_raw = 0; if(unlikely(hash_data == 0)) { hash_data = simple_hash("data"); @@ -1415,6 +1430,8 @@ int web_client_api_request_v1(struct web_client *w, char *url) { hash_badge = simple_hash("badge.svg"); hash_alarms = simple_hash("alarms"); hash_alarm_log = simple_hash("alarm_log"); + hash_alarm_variables = simple_hash("alarm_variables"); + hash_raw = simple_hash("allmetrics"); } // get the command @@ -1444,15 +1461,22 @@ int web_client_api_request_v1(struct web_client *w, char *url) { else if(hash == hash_alarm_log && !strcmp(tok, "alarm_log")) return web_client_api_request_v1_alarm_log(w, url); + else if(hash == hash_alarm_variables && !strcmp(tok, "alarm_variables")) + return web_client_api_request_v1_alarm_variables(w, url); + + else if(hash == hash_raw && !strcmp(tok, "allmetrics")) + return web_client_api_request_v1_allmetrics(w, url); + else { buffer_flush(w->response.data); - buffer_sprintf(w->response.data, "Unsupported v1 API command: %s", tok); + buffer_strcat(w->response.data, "Unsupported v1 API command: "); + buffer_strcat_htmlescape(w->response.data, tok); return 404; } } else { buffer_flush(w->response.data); - buffer_sprintf(w->response.data, "API v1 command?"); + buffer_sprintf(w->response.data, "Which API v1 command?"); return 400; } } @@ -1467,7 +1491,8 @@ int web_client_api_request(struct web_client *w, char *url) return web_client_api_request_v1(w, url); else { buffer_flush(w->response.data); - buffer_sprintf(w->response.data, "Unsupported API version: %s", tok); + buffer_strcat(w->response.data, "Unsupported API version: "); + buffer_strcat_htmlescape(w->response.data, tok); return 404; } } @@ -1480,6 +1505,12 @@ int web_client_api_request(struct web_client *w, char *url) int web_client_api_old_data_request(struct web_client *w, char *url, int datasource_type) { + if(!url || !*url) { + buffer_flush(w->response.data); + buffer_sprintf(w->response.data, "Incomplete request."); + return 400; + } + RRDSET *st = NULL; char *args = strchr(url, '?'); @@ -1519,13 +1550,13 @@ int web_client_api_old_data_request(struct web_client *w, char *url, int datasou if(url) { // parse the lines required tok = mystrsep(&url, "/"); - if(tok) lines = atoi(tok); + if(tok) lines = str2i(tok); if(lines < 1) lines = 1; } if(url) { // parse the group count required tok = mystrsep(&url, "/"); - if(tok && *tok) group_count = atoi(tok); + if(tok && *tok) group_count = str2i(tok); if(group_count < 1) group_count = 1; //if(group_count > save_history / 20) group_count = save_history / 20; } @@ -1542,13 +1573,13 @@ int web_client_api_old_data_request(struct web_client *w, char *url, int datasou if(url) { // parse after time tok = mystrsep(&url, "/"); - if(tok && *tok) after = strtoul(tok, NULL, 10); + if(tok && *tok) after = str2ul(tok); if(after < 0) after = 0; } if(url) { // parse before time tok = mystrsep(&url, "/"); - if(tok && *tok) before = strtoul(tok, NULL, 10); + if(tok && *tok) before = str2ul(tok); if(before < 0) before = 0; } if(url) { @@ -1707,6 +1738,9 @@ const char *web_content_type_to_string(uint8_t contenttype) { case CT_IMAGE_ICNS: return "image/icns"; + case CT_PROMETHEUS: + return "text/plain; version=0.0.4"; + default: case CT_TEXT_PLAIN: return "text/plain; charset=utf-8"; @@ -1917,7 +1951,7 @@ void web_client_process(struct web_client *w) { #endif // start timing us - gettimeofday(&w->tv_in, NULL); + now_realtime_timeval(&w->tv_in); if(unlikely(!hash_api)) { hash_api = simple_hash("api"); @@ -2063,7 +2097,6 @@ void web_client_process(struct web_client *w) { error("web request to exit received."); netdata_cleanup_and_exit(0); - netdata_exit = 1; } else if(hash == hash_debug && strcmp(tok, "debug") == 0) { buffer_flush(w->response.data); @@ -2078,14 +2111,16 @@ void web_client_process(struct web_client *w) { if(!st) st = rrdset_find(tok); if(!st) { code = 404; - buffer_sprintf(w->response.data, "Chart %s is not found.\r\n", tok); + buffer_strcat(w->response.data, "Chart is not found: "); + buffer_strcat_htmlescape(w->response.data, tok); debug(D_WEB_CLIENT_ACCESS, "%llu: %s is not found.", w->id, tok); } else { code = 200; debug_flags |= D_RRD_STATS; st->debug = !st->debug; - buffer_sprintf(w->response.data, "Chart %s has now debug %s.\r\n", tok, st->debug?"enabled":"disabled"); + buffer_sprintf(w->response.data, "Chart has now debug %s: ", st->debug?"enabled":"disabled"); + buffer_strcat_htmlescape(w->response.data, tok); debug(D_WEB_CLIENT_ACCESS, "%llu: debug for %s is %s.", w->id, tok, st->debug?"enabled":"disabled"); } } @@ -2127,7 +2162,7 @@ void web_client_process(struct web_client *w) { } } - gettimeofday(&w->tv_ready, NULL); + now_realtime_timeval(&w->tv_ready); w->response.sent = 0; w->response.code = code; @@ -2210,7 +2245,7 @@ void web_client_process(struct web_client *w) { if(w->mode == WEB_CLIENT_MODE_OPTIONS) { buffer_strcat(w->response.header_output, "Access-Control-Allow-Methods: GET, OPTIONS\r\n" - "Access-Control-Allow-Headers: accept, x-requested-with, origin, content-type, cookie\r\n" + "Access-Control-Allow-Headers: accept, x-requested-with, origin, content-type, cookie, pragma, cache-control\r\n" "Access-Control-Max-Age: 1209600\r\n" // 86400 * 14 ); } diff --git a/src/web_server.c b/src/web_server.c index cbbe6bb40..8e942a59d 100644 --- a/src/web_server.c +++ b/src/web_server.c @@ -7,6 +7,8 @@ char *listen_fds_names[MAX_LISTEN_FDS] = { [0 ... 99] = NULL }; int listen_port = LISTEN_PORT; int web_server_mode = WEB_SERVER_MODE_MULTI_THREADED; +static int shown_server_socket_error = 0; + #ifdef NETDATA_INTERNAL_CHECKS static void log_allocations(void) { @@ -90,6 +92,7 @@ int create_listen_socket4(const char *ip, int port, int listen_backlog) { sock = socket(AF_INET, SOCK_STREAM, 0); if(sock < 0) { error("IPv4 socket() on ip '%s' port %d failed.", ip, port); + shown_server_socket_error = 1; return -1; } @@ -105,6 +108,7 @@ int create_listen_socket4(const char *ip, int port, int listen_backlog) { int ret = inet_pton(AF_INET, ip, (void *)&name.sin_addr.s_addr); if(ret != 1) { error("Failed to convert IP '%s' to a valid IPv4 address.", ip); + shown_server_socket_error = 1; close(sock); return -1; } @@ -112,12 +116,14 @@ int create_listen_socket4(const char *ip, int port, int listen_backlog) { if(bind (sock, (struct sockaddr *) &name, sizeof (name)) < 0) { close(sock); error("IPv4 bind() on ip '%s' port %d failed.", ip, port); + shown_server_socket_error = 1; return -1; } if(listen(sock, listen_backlog) < 0) { close(sock); - fatal("IPv4 listen() on ip '%s' port %d failed.", ip, port); + error("IPv4 listen() on ip '%s' port %d failed.", ip, port); + shown_server_socket_error = 1; return -1; } @@ -135,6 +141,7 @@ int create_listen_socket6(const char *ip, int port, int listen_backlog) { sock = socket(AF_INET6, SOCK_STREAM, 0); if (sock < 0) { error("IPv6 socket() on ip '%s' port %d failed.", ip, port); + shown_server_socket_error = 1; return -1; } @@ -154,6 +161,7 @@ int create_listen_socket6(const char *ip, int port, int listen_backlog) { int ret = inet_pton(AF_INET6, ip, (void *)&name.sin6_addr.s6_addr); if(ret != 1) { error("Failed to convert IP '%s' to a valid IPv6 address.", ip); + shown_server_socket_error = 1; close(sock); return -1; } @@ -163,12 +171,14 @@ int create_listen_socket6(const char *ip, int port, int listen_backlog) { if (bind (sock, (struct sockaddr *) &name, sizeof (name)) < 0) { close(sock); error("IPv6 bind() on ip '%s' port %d failed.", ip, port); + shown_server_socket_error = 1; return -1; } if (listen(sock, listen_backlog) < 0) { close(sock); error("IPv6 listen() on ip '%s' port %d failed.", ip, port); + shown_server_socket_error = 1; return -1; } @@ -179,6 +189,7 @@ int create_listen_socket6(const char *ip, int port, int listen_backlog) { static inline int add_listen_socket(int fd, const char *ip, int port) { if(listen_fds_count >= MAX_LISTEN_FDS) { error("Too many listening sockets. Failed to add listening socket at ip '%s' port %d", ip, port); + shown_server_socket_error = 1; close(fd); return -1; } @@ -290,7 +301,7 @@ static inline int bind_to_one(const char *definition, int default_port, int list } if (fd == -1) - error("Cannot bind to ip '%s', port %d", rip, default_port); + error("Cannot bind to ip '%s', port %d", rip, rport); else { add_listen_socket(fd, rip, rport); added++; @@ -303,6 +314,8 @@ static inline int bind_to_one(const char *definition, int default_port, int list } int create_listen_sockets(void) { + shown_server_socket_error = 0; + listen_backlog = (int) config_get_number("global", "http port listen backlog", LISTEN_BACKLOG); if(config_exists("global", "bind socket to IP") && !config_exists("global", "bind to")) @@ -340,6 +353,11 @@ int create_listen_sockets(void) { if(!listen_fds_count) fatal("Cannot listen on any socket. Exiting..."); + else if(shown_server_socket_error) { + size_t i; + for(i = 0; i < listen_fds_count ;i++) + info("Listen socket %s opened.", listen_fds_names[i]); + } return (int)listen_fds_count; } @@ -372,7 +390,7 @@ static inline void cleanup_web_clients(void) { #define CLEANUP_EVERY_EVENTS 100 void *socket_listen_main_multi_threaded(void *ptr) { - (void)ptr; + struct netdata_static_thread *static_thread = (struct netdata_static_thread *)ptr; web_server_mode = WEB_SERVER_MODE_MULTI_THREADED; info("Multi-threaded WEB SERVER thread created with task id %d", gettid()); @@ -452,6 +470,10 @@ void *socket_listen_main_multi_threaded(void *ptr) { debug(D_WEB_CLIENT, "LISTENER: exit!"); close_listen_sockets(); + freez(fds); + + static_thread->enabled = 0; + pthread_exit(NULL); return NULL; } @@ -500,7 +522,7 @@ static inline int single_threaded_unlink_client(struct web_client *w, fd_set *if } void *socket_listen_main_single_threaded(void *ptr) { - (void)ptr; + struct netdata_static_thread *static_thread = (struct netdata_static_thread *)ptr; web_server_mode = WEB_SERVER_MODE_SINGLE_THREADED; @@ -619,5 +641,8 @@ void *socket_listen_main_single_threaded(void *ptr) { debug(D_WEB_CLIENT, "LISTENER: exit!"); close_listen_sockets(); + + static_thread->enabled = 0; + pthread_exit(NULL); return NULL; } diff --git a/system/Makefile.in b/system/Makefile.in index df5eeb080..749b99f4b 100644 --- a/system/Makefile.in +++ b/system/Makefile.in @@ -1,7 +1,7 @@ -# Makefile.in generated by automake 1.14.1 from Makefile.am. +# Makefile.in generated by automake 1.15 from Makefile.am. # @configure_input@ -# Copyright (C) 1994-2013 Free Software Foundation, Inc. +# Copyright (C) 1994-2014 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -15,7 +15,17 @@ @SET_MAKE@ VPATH = @srcdir@ -am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ @@ -78,8 +88,6 @@ PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ -DIST_COMMON = $(top_srcdir)/build/subst.inc $(srcdir)/Makefile.in \ - $(srcdir)/Makefile.am $(dist_noinst_DATA) subdir = system ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_c___atomic.m4 \ @@ -87,10 +95,13 @@ am__aclocal_m4_deps = $(top_srcdir)/m4/ax_c___atomic.m4 \ $(top_srcdir)/m4/ax_c_mallinfo.m4 \ $(top_srcdir)/m4/ax_c_mallopt.m4 \ $(top_srcdir)/m4/ax_check_compile_flag.m4 \ + $(top_srcdir)/m4/ax_gcc_func_attribute.m4 \ $(top_srcdir)/m4/ax_pthread.m4 $(top_srcdir)/m4/jemalloc.m4 \ $(top_srcdir)/m4/tcmalloc.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(dist_noinst_DATA) \ + $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = @@ -116,6 +127,7 @@ am__can_run_installinfo = \ esac DATA = $(dist_noinst_DATA) $(nodist_noinst_DATA) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/build/subst.inc DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ @@ -300,7 +312,6 @@ $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu system/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu system/Makefile -.PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ @@ -309,7 +320,7 @@ Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; -$(top_srcdir)/build/subst.inc: +$(top_srcdir)/build/subst.inc $(am__empty): $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh @@ -471,6 +482,8 @@ uninstall-am: maintainer-clean-generic mostlyclean mostlyclean-generic pdf \ pdf-am ps ps-am tags-am uninstall uninstall-am +.PRECIOUS: Makefile + .in: if sed \ -e 's#[@]localstatedir_POST@#$(localstatedir)#g' \ diff --git a/system/netdata-init-d.in b/system/netdata-init-d.in index edda9950f..468ceee2a 100644 --- a/system/netdata-init-d.in +++ b/system/netdata-init-d.in @@ -17,12 +17,18 @@ PIDFILE=@localstatedir_POST@/run/$DAEMON.pid DAEMONOPTS="-P $PIDFILE" STOP_TIMEOUT="10" +[ -e /etc/sysconfig/$DAEMON ] && . /etc/sysconfig/$DAEMON + +LOCKFILE=/var/lock/subsys/$DAEMON + service_start() { - echo "Starting $DAEMON..." + [ -x $DAEMON_PATH ] || exit 5 + echo -n "Starting $DAEMON..." daemon $DAEMON_PATH/$DAEMON $DAEMONOPTS RETVAL=$? echo + [ $RETVAL -eq 0 ] && touch $LOCKFILE return $RETVAL } @@ -31,10 +37,8 @@ service_stop() printf "%-50s" "Stopping $DAEMON..." killproc -p ${PIDFILE} -d ${STOP_TIMEOUT} $DAEMON RETVAL=$? - if [ $RETVAL -eq 0 ]; then - rm -f ${PIDFILE} - fi echo + [ $RETVAL -eq 0 ] && rm -f ${PIDFILE} ${LOCKFILE} return $RETVAL } @@ -54,11 +58,18 @@ service_status() status -p ${PIDFILE} $DAEMON_PATH/$DAEMON } +service_status_quiet() +{ + status -p ${PIDFILE} $DAEMON_PATH/$DAEMON >/dev/null 2>&1 +} + case "$1" in start) + service_status_quiet && exit 0 service_start ;; stop) + service_status_quiet || exit 0 service_stop ;; restart) diff --git a/web/.well-known/dnt/cookies b/web/.well-known/dnt/cookies deleted file mode 100644 index dc2fe0b4a..000000000 --- a/web/.well-known/dnt/cookies +++ /dev/null @@ -1,14 +0,0 @@ -{
- "tracking": "T",
- "compliance": ["https://github.com/firehol/netdata/wiki/cookies#compliance"],
- "qualifiers": "afc",
- "controller": ["https://github.com/firehol/netdata/wiki/cookies#controller"],
- "same-party": [
- "my-netdata.io",
- "mynetdata.io",
- "netdata.online",
- "netdata.rocks",
- "registry.my-netdata.io"
- ],
- "policy": "https://github.com/firehol/netdata/wiki/cookies#policy",
-}
diff --git a/web/Makefile.am b/web/Makefile.am index d0795c059..396cf0be8 100644 --- a/web/Makefile.am +++ b/web/Makefile.am @@ -7,6 +7,7 @@ dist_web_DATA = \ demo.html \ demo2.html \ demosites.html \ + demosites2.html \ dashboard.html \ dashboard.js \ dashboard_info.js \ @@ -19,6 +20,7 @@ dist_web_DATA = \ netdata-swagger.json \ robots.txt \ registry.html \ + sitemap.xml \ tv.html \ version.txt \ $(NULL) @@ -45,7 +47,7 @@ dist_weblib_DATA = \ lib/gauge-d5260c3.min.js \ lib/jquery-2.2.4.min.js \ lib/jquery.easypiechart-97b5824.min.js \ - lib/jquery.nanoscroller-0.8.7.min.js \ + lib/perfect-scrollbar-0.6.15.min.js \ lib/jquery.peity-3.2.0.min.js \ lib/jquery.sparkline-2.1.2.min.js \ lib/morris-0.5.1.min.js \ @@ -56,9 +58,9 @@ dist_weblib_DATA = \ webcssdir=$(webdir)/css dist_webcss_DATA = \ css/morris-0.5.1.css \ - css/bootstrap-3.3.7.min.css \ + css/bootstrap-3.3.7.css \ css/bootstrap-theme-3.3.7.min.css \ - css/bootstrap.slate.min.css \ + css/bootstrap-slate-flat-3.3.7.css \ css/bootstrap-toggle-2.2.2.min.css \ css/font-awesome.min.css \ css/c3-0.4.11.min.css \ diff --git a/web/Makefile.in b/web/Makefile.in index d908f472f..e09392e9e 100644 --- a/web/Makefile.in +++ b/web/Makefile.in @@ -1,7 +1,7 @@ -# Makefile.in generated by automake 1.14.1 from Makefile.am. +# Makefile.in generated by automake 1.15 from Makefile.am. # @configure_input@ -# Copyright (C) 1994-2013 Free Software Foundation, Inc. +# Copyright (C) 1994-2014 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -15,7 +15,17 @@ @SET_MAKE@ VPATH = @srcdir@ -am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ @@ -79,21 +89,21 @@ POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = web -DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ - $(dist_web_DATA) $(dist_webcss_DATA) $(dist_webdnt_DATA) \ - $(dist_webfonts_DATA) $(dist_webimages_DATA) \ - $(dist_weblib_DATA) $(dist_webold_DATA) \ - $(dist_webwellknown_DATA) ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_c___atomic.m4 \ $(top_srcdir)/m4/ax_c__generic.m4 \ $(top_srcdir)/m4/ax_c_mallinfo.m4 \ $(top_srcdir)/m4/ax_c_mallopt.m4 \ $(top_srcdir)/m4/ax_check_compile_flag.m4 \ + $(top_srcdir)/m4/ax_gcc_func_attribute.m4 \ $(top_srcdir)/m4/ax_pthread.m4 $(top_srcdir)/m4/jemalloc.m4 \ $(top_srcdir)/m4/tcmalloc.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(dist_web_DATA) \ + $(dist_webcss_DATA) $(dist_webdnt_DATA) $(dist_webfonts_DATA) \ + $(dist_webimages_DATA) $(dist_weblib_DATA) $(dist_webold_DATA) \ + $(dist_webwellknown_DATA) $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = @@ -153,6 +163,7 @@ DATA = $(dist_web_DATA) $(dist_webcss_DATA) $(dist_webdnt_DATA) \ $(dist_weblib_DATA) $(dist_webold_DATA) \ $(dist_webwellknown_DATA) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +am__DIST_COMMON = $(srcdir)/Makefile.in DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ @@ -299,6 +310,7 @@ dist_web_DATA = \ demo.html \ demo2.html \ demosites.html \ + demosites2.html \ dashboard.html \ dashboard.js \ dashboard_info.js \ @@ -311,6 +323,7 @@ dist_web_DATA = \ netdata-swagger.json \ robots.txt \ registry.html \ + sitemap.xml \ tv.html \ version.txt \ $(NULL) @@ -337,7 +350,7 @@ dist_weblib_DATA = \ lib/gauge-d5260c3.min.js \ lib/jquery-2.2.4.min.js \ lib/jquery.easypiechart-97b5824.min.js \ - lib/jquery.nanoscroller-0.8.7.min.js \ + lib/perfect-scrollbar-0.6.15.min.js \ lib/jquery.peity-3.2.0.min.js \ lib/jquery.sparkline-2.1.2.min.js \ lib/morris-0.5.1.min.js \ @@ -348,9 +361,9 @@ dist_weblib_DATA = \ webcssdir = $(webdir)/css dist_webcss_DATA = \ css/morris-0.5.1.css \ - css/bootstrap-3.3.7.min.css \ + css/bootstrap-3.3.7.css \ css/bootstrap-theme-3.3.7.min.css \ - css/bootstrap.slate.min.css \ + css/bootstrap-slate-flat-3.3.7.css \ css/bootstrap-toggle-2.2.2.min.css \ css/font-awesome.min.css \ css/c3-0.4.11.min.css \ @@ -419,7 +432,6 @@ $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__confi echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu web/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu web/Makefile -.PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ @@ -773,6 +785,8 @@ uninstall-am: uninstall-dist_webDATA uninstall-dist_webcssDATA \ uninstall-dist_weblibDATA uninstall-dist_weboldDATA \ uninstall-dist_webwellknownDATA +.PRECIOUS: Makefile + version.txt: if test -d "$(top_srcdir)/.git"; then \ diff --git a/web/css/bootstrap-3.3.7.css b/web/css/bootstrap-3.3.7.css new file mode 100644 index 000000000..6167622ce --- /dev/null +++ b/web/css/bootstrap-3.3.7.css @@ -0,0 +1,6757 @@ +/*! + * Bootstrap v3.3.7 (http://getbootstrap.com) + * Copyright 2011-2016 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */ +/*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */ +html { + font-family: sans-serif; + -webkit-text-size-adjust: 100%; + -ms-text-size-adjust: 100%; +} +body { + margin: 0; +} +article, +aside, +details, +figcaption, +figure, +footer, +header, +hgroup, +main, +menu, +nav, +section, +summary { + display: block; +} +audio, +canvas, +progress, +video { + display: inline-block; + vertical-align: baseline; +} +audio:not([controls]) { + display: none; + height: 0; +} +[hidden], +template { + display: none; +} +a { + background-color: transparent; +} +a:active, +a:hover { + outline: 0; +} +abbr[title] { + border-bottom: 1px dotted; +} +b, +strong { + font-weight: bold; +} +dfn { + font-style: italic; +} +h1 { + margin: .67em 0; + font-size: 2em; +} +mark { + color: #000; + background: #ff0; +} +small { + font-size: 80%; +} +sub, +sup { + position: relative; + font-size: 75%; + line-height: 0; + vertical-align: baseline; +} +sup { + top: -.5em; +} +sub { + bottom: -.25em; +} +img { + border: 0; +} +svg:not(:root) { + overflow: hidden; +} +figure { + margin: 1em 40px; +} +hr { + height: 0; + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box; +} +pre { + overflow: auto; +} +code, +kbd, +pre, +samp { + font-family: monospace, monospace; + font-size: 1em; +} +button, +input, +optgroup, +select, +textarea { + margin: 0; + font: inherit; + color: inherit; +} +button { + overflow: visible; +} +button, +select { + text-transform: none; +} +button, +html input[type="button"], +input[type="reset"], +input[type="submit"] { + -webkit-appearance: button; + cursor: pointer; +} +button[disabled], +html input[disabled] { + cursor: default; +} +button::-moz-focus-inner, +input::-moz-focus-inner { + padding: 0; + border: 0; +} +input { + line-height: normal; +} +input[type="checkbox"], +input[type="radio"] { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + padding: 0; +} +input[type="number"]::-webkit-inner-spin-button, +input[type="number"]::-webkit-outer-spin-button { + height: auto; +} +input[type="search"] { + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box; + -webkit-appearance: textfield; +} +input[type="search"]::-webkit-search-cancel-button, +input[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; +} +fieldset { + padding: .35em .625em .75em; + margin: 0 2px; + border: 1px solid #c0c0c0; +} +legend { + padding: 0; + border: 0; +} +textarea { + overflow: auto; +} +optgroup { + font-weight: bold; +} +table { + border-spacing: 0; + border-collapse: collapse; +} +td, +th { + padding: 0; +} +/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */ +@media print { + *, + *:before, + *:after { + color: #000 !important; + text-shadow: none !important; + background: transparent !important; + -webkit-box-shadow: none !important; + box-shadow: none !important; + } + a, + a:visited { + text-decoration: underline; + } + a[href]:after { + content: " (" attr(href) ")"; + } + abbr[title]:after { + content: " (" attr(title) ")"; + } + a[href^="#"]:after, + a[href^="javascript:"]:after { + content: ""; + } + pre, + blockquote { + border: 1px solid #999; + + page-break-inside: avoid; + } + thead { + display: table-header-group; + } + tr, + img { + page-break-inside: avoid; + } + img { + max-width: 100% !important; + } + p, + h2, + h3 { + orphans: 3; + widows: 3; + } + h2, + h3 { + page-break-after: avoid; + } + .navbar { + display: none; + } + .btn > .caret, + .dropup > .btn > .caret { + border-top-color: #000 !important; + } + .label { + border: 1px solid #000; + } + .table { + border-collapse: collapse !important; + } + .table td, + .table th { + background-color: #fff !important; + } + .table-bordered th, + .table-bordered td { + border: 1px solid #ddd !important; + } +} +@font-face { + font-family: 'Glyphicons Halflings'; + + src: url('../fonts/glyphicons-halflings-regular.eot'); + src: url('../fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'), url('../fonts/glyphicons-halflings-regular.woff2') format('woff2'), url('../fonts/glyphicons-halflings-regular.woff') format('woff'), url('../fonts/glyphicons-halflings-regular.ttf') format('truetype'), url('../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular') format('svg'); +} +.glyphicon { + position: relative; + top: 1px; + display: inline-block; + font-family: 'Glyphicons Halflings'; + font-style: normal; + font-weight: normal; + line-height: 1; + + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} +.glyphicon-asterisk:before { + content: "\002a"; +} +.glyphicon-plus:before { + content: "\002b"; +} +.glyphicon-euro:before, +.glyphicon-eur:before { + content: "\20ac"; +} +.glyphicon-minus:before { + content: "\2212"; +} +.glyphicon-cloud:before { + content: "\2601"; +} +.glyphicon-envelope:before { + content: "\2709"; +} +.glyphicon-pencil:before { + content: "\270f"; +} +.glyphicon-glass:before { + content: "\e001"; +} +.glyphicon-music:before { + content: "\e002"; +} +.glyphicon-search:before { + content: "\e003"; +} +.glyphicon-heart:before { + content: "\e005"; +} +.glyphicon-star:before { + content: "\e006"; +} +.glyphicon-star-empty:before { + content: "\e007"; +} +.glyphicon-user:before { + content: "\e008"; +} +.glyphicon-film:before { + content: "\e009"; +} +.glyphicon-th-large:before { + content: "\e010"; +} +.glyphicon-th:before { + content: "\e011"; +} +.glyphicon-th-list:before { + content: "\e012"; +} +.glyphicon-ok:before { + content: "\e013"; +} +.glyphicon-remove:before { + content: "\e014"; +} +.glyphicon-zoom-in:before { + content: "\e015"; +} +.glyphicon-zoom-out:before { + content: "\e016"; +} +.glyphicon-off:before { + content: "\e017"; +} +.glyphicon-signal:before { + content: "\e018"; +} +.glyphicon-cog:before { + content: "\e019"; +} +.glyphicon-trash:before { + content: "\e020"; +} +.glyphicon-home:before { + content: "\e021"; +} +.glyphicon-file:before { + content: "\e022"; +} +.glyphicon-time:before { + content: "\e023"; +} +.glyphicon-road:before { + content: "\e024"; +} +.glyphicon-download-alt:before { + content: "\e025"; +} +.glyphicon-download:before { + content: "\e026"; +} +.glyphicon-upload:before { + content: "\e027"; +} +.glyphicon-inbox:before { + content: "\e028"; +} +.glyphicon-play-circle:before { + content: "\e029"; +} +.glyphicon-repeat:before { + content: "\e030"; +} +.glyphicon-refresh:before { + content: "\e031"; +} +.glyphicon-list-alt:before { + content: "\e032"; +} +.glyphicon-lock:before { + content: "\e033"; +} +.glyphicon-flag:before { + content: "\e034"; +} +.glyphicon-headphones:before { + content: "\e035"; +} +.glyphicon-volume-off:before { + content: "\e036"; +} +.glyphicon-volume-down:before { + content: "\e037"; +} +.glyphicon-volume-up:before { + content: "\e038"; +} +.glyphicon-qrcode:before { + content: "\e039"; +} +.glyphicon-barcode:before { + content: "\e040"; +} +.glyphicon-tag:before { + content: "\e041"; +} +.glyphicon-tags:before { + content: "\e042"; +} +.glyphicon-book:before { + content: "\e043"; +} +.glyphicon-bookmark:before { + content: "\e044"; +} +.glyphicon-print:before { + content: "\e045"; +} +.glyphicon-camera:before { + content: "\e046"; +} +.glyphicon-font:before { + content: "\e047"; +} +.glyphicon-bold:before { + content: "\e048"; +} +.glyphicon-italic:before { + content: "\e049"; +} +.glyphicon-text-height:before { + content: "\e050"; +} +.glyphicon-text-width:before { + content: "\e051"; +} +.glyphicon-align-left:before { + content: "\e052"; +} +.glyphicon-align-center:before { + content: "\e053"; +} +.glyphicon-align-right:before { + content: "\e054"; +} +.glyphicon-align-justify:before { + content: "\e055"; +} +.glyphicon-list:before { + content: "\e056"; +} +.glyphicon-indent-left:before { + content: "\e057"; +} +.glyphicon-indent-right:before { + content: "\e058"; +} +.glyphicon-facetime-video:before { + content: "\e059"; +} +.glyphicon-picture:before { + content: "\e060"; +} +.glyphicon-map-marker:before { + content: "\e062"; +} +.glyphicon-adjust:before { + content: "\e063"; +} +.glyphicon-tint:before { + content: "\e064"; +} +.glyphicon-edit:before { + content: "\e065"; +} +.glyphicon-share:before { + content: "\e066"; +} +.glyphicon-check:before { + content: "\e067"; +} +.glyphicon-move:before { + content: "\e068"; +} +.glyphicon-step-backward:before { + content: "\e069"; +} +.glyphicon-fast-backward:before { + content: "\e070"; +} +.glyphicon-backward:before { + content: "\e071"; +} +.glyphicon-play:before { + content: "\e072"; +} +.glyphicon-pause:before { + content: "\e073"; +} +.glyphicon-stop:before { + content: "\e074"; +} +.glyphicon-forward:before { + content: "\e075"; +} +.glyphicon-fast-forward:before { + content: "\e076"; +} +.glyphicon-step-forward:before { + content: "\e077"; +} +.glyphicon-eject:before { + content: "\e078"; +} +.glyphicon-chevron-left:before { + content: "\e079"; +} +.glyphicon-chevron-right:before { + content: "\e080"; +} +.glyphicon-plus-sign:before { + content: "\e081"; +} +.glyphicon-minus-sign:before { + content: "\e082"; +} +.glyphicon-remove-sign:before { + content: "\e083"; +} +.glyphicon-ok-sign:before { + content: "\e084"; +} +.glyphicon-question-sign:before { + content: "\e085"; +} +.glyphicon-info-sign:before { + content: "\e086"; +} +.glyphicon-screenshot:before { + content: "\e087"; +} +.glyphicon-remove-circle:before { + content: "\e088"; +} +.glyphicon-ok-circle:before { + content: "\e089"; +} +.glyphicon-ban-circle:before { + content: "\e090"; +} +.glyphicon-arrow-left:before { + content: "\e091"; +} +.glyphicon-arrow-right:before { + content: "\e092"; +} +.glyphicon-arrow-up:before { + content: "\e093"; +} +.glyphicon-arrow-down:before { + content: "\e094"; +} +.glyphicon-share-alt:before { + content: "\e095"; +} +.glyphicon-resize-full:before { + content: "\e096"; +} +.glyphicon-resize-small:before { + content: "\e097"; +} +.glyphicon-exclamation-sign:before { + content: "\e101"; +} +.glyphicon-gift:before { + content: "\e102"; +} +.glyphicon-leaf:before { + content: "\e103"; +} +.glyphicon-fire:before { + content: "\e104"; +} +.glyphicon-eye-open:before { + content: "\e105"; +} +.glyphicon-eye-close:before { + content: "\e106"; +} +.glyphicon-warning-sign:before { + content: "\e107"; +} +.glyphicon-plane:before { + content: "\e108"; +} +.glyphicon-calendar:before { + content: "\e109"; +} +.glyphicon-random:before { + content: "\e110"; +} +.glyphicon-comment:before { + content: "\e111"; +} +.glyphicon-magnet:before { + content: "\e112"; +} +.glyphicon-chevron-up:before { + content: "\e113"; +} +.glyphicon-chevron-down:before { + content: "\e114"; +} +.glyphicon-retweet:before { + content: "\e115"; +} +.glyphicon-shopping-cart:before { + content: "\e116"; +} +.glyphicon-folder-close:before { + content: "\e117"; +} +.glyphicon-folder-open:before { + content: "\e118"; +} +.glyphicon-resize-vertical:before { + content: "\e119"; +} +.glyphicon-resize-horizontal:before { + content: "\e120"; +} +.glyphicon-hdd:before { + content: "\e121"; +} +.glyphicon-bullhorn:before { + content: "\e122"; +} +.glyphicon-bell:before { + content: "\e123"; +} +.glyphicon-certificate:before { + content: "\e124"; +} +.glyphicon-thumbs-up:before { + content: "\e125"; +} +.glyphicon-thumbs-down:before { + content: "\e126"; +} +.glyphicon-hand-right:before { + content: "\e127"; +} +.glyphicon-hand-left:before { + content: "\e128"; +} +.glyphicon-hand-up:before { + content: "\e129"; +} +.glyphicon-hand-down:before { + content: "\e130"; +} +.glyphicon-circle-arrow-right:before { + content: "\e131"; +} +.glyphicon-circle-arrow-left:before { + content: "\e132"; +} +.glyphicon-circle-arrow-up:before { + content: "\e133"; +} +.glyphicon-circle-arrow-down:before { + content: "\e134"; +} +.glyphicon-globe:before { + content: "\e135"; +} +.glyphicon-wrench:before { + content: "\e136"; +} +.glyphicon-tasks:before { + content: "\e137"; +} +.glyphicon-filter:before { + content: "\e138"; +} +.glyphicon-briefcase:before { + content: "\e139"; +} +.glyphicon-fullscreen:before { + content: "\e140"; +} +.glyphicon-dashboard:before { + content: "\e141"; +} +.glyphicon-paperclip:before { + content: "\e142"; +} +.glyphicon-heart-empty:before { + content: "\e143"; +} +.glyphicon-link:before { + content: "\e144"; +} +.glyphicon-phone:before { + content: "\e145"; +} +.glyphicon-pushpin:before { + content: "\e146"; +} +.glyphicon-usd:before { + content: "\e148"; +} +.glyphicon-gbp:before { + content: "\e149"; +} +.glyphicon-sort:before { + content: "\e150"; +} +.glyphicon-sort-by-alphabet:before { + content: "\e151"; +} +.glyphicon-sort-by-alphabet-alt:before { + content: "\e152"; +} +.glyphicon-sort-by-order:before { + content: "\e153"; +} +.glyphicon-sort-by-order-alt:before { + content: "\e154"; +} +.glyphicon-sort-by-attributes:before { + content: "\e155"; +} +.glyphicon-sort-by-attributes-alt:before { + content: "\e156"; +} +.glyphicon-unchecked:before { + content: "\e157"; +} +.glyphicon-expand:before { + content: "\e158"; +} +.glyphicon-collapse-down:before { + content: "\e159"; +} +.glyphicon-collapse-up:before { + content: "\e160"; +} +.glyphicon-log-in:before { + content: "\e161"; +} +.glyphicon-flash:before { + content: "\e162"; +} +.glyphicon-log-out:before { + content: "\e163"; +} +.glyphicon-new-window:before { + content: "\e164"; +} +.glyphicon-record:before { + content: "\e165"; +} +.glyphicon-save:before { + content: "\e166"; +} +.glyphicon-open:before { + content: "\e167"; +} +.glyphicon-saved:before { + content: "\e168"; +} +.glyphicon-import:before { + content: "\e169"; +} +.glyphicon-export:before { + content: "\e170"; +} +.glyphicon-send:before { + content: "\e171"; +} +.glyphicon-floppy-disk:before { + content: "\e172"; +} +.glyphicon-floppy-saved:before { + content: "\e173"; +} +.glyphicon-floppy-remove:before { + content: "\e174"; +} +.glyphicon-floppy-save:before { + content: "\e175"; +} +.glyphicon-floppy-open:before { + content: "\e176"; +} +.glyphicon-credit-card:before { + content: "\e177"; +} +.glyphicon-transfer:before { + content: "\e178"; +} +.glyphicon-cutlery:before { + content: "\e179"; +} +.glyphicon-header:before { + content: "\e180"; +} +.glyphicon-compressed:before { + content: "\e181"; +} +.glyphicon-earphone:before { + content: "\e182"; +} +.glyphicon-phone-alt:before { + content: "\e183"; +} +.glyphicon-tower:before { + content: "\e184"; +} +.glyphicon-stats:before { + content: "\e185"; +} +.glyphicon-sd-video:before { + content: "\e186"; +} +.glyphicon-hd-video:before { + content: "\e187"; +} +.glyphicon-subtitles:before { + content: "\e188"; +} +.glyphicon-sound-stereo:before { + content: "\e189"; +} +.glyphicon-sound-dolby:before { + content: "\e190"; +} +.glyphicon-sound-5-1:before { + content: "\e191"; +} +.glyphicon-sound-6-1:before { + content: "\e192"; +} +.glyphicon-sound-7-1:before { + content: "\e193"; +} +.glyphicon-copyright-mark:before { + content: "\e194"; +} +.glyphicon-registration-mark:before { + content: "\e195"; +} +.glyphicon-cloud-download:before { + content: "\e197"; +} +.glyphicon-cloud-upload:before { + content: "\e198"; +} +.glyphicon-tree-conifer:before { + content: "\e199"; +} +.glyphicon-tree-deciduous:before { + content: "\e200"; +} +.glyphicon-cd:before { + content: "\e201"; +} +.glyphicon-save-file:before { + content: "\e202"; +} +.glyphicon-open-file:before { + content: "\e203"; +} +.glyphicon-level-up:before { + content: "\e204"; +} +.glyphicon-copy:before { + content: "\e205"; +} +.glyphicon-paste:before { + content: "\e206"; +} +.glyphicon-alert:before { + content: "\e209"; +} +.glyphicon-equalizer:before { + content: "\e210"; +} +.glyphicon-king:before { + content: "\e211"; +} +.glyphicon-queen:before { + content: "\e212"; +} +.glyphicon-pawn:before { + content: "\e213"; +} +.glyphicon-bishop:before { + content: "\e214"; +} +.glyphicon-knight:before { + content: "\e215"; +} +.glyphicon-baby-formula:before { + content: "\e216"; +} +.glyphicon-tent:before { + content: "\26fa"; +} +.glyphicon-blackboard:before { + content: "\e218"; +} +.glyphicon-bed:before { + content: "\e219"; +} +.glyphicon-apple:before { + content: "\f8ff"; +} +.glyphicon-erase:before { + content: "\e221"; +} +.glyphicon-hourglass:before { + content: "\231b"; +} +.glyphicon-lamp:before { + content: "\e223"; +} +.glyphicon-duplicate:before { + content: "\e224"; +} +.glyphicon-piggy-bank:before { + content: "\e225"; +} +.glyphicon-scissors:before { + content: "\e226"; +} +.glyphicon-bitcoin:before { + content: "\e227"; +} +.glyphicon-btc:before { + content: "\e227"; +} +.glyphicon-xbt:before { + content: "\e227"; +} +.glyphicon-yen:before { + content: "\00a5"; +} +.glyphicon-jpy:before { + content: "\00a5"; +} +.glyphicon-ruble:before { + content: "\20bd"; +} +.glyphicon-rub:before { + content: "\20bd"; +} +.glyphicon-scale:before { + content: "\e230"; +} +.glyphicon-ice-lolly:before { + content: "\e231"; +} +.glyphicon-ice-lolly-tasted:before { + content: "\e232"; +} +.glyphicon-education:before { + content: "\e233"; +} +.glyphicon-option-horizontal:before { + content: "\e234"; +} +.glyphicon-option-vertical:before { + content: "\e235"; +} +.glyphicon-menu-hamburger:before { + content: "\e236"; +} +.glyphicon-modal-window:before { + content: "\e237"; +} +.glyphicon-oil:before { + content: "\e238"; +} +.glyphicon-grain:before { + content: "\e239"; +} +.glyphicon-sunglasses:before { + content: "\e240"; +} +.glyphicon-text-size:before { + content: "\e241"; +} +.glyphicon-text-color:before { + content: "\e242"; +} +.glyphicon-text-background:before { + content: "\e243"; +} +.glyphicon-object-align-top:before { + content: "\e244"; +} +.glyphicon-object-align-bottom:before { + content: "\e245"; +} +.glyphicon-object-align-horizontal:before { + content: "\e246"; +} +.glyphicon-object-align-left:before { + content: "\e247"; +} +.glyphicon-object-align-vertical:before { + content: "\e248"; +} +.glyphicon-object-align-right:before { + content: "\e249"; +} +.glyphicon-triangle-right:before { + content: "\e250"; +} +.glyphicon-triangle-left:before { + content: "\e251"; +} +.glyphicon-triangle-bottom:before { + content: "\e252"; +} +.glyphicon-triangle-top:before { + content: "\e253"; +} +.glyphicon-console:before { + content: "\e254"; +} +.glyphicon-superscript:before { + content: "\e255"; +} +.glyphicon-subscript:before { + content: "\e256"; +} +.glyphicon-menu-left:before { + content: "\e257"; +} +.glyphicon-menu-right:before { + content: "\e258"; +} +.glyphicon-menu-down:before { + content: "\e259"; +} +.glyphicon-menu-up:before { + content: "\e260"; +} +* { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +*:before, +*:after { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +html { + font-size: 10px; + + -webkit-tap-highlight-color: rgba(0, 0, 0, 0); +} +body { + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 14px; + line-height: 1.42857143; + color: #333; + background-color: #fff; +} +input, +button, +select, +textarea { + font-family: inherit; + font-size: inherit; + line-height: inherit; +} +a { + color: #337ab7; + text-decoration: none; +} +a:hover, +a:focus { + color: #23527c; + text-decoration: underline; +} +a:focus { + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} +figure { + margin: 0; +} +img { + vertical-align: middle; +} +.img-responsive, +.thumbnail > img, +.thumbnail a > img, +.carousel-inner > .item > img, +.carousel-inner > .item > a > img { + display: block; + max-width: 100%; + height: auto; +} +.img-rounded { + border-radius: 6px; +} +.img-thumbnail { + display: inline-block; + max-width: 100%; + height: auto; + padding: 4px; + line-height: 1.42857143; + background-color: #fff; + border: 1px solid #ddd; + border-radius: 4px; + -webkit-transition: all .2s ease-in-out; + -o-transition: all .2s ease-in-out; + transition: all .2s ease-in-out; +} +.img-circle { + border-radius: 50%; +} +hr { + margin-top: 20px; + margin-bottom: 20px; + border: 0; + border-top: 1px solid #eee; +} +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + border: 0; +} +.sr-only-focusable:active, +.sr-only-focusable:focus { + position: static; + width: auto; + height: auto; + margin: 0; + overflow: visible; + clip: auto; +} +[role="button"] { + cursor: pointer; +} +h1, +h2, +h3, +h4, +h5, +h6, +.h1, +.h2, +.h3, +.h4, +.h5, +.h6 { + font-family: inherit; + font-weight: 500; + line-height: 1.1; + color: inherit; +} +h1 small, +h2 small, +h3 small, +h4 small, +h5 small, +h6 small, +.h1 small, +.h2 small, +.h3 small, +.h4 small, +.h5 small, +.h6 small, +h1 .small, +h2 .small, +h3 .small, +h4 .small, +h5 .small, +h6 .small, +.h1 .small, +.h2 .small, +.h3 .small, +.h4 .small, +.h5 .small, +.h6 .small { + font-weight: normal; + line-height: 1; + color: #777; +} +h1, +.h1, +h2, +.h2, +h3, +.h3 { + margin-top: 20px; + margin-bottom: 10px; +} +h1 small, +.h1 small, +h2 small, +.h2 small, +h3 small, +.h3 small, +h1 .small, +.h1 .small, +h2 .small, +.h2 .small, +h3 .small, +.h3 .small { + font-size: 65%; +} +h4, +.h4, +h5, +.h5, +h6, +.h6 { + margin-top: 10px; + margin-bottom: 10px; +} +h4 small, +.h4 small, +h5 small, +.h5 small, +h6 small, +.h6 small, +h4 .small, +.h4 .small, +h5 .small, +.h5 .small, +h6 .small, +.h6 .small { + font-size: 75%; +} +h1, +.h1 { + font-size: 36px; +} +h2, +.h2 { + font-size: 30px; +} +h3, +.h3 { + font-size: 24px; +} +h4, +.h4 { + font-size: 18px; +} +h5, +.h5 { + font-size: 14px; +} +h6, +.h6 { + font-size: 12px; +} +p { + margin: 0 0 10px; +} +.lead { + margin-bottom: 20px; + font-size: 16px; + font-weight: 300; + line-height: 1.4; +} +@media (min-width: 768px) { + .lead { + font-size: 21px; + } +} +small, +.small { + font-size: 85%; +} +mark, +.mark { + padding: .2em; + background-color: #fcf8e3; +} +.text-left { + text-align: left; +} +.text-right { + text-align: right; +} +.text-center { + text-align: center; +} +.text-justify { + text-align: justify; +} +.text-nowrap { + white-space: nowrap; +} +.text-lowercase { + text-transform: lowercase; +} +.text-uppercase { + text-transform: uppercase; +} +.text-capitalize { + text-transform: capitalize; +} +.text-muted { + color: #777; +} +.text-primary { + color: #337ab7; +} +a.text-primary:hover, +a.text-primary:focus { + color: #286090; +} +.text-success { + color: #3c763d; +} +a.text-success:hover, +a.text-success:focus { + color: #2b542c; +} +.text-info { + color: #31708f; +} +a.text-info:hover, +a.text-info:focus { + color: #245269; +} +.text-warning { + color: #8a6d3b; +} +a.text-warning:hover, +a.text-warning:focus { + color: #66512c; +} +.text-danger { + color: #a94442; +} +a.text-danger:hover, +a.text-danger:focus { + color: #843534; +} +.bg-primary { + color: #fff; + background-color: #337ab7; +} +a.bg-primary:hover, +a.bg-primary:focus { + background-color: #286090; +} +.bg-success { + background-color: #dff0d8; +} +a.bg-success:hover, +a.bg-success:focus { + background-color: #c1e2b3; +} +.bg-info { + background-color: #d9edf7; +} +a.bg-info:hover, +a.bg-info:focus { + background-color: #afd9ee; +} +.bg-warning { + background-color: #fcf8e3; +} +a.bg-warning:hover, +a.bg-warning:focus { + background-color: #f7ecb5; +} +.bg-danger { + background-color: #f2dede; +} +a.bg-danger:hover, +a.bg-danger:focus { + background-color: #e4b9b9; +} +.page-header { + padding-bottom: 9px; + margin: 40px 0 20px; + border-bottom: 1px solid #eee; +} +ul, +ol { + margin-top: 0; + margin-bottom: 10px; +} +ul ul, +ol ul, +ul ol, +ol ol { + margin-bottom: 0; +} +.list-unstyled { + padding-left: 0; + list-style: none; +} +.list-inline { + padding-left: 0; + margin-left: -5px; + list-style: none; +} +.list-inline > li { + display: inline-block; + padding-right: 5px; + padding-left: 5px; +} +dl { + margin-top: 0; + margin-bottom: 20px; +} +dt, +dd { + line-height: 1.42857143; +} +dt { + font-weight: bold; +} +dd { + margin-left: 0; +} +@media (min-width: 768px) { + .dl-horizontal dt { + float: left; + width: 160px; + overflow: hidden; + clear: left; + text-align: right; + text-overflow: ellipsis; + white-space: nowrap; + } + .dl-horizontal dd { + margin-left: 180px; + } +} +abbr[title], +abbr[data-original-title] { + cursor: help; + border-bottom: 1px dotted #777; +} +.initialism { + font-size: 90%; + text-transform: uppercase; +} +blockquote { + padding: 10px 20px; + margin: 0 0 20px; + font-size: 17.5px; + border-left: 5px solid #eee; +} +blockquote p:last-child, +blockquote ul:last-child, +blockquote ol:last-child { + margin-bottom: 0; +} +blockquote footer, +blockquote small, +blockquote .small { + display: block; + font-size: 80%; + line-height: 1.42857143; + color: #777; +} +blockquote footer:before, +blockquote small:before, +blockquote .small:before { + content: '\2014 \00A0'; +} +.blockquote-reverse, +blockquote.pull-right { + padding-right: 15px; + padding-left: 0; + text-align: right; + border-right: 5px solid #eee; + border-left: 0; +} +.blockquote-reverse footer:before, +blockquote.pull-right footer:before, +.blockquote-reverse small:before, +blockquote.pull-right small:before, +.blockquote-reverse .small:before, +blockquote.pull-right .small:before { + content: ''; +} +.blockquote-reverse footer:after, +blockquote.pull-right footer:after, +.blockquote-reverse small:after, +blockquote.pull-right small:after, +.blockquote-reverse .small:after, +blockquote.pull-right .small:after { + content: '\00A0 \2014'; +} +address { + margin-bottom: 20px; + font-style: normal; + line-height: 1.42857143; +} +code, +kbd, +pre, +samp { + font-family: Menlo, Monaco, Consolas, "Courier New", monospace; +} +code { + padding: 2px 4px; + font-size: 90%; + color: #c7254e; + background-color: #f9f2f4; + border-radius: 4px; +} +kbd { + padding: 2px 4px; + font-size: 90%; + color: #fff; + background-color: #333; + border-radius: 3px; + -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .25); + box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .25); +} +kbd kbd { + padding: 0; + font-size: 100%; + font-weight: bold; + -webkit-box-shadow: none; + box-shadow: none; +} +pre { + display: block; + padding: 9.5px; + margin: 0 0 10px; + font-size: 13px; + line-height: 1.42857143; + color: #333; + word-break: break-all; + word-wrap: break-word; + background-color: #f5f5f5; + border: 1px solid #ccc; + border-radius: 4px; +} +pre code { + padding: 0; + font-size: inherit; + color: inherit; + white-space: pre-wrap; + background-color: transparent; + border-radius: 0; +} +.pre-scrollable { + max-height: 340px; + overflow-y: scroll; +} +.container { + padding-right: 15px; + padding-left: 15px; + margin-right: auto; + margin-left: auto; +} +@media (min-width: 768px) { + .container { + width: 750px; + } +} +@media (min-width: 992px) { + .container { + width: 970px; + } +} +@media (min-width: 1200px) { + .container { + width: 1170px; + } +} +.container-fluid { + padding-right: 15px; + padding-left: 15px; + margin-right: auto; + margin-left: auto; +} +.row { + margin-right: -15px; + margin-left: -15px; +} +.col-xs-1, .col-sm-1, .col-md-1, .col-lg-1, .col-xs-2, .col-sm-2, .col-md-2, .col-lg-2, .col-xs-3, .col-sm-3, .col-md-3, .col-lg-3, .col-xs-4, .col-sm-4, .col-md-4, .col-lg-4, .col-xs-5, .col-sm-5, .col-md-5, .col-lg-5, .col-xs-6, .col-sm-6, .col-md-6, .col-lg-6, .col-xs-7, .col-sm-7, .col-md-7, .col-lg-7, .col-xs-8, .col-sm-8, .col-md-8, .col-lg-8, .col-xs-9, .col-sm-9, .col-md-9, .col-lg-9, .col-xs-10, .col-sm-10, .col-md-10, .col-lg-10, .col-xs-11, .col-sm-11, .col-md-11, .col-lg-11, .col-xs-12, .col-sm-12, .col-md-12, .col-lg-12 { + position: relative; + min-height: 1px; + padding-right: 15px; + padding-left: 15px; +} +.col-xs-1, .col-xs-2, .col-xs-3, .col-xs-4, .col-xs-5, .col-xs-6, .col-xs-7, .col-xs-8, .col-xs-9, .col-xs-10, .col-xs-11, .col-xs-12 { + float: left; +} +.col-xs-12 { + width: 100%; +} +.col-xs-11 { + width: 91.66666667%; +} +.col-xs-10 { + width: 83.33333333%; +} +.col-xs-9 { + width: 75%; +} +.col-xs-8 { + width: 66.66666667%; +} +.col-xs-7 { + width: 58.33333333%; +} +.col-xs-6 { + width: 50%; +} +.col-xs-5 { + width: 41.66666667%; +} +.col-xs-4 { + width: 33.33333333%; +} +.col-xs-3 { + width: 25%; +} +.col-xs-2 { + width: 16.66666667%; +} +.col-xs-1 { + width: 8.33333333%; +} +.col-xs-pull-12 { + right: 100%; +} +.col-xs-pull-11 { + right: 91.66666667%; +} +.col-xs-pull-10 { + right: 83.33333333%; +} +.col-xs-pull-9 { + right: 75%; +} +.col-xs-pull-8 { + right: 66.66666667%; +} +.col-xs-pull-7 { + right: 58.33333333%; +} +.col-xs-pull-6 { + right: 50%; +} +.col-xs-pull-5 { + right: 41.66666667%; +} +.col-xs-pull-4 { + right: 33.33333333%; +} +.col-xs-pull-3 { + right: 25%; +} +.col-xs-pull-2 { + right: 16.66666667%; +} +.col-xs-pull-1 { + right: 8.33333333%; +} +.col-xs-pull-0 { + right: auto; +} +.col-xs-push-12 { + left: 100%; +} +.col-xs-push-11 { + left: 91.66666667%; +} +.col-xs-push-10 { + left: 83.33333333%; +} +.col-xs-push-9 { + left: 75%; +} +.col-xs-push-8 { + left: 66.66666667%; +} +.col-xs-push-7 { + left: 58.33333333%; +} +.col-xs-push-6 { + left: 50%; +} +.col-xs-push-5 { + left: 41.66666667%; +} +.col-xs-push-4 { + left: 33.33333333%; +} +.col-xs-push-3 { + left: 25%; +} +.col-xs-push-2 { + left: 16.66666667%; +} +.col-xs-push-1 { + left: 8.33333333%; +} +.col-xs-push-0 { + left: auto; +} +.col-xs-offset-12 { + margin-left: 100%; +} +.col-xs-offset-11 { + margin-left: 91.66666667%; +} +.col-xs-offset-10 { + margin-left: 83.33333333%; +} +.col-xs-offset-9 { + margin-left: 75%; +} +.col-xs-offset-8 { + margin-left: 66.66666667%; +} +.col-xs-offset-7 { + margin-left: 58.33333333%; +} +.col-xs-offset-6 { + margin-left: 50%; +} +.col-xs-offset-5 { + margin-left: 41.66666667%; +} +.col-xs-offset-4 { + margin-left: 33.33333333%; +} +.col-xs-offset-3 { + margin-left: 25%; +} +.col-xs-offset-2 { + margin-left: 16.66666667%; +} +.col-xs-offset-1 { + margin-left: 8.33333333%; +} +.col-xs-offset-0 { + margin-left: 0; +} +@media (min-width: 768px) { + .col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12 { + float: left; + } + .col-sm-12 { + width: 100%; + } + .col-sm-11 { + width: 91.66666667%; + } + .col-sm-10 { + width: 83.33333333%; + } + .col-sm-9 { + width: 75%; + } + .col-sm-8 { + width: 66.66666667%; + } + .col-sm-7 { + width: 58.33333333%; + } + .col-sm-6 { + width: 50%; + } + .col-sm-5 { + width: 41.66666667%; + } + .col-sm-4 { + width: 33.33333333%; + } + .col-sm-3 { + width: 25%; + } + .col-sm-2 { + width: 16.66666667%; + } + .col-sm-1 { + width: 8.33333333%; + } + .col-sm-pull-12 { + right: 100%; + } + .col-sm-pull-11 { + right: 91.66666667%; + } + .col-sm-pull-10 { + right: 83.33333333%; + } + .col-sm-pull-9 { + right: 75%; + } + .col-sm-pull-8 { + right: 66.66666667%; + } + .col-sm-pull-7 { + right: 58.33333333%; + } + .col-sm-pull-6 { + right: 50%; + } + .col-sm-pull-5 { + right: 41.66666667%; + } + .col-sm-pull-4 { + right: 33.33333333%; + } + .col-sm-pull-3 { + right: 25%; + } + .col-sm-pull-2 { + right: 16.66666667%; + } + .col-sm-pull-1 { + right: 8.33333333%; + } + .col-sm-pull-0 { + right: auto; + } + .col-sm-push-12 { + left: 100%; + } + .col-sm-push-11 { + left: 91.66666667%; + } + .col-sm-push-10 { + left: 83.33333333%; + } + .col-sm-push-9 { + left: 75%; + } + .col-sm-push-8 { + left: 66.66666667%; + } + .col-sm-push-7 { + left: 58.33333333%; + } + .col-sm-push-6 { + left: 50%; + } + .col-sm-push-5 { + left: 41.66666667%; + } + .col-sm-push-4 { + left: 33.33333333%; + } + .col-sm-push-3 { + left: 25%; + } + .col-sm-push-2 { + left: 16.66666667%; + } + .col-sm-push-1 { + left: 8.33333333%; + } + .col-sm-push-0 { + left: auto; + } + .col-sm-offset-12 { + margin-left: 100%; + } + .col-sm-offset-11 { + margin-left: 91.66666667%; + } + .col-sm-offset-10 { + margin-left: 83.33333333%; + } + .col-sm-offset-9 { + margin-left: 75%; + } + .col-sm-offset-8 { + margin-left: 66.66666667%; + } + .col-sm-offset-7 { + margin-left: 58.33333333%; + } + .col-sm-offset-6 { + margin-left: 50%; + } + .col-sm-offset-5 { + margin-left: 41.66666667%; + } + .col-sm-offset-4 { + margin-left: 33.33333333%; + } + .col-sm-offset-3 { + margin-left: 25%; + } + .col-sm-offset-2 { + margin-left: 16.66666667%; + } + .col-sm-offset-1 { + margin-left: 8.33333333%; + } + .col-sm-offset-0 { + margin-left: 0; + } +} +@media (min-width: 992px) { + .col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12 { + float: left; + } + .col-md-12 { + width: 100%; + } + .col-md-11 { + width: 91.66666667%; + } + .col-md-10 { + width: 83.33333333%; + } + .col-md-9 { + width: 75%; + } + .col-md-8 { + width: 66.66666667%; + } + .col-md-7 { + width: 58.33333333%; + } + .col-md-6 { + width: 50%; + } + .col-md-5 { + width: 41.66666667%; + } + .col-md-4 { + width: 33.33333333%; + } + .col-md-3 { + width: 25%; + } + .col-md-2 { + width: 16.66666667%; + } + .col-md-1 { + width: 8.33333333%; + } + .col-md-pull-12 { + right: 100%; + } + .col-md-pull-11 { + right: 91.66666667%; + } + .col-md-pull-10 { + right: 83.33333333%; + } + .col-md-pull-9 { + right: 75%; + } + .col-md-pull-8 { + right: 66.66666667%; + } + .col-md-pull-7 { + right: 58.33333333%; + } + .col-md-pull-6 { + right: 50%; + } + .col-md-pull-5 { + right: 41.66666667%; + } + .col-md-pull-4 { + right: 33.33333333%; + } + .col-md-pull-3 { + right: 25%; + } + .col-md-pull-2 { + right: 16.66666667%; + } + .col-md-pull-1 { + right: 8.33333333%; + } + .col-md-pull-0 { + right: auto; + } + .col-md-push-12 { + left: 100%; + } + .col-md-push-11 { + left: 91.66666667%; + } + .col-md-push-10 { + left: 83.33333333%; + } + .col-md-push-9 { + left: 75%; + } + .col-md-push-8 { + left: 66.66666667%; + } + .col-md-push-7 { + left: 58.33333333%; + } + .col-md-push-6 { + left: 50%; + } + .col-md-push-5 { + left: 41.66666667%; + } + .col-md-push-4 { + left: 33.33333333%; + } + .col-md-push-3 { + left: 25%; + } + .col-md-push-2 { + left: 16.66666667%; + } + .col-md-push-1 { + left: 8.33333333%; + } + .col-md-push-0 { + left: auto; + } + .col-md-offset-12 { + margin-left: 100%; + } + .col-md-offset-11 { + margin-left: 91.66666667%; + } + .col-md-offset-10 { + margin-left: 83.33333333%; + } + .col-md-offset-9 { + margin-left: 75%; + } + .col-md-offset-8 { + margin-left: 66.66666667%; + } + .col-md-offset-7 { + margin-left: 58.33333333%; + } + .col-md-offset-6 { + margin-left: 50%; + } + .col-md-offset-5 { + margin-left: 41.66666667%; + } + .col-md-offset-4 { + margin-left: 33.33333333%; + } + .col-md-offset-3 { + margin-left: 25%; + } + .col-md-offset-2 { + margin-left: 16.66666667%; + } + .col-md-offset-1 { + margin-left: 8.33333333%; + } + .col-md-offset-0 { + margin-left: 0; + } +} +@media (min-width: 1200px) { + .col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12 { + float: left; + } + .col-lg-12 { + width: 100%; + } + .col-lg-11 { + width: 91.66666667%; + } + .col-lg-10 { + width: 83.33333333%; + } + .col-lg-9 { + width: 75%; + } + .col-lg-8 { + width: 66.66666667%; + } + .col-lg-7 { + width: 58.33333333%; + } + .col-lg-6 { + width: 50%; + } + .col-lg-5 { + width: 41.66666667%; + } + .col-lg-4 { + width: 33.33333333%; + } + .col-lg-3 { + width: 25%; + } + .col-lg-2 { + width: 16.66666667%; + } + .col-lg-1 { + width: 8.33333333%; + } + .col-lg-pull-12 { + right: 100%; + } + .col-lg-pull-11 { + right: 91.66666667%; + } + .col-lg-pull-10 { + right: 83.33333333%; + } + .col-lg-pull-9 { + right: 75%; + } + .col-lg-pull-8 { + right: 66.66666667%; + } + .col-lg-pull-7 { + right: 58.33333333%; + } + .col-lg-pull-6 { + right: 50%; + } + .col-lg-pull-5 { + right: 41.66666667%; + } + .col-lg-pull-4 { + right: 33.33333333%; + } + .col-lg-pull-3 { + right: 25%; + } + .col-lg-pull-2 { + right: 16.66666667%; + } + .col-lg-pull-1 { + right: 8.33333333%; + } + .col-lg-pull-0 { + right: auto; + } + .col-lg-push-12 { + left: 100%; + } + .col-lg-push-11 { + left: 91.66666667%; + } + .col-lg-push-10 { + left: 83.33333333%; + } + .col-lg-push-9 { + left: 75%; + } + .col-lg-push-8 { + left: 66.66666667%; + } + .col-lg-push-7 { + left: 58.33333333%; + } + .col-lg-push-6 { + left: 50%; + } + .col-lg-push-5 { + left: 41.66666667%; + } + .col-lg-push-4 { + left: 33.33333333%; + } + .col-lg-push-3 { + left: 25%; + } + .col-lg-push-2 { + left: 16.66666667%; + } + .col-lg-push-1 { + left: 8.33333333%; + } + .col-lg-push-0 { + left: auto; + } + .col-lg-offset-12 { + margin-left: 100%; + } + .col-lg-offset-11 { + margin-left: 91.66666667%; + } + .col-lg-offset-10 { + margin-left: 83.33333333%; + } + .col-lg-offset-9 { + margin-left: 75%; + } + .col-lg-offset-8 { + margin-left: 66.66666667%; + } + .col-lg-offset-7 { + margin-left: 58.33333333%; + } + .col-lg-offset-6 { + margin-left: 50%; + } + .col-lg-offset-5 { + margin-left: 41.66666667%; + } + .col-lg-offset-4 { + margin-left: 33.33333333%; + } + .col-lg-offset-3 { + margin-left: 25%; + } + .col-lg-offset-2 { + margin-left: 16.66666667%; + } + .col-lg-offset-1 { + margin-left: 8.33333333%; + } + .col-lg-offset-0 { + margin-left: 0; + } +} +table { + background-color: transparent; +} +caption { + padding-top: 8px; + padding-bottom: 8px; + color: #777; + text-align: left; +} +th { + text-align: left; +} +.table { + width: 100%; + max-width: 100%; + margin-bottom: 20px; +} +.table > thead > tr > th, +.table > tbody > tr > th, +.table > tfoot > tr > th, +.table > thead > tr > td, +.table > tbody > tr > td, +.table > tfoot > tr > td { + padding: 8px; + line-height: 1.42857143; + vertical-align: top; + border-top: 1px solid #ddd; +} +.table > thead > tr > th { + vertical-align: bottom; + border-bottom: 2px solid #ddd; +} +.table > caption + thead > tr:first-child > th, +.table > colgroup + thead > tr:first-child > th, +.table > thead:first-child > tr:first-child > th, +.table > caption + thead > tr:first-child > td, +.table > colgroup + thead > tr:first-child > td, +.table > thead:first-child > tr:first-child > td { + border-top: 0; +} +.table > tbody + tbody { + border-top: 2px solid #ddd; +} +.table .table { + background-color: #fff; +} +.table-condensed > thead > tr > th, +.table-condensed > tbody > tr > th, +.table-condensed > tfoot > tr > th, +.table-condensed > thead > tr > td, +.table-condensed > tbody > tr > td, +.table-condensed > tfoot > tr > td { + padding: 5px; +} +.table-bordered { + border: 1px solid #ddd; +} +.table-bordered > thead > tr > th, +.table-bordered > tbody > tr > th, +.table-bordered > tfoot > tr > th, +.table-bordered > thead > tr > td, +.table-bordered > tbody > tr > td, +.table-bordered > tfoot > tr > td { + border: 1px solid #ddd; +} +.table-bordered > thead > tr > th, +.table-bordered > thead > tr > td { + border-bottom-width: 2px; +} +.table-striped > tbody > tr:nth-of-type(odd) { + background-color: #f9f9f9; +} +.table-hover > tbody > tr:hover { + background-color: #f5f5f5; +} +table col[class*="col-"] { + position: static; + display: table-column; + float: none; +} +table td[class*="col-"], +table th[class*="col-"] { + position: static; + display: table-cell; + float: none; +} +.table > thead > tr > td.active, +.table > tbody > tr > td.active, +.table > tfoot > tr > td.active, +.table > thead > tr > th.active, +.table > tbody > tr > th.active, +.table > tfoot > tr > th.active, +.table > thead > tr.active > td, +.table > tbody > tr.active > td, +.table > tfoot > tr.active > td, +.table > thead > tr.active > th, +.table > tbody > tr.active > th, +.table > tfoot > tr.active > th { + background-color: #f5f5f5; +} +.table-hover > tbody > tr > td.active:hover, +.table-hover > tbody > tr > th.active:hover, +.table-hover > tbody > tr.active:hover > td, +.table-hover > tbody > tr:hover > .active, +.table-hover > tbody > tr.active:hover > th { + background-color: #e8e8e8; +} +.table > thead > tr > td.success, +.table > tbody > tr > td.success, +.table > tfoot > tr > td.success, +.table > thead > tr > th.success, +.table > tbody > tr > th.success, +.table > tfoot > tr > th.success, +.table > thead > tr.success > td, +.table > tbody > tr.success > td, +.table > tfoot > tr.success > td, +.table > thead > tr.success > th, +.table > tbody > tr.success > th, +.table > tfoot > tr.success > th { + background-color: #dff0d8; +} +.table-hover > tbody > tr > td.success:hover, +.table-hover > tbody > tr > th.success:hover, +.table-hover > tbody > tr.success:hover > td, +.table-hover > tbody > tr:hover > .success, +.table-hover > tbody > tr.success:hover > th { + background-color: #d0e9c6; +} +.table > thead > tr > td.info, +.table > tbody > tr > td.info, +.table > tfoot > tr > td.info, +.table > thead > tr > th.info, +.table > tbody > tr > th.info, +.table > tfoot > tr > th.info, +.table > thead > tr.info > td, +.table > tbody > tr.info > td, +.table > tfoot > tr.info > td, +.table > thead > tr.info > th, +.table > tbody > tr.info > th, +.table > tfoot > tr.info > th { + background-color: #d9edf7; +} +.table-hover > tbody > tr > td.info:hover, +.table-hover > tbody > tr > th.info:hover, +.table-hover > tbody > tr.info:hover > td, +.table-hover > tbody > tr:hover > .info, +.table-hover > tbody > tr.info:hover > th { + background-color: #c4e3f3; +} +.table > thead > tr > td.warning, +.table > tbody > tr > td.warning, +.table > tfoot > tr > td.warning, +.table > thead > tr > th.warning, +.table > tbody > tr > th.warning, +.table > tfoot > tr > th.warning, +.table > thead > tr.warning > td, +.table > tbody > tr.warning > td, +.table > tfoot > tr.warning > td, +.table > thead > tr.warning > th, +.table > tbody > tr.warning > th, +.table > tfoot > tr.warning > th { + background-color: #fcf8e3; +} +.table-hover > tbody > tr > td.warning:hover, +.table-hover > tbody > tr > th.warning:hover, +.table-hover > tbody > tr.warning:hover > td, +.table-hover > tbody > tr:hover > .warning, +.table-hover > tbody > tr.warning:hover > th { + background-color: #faf2cc; +} +.table > thead > tr > td.danger, +.table > tbody > tr > td.danger, +.table > tfoot > tr > td.danger, +.table > thead > tr > th.danger, +.table > tbody > tr > th.danger, +.table > tfoot > tr > th.danger, +.table > thead > tr.danger > td, +.table > tbody > tr.danger > td, +.table > tfoot > tr.danger > td, +.table > thead > tr.danger > th, +.table > tbody > tr.danger > th, +.table > tfoot > tr.danger > th { + background-color: #f2dede; +} +.table-hover > tbody > tr > td.danger:hover, +.table-hover > tbody > tr > th.danger:hover, +.table-hover > tbody > tr.danger:hover > td, +.table-hover > tbody > tr:hover > .danger, +.table-hover > tbody > tr.danger:hover > th { + background-color: #ebcccc; +} +.table-responsive { + min-height: .01%; + overflow-x: auto; +} +@media screen and (max-width: 767px) { + .table-responsive { + width: 100%; + margin-bottom: 15px; + overflow-y: hidden; + -ms-overflow-style: -ms-autohiding-scrollbar; + border: 1px solid #ddd; + } + .table-responsive > .table { + margin-bottom: 0; + } + .table-responsive > .table > thead > tr > th, + .table-responsive > .table > tbody > tr > th, + .table-responsive > .table > tfoot > tr > th, + .table-responsive > .table > thead > tr > td, + .table-responsive > .table > tbody > tr > td, + .table-responsive > .table > tfoot > tr > td { + white-space: nowrap; + } + .table-responsive > .table-bordered { + border: 0; + } + .table-responsive > .table-bordered > thead > tr > th:first-child, + .table-responsive > .table-bordered > tbody > tr > th:first-child, + .table-responsive > .table-bordered > tfoot > tr > th:first-child, + .table-responsive > .table-bordered > thead > tr > td:first-child, + .table-responsive > .table-bordered > tbody > tr > td:first-child, + .table-responsive > .table-bordered > tfoot > tr > td:first-child { + border-left: 0; + } + .table-responsive > .table-bordered > thead > tr > th:last-child, + .table-responsive > .table-bordered > tbody > tr > th:last-child, + .table-responsive > .table-bordered > tfoot > tr > th:last-child, + .table-responsive > .table-bordered > thead > tr > td:last-child, + .table-responsive > .table-bordered > tbody > tr > td:last-child, + .table-responsive > .table-bordered > tfoot > tr > td:last-child { + border-right: 0; + } + .table-responsive > .table-bordered > tbody > tr:last-child > th, + .table-responsive > .table-bordered > tfoot > tr:last-child > th, + .table-responsive > .table-bordered > tbody > tr:last-child > td, + .table-responsive > .table-bordered > tfoot > tr:last-child > td { + border-bottom: 0; + } +} +fieldset { + min-width: 0; + padding: 0; + margin: 0; + border: 0; +} +legend { + display: block; + width: 100%; + padding: 0; + margin-bottom: 20px; + font-size: 21px; + line-height: inherit; + color: #333; + border: 0; + border-bottom: 1px solid #e5e5e5; +} +label { + display: inline-block; + max-width: 100%; + margin-bottom: 5px; + font-weight: bold; +} +input[type="search"] { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +input[type="radio"], +input[type="checkbox"] { + margin: 4px 0 0; + margin-top: 1px \9; + line-height: normal; +} +input[type="file"] { + display: block; +} +input[type="range"] { + display: block; + width: 100%; +} +select[multiple], +select[size] { + height: auto; +} +input[type="file"]:focus, +input[type="radio"]:focus, +input[type="checkbox"]:focus { + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} +output { + display: block; + padding-top: 7px; + font-size: 14px; + line-height: 1.42857143; + color: #555; +} +.form-control { + display: block; + width: 100%; + height: 34px; + padding: 6px 12px; + font-size: 14px; + line-height: 1.42857143; + color: #555; + background-color: #fff; + background-image: none; + border: 1px solid #ccc; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); + -webkit-transition: border-color ease-in-out .15s, -webkit-box-shadow ease-in-out .15s; + -o-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; + transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; +} +.form-control:focus { + border-color: #66afe9; + outline: 0; + -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, .6); + box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, .6); +} +.form-control::-moz-placeholder { + color: #999; + opacity: 1; +} +.form-control:-ms-input-placeholder { + color: #999; +} +.form-control::-webkit-input-placeholder { + color: #999; +} +.form-control::-ms-expand { + background-color: transparent; + border: 0; +} +.form-control[disabled], +.form-control[readonly], +fieldset[disabled] .form-control { + background-color: #eee; + opacity: 1; +} +.form-control[disabled], +fieldset[disabled] .form-control { + cursor: not-allowed; +} +textarea.form-control { + height: auto; +} +input[type="search"] { + -webkit-appearance: none; +} +@media screen and (-webkit-min-device-pixel-ratio: 0) { + input[type="date"].form-control, + input[type="time"].form-control, + input[type="datetime-local"].form-control, + input[type="month"].form-control { + line-height: 34px; + } + input[type="date"].input-sm, + input[type="time"].input-sm, + input[type="datetime-local"].input-sm, + input[type="month"].input-sm, + .input-group-sm input[type="date"], + .input-group-sm input[type="time"], + .input-group-sm input[type="datetime-local"], + .input-group-sm input[type="month"] { + line-height: 30px; + } + input[type="date"].input-lg, + input[type="time"].input-lg, + input[type="datetime-local"].input-lg, + input[type="month"].input-lg, + .input-group-lg input[type="date"], + .input-group-lg input[type="time"], + .input-group-lg input[type="datetime-local"], + .input-group-lg input[type="month"] { + line-height: 46px; + } +} +.form-group { + margin-bottom: 15px; +} +.radio, +.checkbox { + position: relative; + display: block; + margin-top: 10px; + margin-bottom: 10px; +} +.radio label, +.checkbox label { + min-height: 20px; + padding-left: 20px; + margin-bottom: 0; + font-weight: normal; + cursor: pointer; +} +.radio input[type="radio"], +.radio-inline input[type="radio"], +.checkbox input[type="checkbox"], +.checkbox-inline input[type="checkbox"] { + position: absolute; + margin-top: 4px \9; + margin-left: -20px; +} +.radio + .radio, +.checkbox + .checkbox { + margin-top: -5px; +} +.radio-inline, +.checkbox-inline { + position: relative; + display: inline-block; + padding-left: 20px; + margin-bottom: 0; + font-weight: normal; + vertical-align: middle; + cursor: pointer; +} +.radio-inline + .radio-inline, +.checkbox-inline + .checkbox-inline { + margin-top: 0; + margin-left: 10px; +} +input[type="radio"][disabled], +input[type="checkbox"][disabled], +input[type="radio"].disabled, +input[type="checkbox"].disabled, +fieldset[disabled] input[type="radio"], +fieldset[disabled] input[type="checkbox"] { + cursor: not-allowed; +} +.radio-inline.disabled, +.checkbox-inline.disabled, +fieldset[disabled] .radio-inline, +fieldset[disabled] .checkbox-inline { + cursor: not-allowed; +} +.radio.disabled label, +.checkbox.disabled label, +fieldset[disabled] .radio label, +fieldset[disabled] .checkbox label { + cursor: not-allowed; +} +.form-control-static { + min-height: 34px; + padding-top: 7px; + padding-bottom: 7px; + margin-bottom: 0; +} +.form-control-static.input-lg, +.form-control-static.input-sm { + padding-right: 0; + padding-left: 0; +} +.input-sm { + height: 30px; + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} +select.input-sm { + height: 30px; + line-height: 30px; +} +textarea.input-sm, +select[multiple].input-sm { + height: auto; +} +.form-group-sm .form-control { + height: 30px; + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} +.form-group-sm select.form-control { + height: 30px; + line-height: 30px; +} +.form-group-sm textarea.form-control, +.form-group-sm select[multiple].form-control { + height: auto; +} +.form-group-sm .form-control-static { + height: 30px; + min-height: 32px; + padding: 6px 10px; + font-size: 12px; + line-height: 1.5; +} +.input-lg { + height: 46px; + padding: 10px 16px; + font-size: 18px; + line-height: 1.3333333; + border-radius: 6px; +} +select.input-lg { + height: 46px; + line-height: 46px; +} +textarea.input-lg, +select[multiple].input-lg { + height: auto; +} +.form-group-lg .form-control { + height: 46px; + padding: 10px 16px; + font-size: 18px; + line-height: 1.3333333; + border-radius: 6px; +} +.form-group-lg select.form-control { + height: 46px; + line-height: 46px; +} +.form-group-lg textarea.form-control, +.form-group-lg select[multiple].form-control { + height: auto; +} +.form-group-lg .form-control-static { + height: 46px; + min-height: 38px; + padding: 11px 16px; + font-size: 18px; + line-height: 1.3333333; +} +.has-feedback { + position: relative; +} +.has-feedback .form-control { + padding-right: 42.5px; +} +.form-control-feedback { + position: absolute; + top: 0; + right: 0; + z-index: 2; + display: block; + width: 34px; + height: 34px; + line-height: 34px; + text-align: center; + pointer-events: none; +} +.input-lg + .form-control-feedback, +.input-group-lg + .form-control-feedback, +.form-group-lg .form-control + .form-control-feedback { + width: 46px; + height: 46px; + line-height: 46px; +} +.input-sm + .form-control-feedback, +.input-group-sm + .form-control-feedback, +.form-group-sm .form-control + .form-control-feedback { + width: 30px; + height: 30px; + line-height: 30px; +} +.has-success .help-block, +.has-success .control-label, +.has-success .radio, +.has-success .checkbox, +.has-success .radio-inline, +.has-success .checkbox-inline, +.has-success.radio label, +.has-success.checkbox label, +.has-success.radio-inline label, +.has-success.checkbox-inline label { + color: #3c763d; +} +.has-success .form-control { + border-color: #3c763d; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); +} +.has-success .form-control:focus { + border-color: #2b542c; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #67b168; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #67b168; +} +.has-success .input-group-addon { + color: #3c763d; + background-color: #dff0d8; + border-color: #3c763d; +} +.has-success .form-control-feedback { + color: #3c763d; +} +.has-warning .help-block, +.has-warning .control-label, +.has-warning .radio, +.has-warning .checkbox, +.has-warning .radio-inline, +.has-warning .checkbox-inline, +.has-warning.radio label, +.has-warning.checkbox label, +.has-warning.radio-inline label, +.has-warning.checkbox-inline label { + color: #8a6d3b; +} +.has-warning .form-control { + border-color: #8a6d3b; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); +} +.has-warning .form-control:focus { + border-color: #66512c; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #c0a16b; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #c0a16b; +} +.has-warning .input-group-addon { + color: #8a6d3b; + background-color: #fcf8e3; + border-color: #8a6d3b; +} +.has-warning .form-control-feedback { + color: #8a6d3b; +} +.has-error .help-block, +.has-error .control-label, +.has-error .radio, +.has-error .checkbox, +.has-error .radio-inline, +.has-error .checkbox-inline, +.has-error.radio label, +.has-error.checkbox label, +.has-error.radio-inline label, +.has-error.checkbox-inline label { + color: #a94442; +} +.has-error .form-control { + border-color: #a94442; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); +} +.has-error .form-control:focus { + border-color: #843534; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #ce8483; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #ce8483; +} +.has-error .input-group-addon { + color: #a94442; + background-color: #f2dede; + border-color: #a94442; +} +.has-error .form-control-feedback { + color: #a94442; +} +.has-feedback label ~ .form-control-feedback { + top: 25px; +} +.has-feedback label.sr-only ~ .form-control-feedback { + top: 0; +} +.help-block { + display: block; + margin-top: 5px; + margin-bottom: 10px; + color: #737373; +} +@media (min-width: 768px) { + .form-inline .form-group { + display: inline-block; + margin-bottom: 0; + vertical-align: middle; + } + .form-inline .form-control { + display: inline-block; + width: auto; + vertical-align: middle; + } + .form-inline .form-control-static { + display: inline-block; + } + .form-inline .input-group { + display: inline-table; + vertical-align: middle; + } + .form-inline .input-group .input-group-addon, + .form-inline .input-group .input-group-btn, + .form-inline .input-group .form-control { + width: auto; + } + .form-inline .input-group > .form-control { + width: 100%; + } + .form-inline .control-label { + margin-bottom: 0; + vertical-align: middle; + } + .form-inline .radio, + .form-inline .checkbox { + display: inline-block; + margin-top: 0; + margin-bottom: 0; + vertical-align: middle; + } + .form-inline .radio label, + .form-inline .checkbox label { + padding-left: 0; + } + .form-inline .radio input[type="radio"], + .form-inline .checkbox input[type="checkbox"] { + position: relative; + margin-left: 0; + } + .form-inline .has-feedback .form-control-feedback { + top: 0; + } +} +.form-horizontal .radio, +.form-horizontal .checkbox, +.form-horizontal .radio-inline, +.form-horizontal .checkbox-inline { + padding-top: 7px; + margin-top: 0; + margin-bottom: 0; +} +.form-horizontal .radio, +.form-horizontal .checkbox { + min-height: 27px; +} +.form-horizontal .form-group { + margin-right: -15px; + margin-left: -15px; +} +@media (min-width: 768px) { + .form-horizontal .control-label { + padding-top: 7px; + margin-bottom: 0; + text-align: right; + } +} +.form-horizontal .has-feedback .form-control-feedback { + right: 15px; +} +@media (min-width: 768px) { + .form-horizontal .form-group-lg .control-label { + padding-top: 11px; + font-size: 18px; + } +} +@media (min-width: 768px) { + .form-horizontal .form-group-sm .control-label { + padding-top: 6px; + font-size: 12px; + } +} +.btn { + display: inline-block; + padding: 6px 12px; + margin-bottom: 0; + font-size: 14px; + font-weight: normal; + line-height: 1.42857143; + text-align: center; + white-space: nowrap; + vertical-align: middle; + -ms-touch-action: manipulation; + touch-action: manipulation; + cursor: pointer; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + background-image: none; + border: 1px solid transparent; + border-radius: 4px; +} +.btn:focus, +.btn:active:focus, +.btn.active:focus, +.btn.focus, +.btn:active.focus, +.btn.active.focus { + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} +.btn:hover, +.btn:focus, +.btn.focus { + color: #333; + text-decoration: none; +} +.btn:active, +.btn.active { + background-image: none; + outline: 0; + -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); + box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); +} +.btn.disabled, +.btn[disabled], +fieldset[disabled] .btn { + cursor: not-allowed; + filter: alpha(opacity=65); + -webkit-box-shadow: none; + box-shadow: none; + opacity: .65; +} +a.btn.disabled, +fieldset[disabled] a.btn { + pointer-events: none; +} +.btn-default { + color: #333; + background-color: #fff; + border-color: #ccc; +} +.btn-default:focus, +.btn-default.focus { + color: #333; + background-color: #e6e6e6; + border-color: #8c8c8c; +} +.btn-default:hover { + color: #333; + background-color: #e6e6e6; + border-color: #adadad; +} +.btn-default:active, +.btn-default.active, +.open > .dropdown-toggle.btn-default { + color: #333; + background-color: #e6e6e6; + border-color: #adadad; +} +.btn-default:active:hover, +.btn-default.active:hover, +.open > .dropdown-toggle.btn-default:hover, +.btn-default:active:focus, +.btn-default.active:focus, +.open > .dropdown-toggle.btn-default:focus, +.btn-default:active.focus, +.btn-default.active.focus, +.open > .dropdown-toggle.btn-default.focus { + color: #333; + background-color: #d4d4d4; + border-color: #8c8c8c; +} +.btn-default:active, +.btn-default.active, +.open > .dropdown-toggle.btn-default { + background-image: none; +} +.btn-default.disabled:hover, +.btn-default[disabled]:hover, +fieldset[disabled] .btn-default:hover, +.btn-default.disabled:focus, +.btn-default[disabled]:focus, +fieldset[disabled] .btn-default:focus, +.btn-default.disabled.focus, +.btn-default[disabled].focus, +fieldset[disabled] .btn-default.focus { + background-color: #fff; + border-color: #ccc; +} +.btn-default .badge { + color: #fff; + background-color: #333; +} +.btn-primary { + color: #fff; + background-color: #337ab7; + border-color: #2e6da4; +} +.btn-primary:focus, +.btn-primary.focus { + color: #fff; + background-color: #286090; + border-color: #122b40; +} +.btn-primary:hover { + color: #fff; + background-color: #286090; + border-color: #204d74; +} +.btn-primary:active, +.btn-primary.active, +.open > .dropdown-toggle.btn-primary { + color: #fff; + background-color: #286090; + border-color: #204d74; +} +.btn-primary:active:hover, +.btn-primary.active:hover, +.open > .dropdown-toggle.btn-primary:hover, +.btn-primary:active:focus, +.btn-primary.active:focus, +.open > .dropdown-toggle.btn-primary:focus, +.btn-primary:active.focus, +.btn-primary.active.focus, +.open > .dropdown-toggle.btn-primary.focus { + color: #fff; + background-color: #204d74; + border-color: #122b40; +} +.btn-primary:active, +.btn-primary.active, +.open > .dropdown-toggle.btn-primary { + background-image: none; +} +.btn-primary.disabled:hover, +.btn-primary[disabled]:hover, +fieldset[disabled] .btn-primary:hover, +.btn-primary.disabled:focus, +.btn-primary[disabled]:focus, +fieldset[disabled] .btn-primary:focus, +.btn-primary.disabled.focus, +.btn-primary[disabled].focus, +fieldset[disabled] .btn-primary.focus { + background-color: #337ab7; + border-color: #2e6da4; +} +.btn-primary .badge { + color: #337ab7; + background-color: #fff; +} +.btn-success { + color: #fff; + background-color: #5cb85c; + border-color: #4cae4c; +} +.btn-success:focus, +.btn-success.focus { + color: #fff; + background-color: #449d44; + border-color: #255625; +} +.btn-success:hover { + color: #fff; + background-color: #449d44; + border-color: #398439; +} +.btn-success:active, +.btn-success.active, +.open > .dropdown-toggle.btn-success { + color: #fff; + background-color: #449d44; + border-color: #398439; +} +.btn-success:active:hover, +.btn-success.active:hover, +.open > .dropdown-toggle.btn-success:hover, +.btn-success:active:focus, +.btn-success.active:focus, +.open > .dropdown-toggle.btn-success:focus, +.btn-success:active.focus, +.btn-success.active.focus, +.open > .dropdown-toggle.btn-success.focus { + color: #fff; + background-color: #398439; + border-color: #255625; +} +.btn-success:active, +.btn-success.active, +.open > .dropdown-toggle.btn-success { + background-image: none; +} +.btn-success.disabled:hover, +.btn-success[disabled]:hover, +fieldset[disabled] .btn-success:hover, +.btn-success.disabled:focus, +.btn-success[disabled]:focus, +fieldset[disabled] .btn-success:focus, +.btn-success.disabled.focus, +.btn-success[disabled].focus, +fieldset[disabled] .btn-success.focus { + background-color: #5cb85c; + border-color: #4cae4c; +} +.btn-success .badge { + color: #5cb85c; + background-color: #fff; +} +.btn-info { + color: #fff; + background-color: #5bc0de; + border-color: #46b8da; +} +.btn-info:focus, +.btn-info.focus { + color: #fff; + background-color: #31b0d5; + border-color: #1b6d85; +} +.btn-info:hover { + color: #fff; + background-color: #31b0d5; + border-color: #269abc; +} +.btn-info:active, +.btn-info.active, +.open > .dropdown-toggle.btn-info { + color: #fff; + background-color: #31b0d5; + border-color: #269abc; +} +.btn-info:active:hover, +.btn-info.active:hover, +.open > .dropdown-toggle.btn-info:hover, +.btn-info:active:focus, +.btn-info.active:focus, +.open > .dropdown-toggle.btn-info:focus, +.btn-info:active.focus, +.btn-info.active.focus, +.open > .dropdown-toggle.btn-info.focus { + color: #fff; + background-color: #269abc; + border-color: #1b6d85; +} +.btn-info:active, +.btn-info.active, +.open > .dropdown-toggle.btn-info { + background-image: none; +} +.btn-info.disabled:hover, +.btn-info[disabled]:hover, +fieldset[disabled] .btn-info:hover, +.btn-info.disabled:focus, +.btn-info[disabled]:focus, +fieldset[disabled] .btn-info:focus, +.btn-info.disabled.focus, +.btn-info[disabled].focus, +fieldset[disabled] .btn-info.focus { + background-color: #5bc0de; + border-color: #46b8da; +} +.btn-info .badge { + color: #5bc0de; + background-color: #fff; +} +.btn-warning { + color: #fff; + background-color: #f0ad4e; + border-color: #eea236; +} +.btn-warning:focus, +.btn-warning.focus { + color: #fff; + background-color: #ec971f; + border-color: #985f0d; +} +.btn-warning:hover { + color: #fff; + background-color: #ec971f; + border-color: #d58512; +} +.btn-warning:active, +.btn-warning.active, +.open > .dropdown-toggle.btn-warning { + color: #fff; + background-color: #ec971f; + border-color: #d58512; +} +.btn-warning:active:hover, +.btn-warning.active:hover, +.open > .dropdown-toggle.btn-warning:hover, +.btn-warning:active:focus, +.btn-warning.active:focus, +.open > .dropdown-toggle.btn-warning:focus, +.btn-warning:active.focus, +.btn-warning.active.focus, +.open > .dropdown-toggle.btn-warning.focus { + color: #fff; + background-color: #d58512; + border-color: #985f0d; +} +.btn-warning:active, +.btn-warning.active, +.open > .dropdown-toggle.btn-warning { + background-image: none; +} +.btn-warning.disabled:hover, +.btn-warning[disabled]:hover, +fieldset[disabled] .btn-warning:hover, +.btn-warning.disabled:focus, +.btn-warning[disabled]:focus, +fieldset[disabled] .btn-warning:focus, +.btn-warning.disabled.focus, +.btn-warning[disabled].focus, +fieldset[disabled] .btn-warning.focus { + background-color: #f0ad4e; + border-color: #eea236; +} +.btn-warning .badge { + color: #f0ad4e; + background-color: #fff; +} +.btn-danger { + color: #fff; + background-color: #d9534f; + border-color: #d43f3a; +} +.btn-danger:focus, +.btn-danger.focus { + color: #fff; + background-color: #c9302c; + border-color: #761c19; +} +.btn-danger:hover { + color: #fff; + background-color: #c9302c; + border-color: #ac2925; +} +.btn-danger:active, +.btn-danger.active, +.open > .dropdown-toggle.btn-danger { + color: #fff; + background-color: #c9302c; + border-color: #ac2925; +} +.btn-danger:active:hover, +.btn-danger.active:hover, +.open > .dropdown-toggle.btn-danger:hover, +.btn-danger:active:focus, +.btn-danger.active:focus, +.open > .dropdown-toggle.btn-danger:focus, +.btn-danger:active.focus, +.btn-danger.active.focus, +.open > .dropdown-toggle.btn-danger.focus { + color: #fff; + background-color: #ac2925; + border-color: #761c19; +} +.btn-danger:active, +.btn-danger.active, +.open > .dropdown-toggle.btn-danger { + background-image: none; +} +.btn-danger.disabled:hover, +.btn-danger[disabled]:hover, +fieldset[disabled] .btn-danger:hover, +.btn-danger.disabled:focus, +.btn-danger[disabled]:focus, +fieldset[disabled] .btn-danger:focus, +.btn-danger.disabled.focus, +.btn-danger[disabled].focus, +fieldset[disabled] .btn-danger.focus { + background-color: #d9534f; + border-color: #d43f3a; +} +.btn-danger .badge { + color: #d9534f; + background-color: #fff; +} +.btn-link { + font-weight: normal; + color: #337ab7; + border-radius: 0; +} +.btn-link, +.btn-link:active, +.btn-link.active, +.btn-link[disabled], +fieldset[disabled] .btn-link { + background-color: transparent; + -webkit-box-shadow: none; + box-shadow: none; +} +.btn-link, +.btn-link:hover, +.btn-link:focus, +.btn-link:active { + border-color: transparent; +} +.btn-link:hover, +.btn-link:focus { + color: #23527c; + text-decoration: underline; + background-color: transparent; +} +.btn-link[disabled]:hover, +fieldset[disabled] .btn-link:hover, +.btn-link[disabled]:focus, +fieldset[disabled] .btn-link:focus { + color: #777; + text-decoration: none; +} +.btn-lg, +.btn-group-lg > .btn { + padding: 10px 16px; + font-size: 18px; + line-height: 1.3333333; + border-radius: 6px; +} +.btn-sm, +.btn-group-sm > .btn { + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} +.btn-xs, +.btn-group-xs > .btn { + padding: 1px 5px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} +.btn-block { + display: block; + width: 100%; +} +.btn-block + .btn-block { + margin-top: 5px; +} +input[type="submit"].btn-block, +input[type="reset"].btn-block, +input[type="button"].btn-block { + width: 100%; +} +.fade { + opacity: 0; + -webkit-transition: opacity .15s linear; + -o-transition: opacity .15s linear; + transition: opacity .15s linear; +} +.fade.in { + opacity: 1; +} +.collapse { + display: none; +} +.collapse.in { + display: block; +} +tr.collapse.in { + display: table-row; +} +tbody.collapse.in { + display: table-row-group; +} +.collapsing { + position: relative; + height: 0; + overflow: hidden; + -webkit-transition-timing-function: ease; + -o-transition-timing-function: ease; + transition-timing-function: ease; + -webkit-transition-duration: .35s; + -o-transition-duration: .35s; + transition-duration: .35s; + -webkit-transition-property: height, visibility; + -o-transition-property: height, visibility; + transition-property: height, visibility; +} +.caret { + display: inline-block; + width: 0; + height: 0; + margin-left: 2px; + vertical-align: middle; + border-top: 4px dashed; + border-top: 4px solid \9; + border-right: 4px solid transparent; + border-left: 4px solid transparent; +} +.dropup, +.dropdown { + position: relative; +} +.dropdown-toggle:focus { + outline: 0; +} +.dropdown-menu { + position: absolute; + top: 100%; + left: 0; + z-index: 1000; + display: none; + float: left; + min-width: 160px; + padding: 5px 0; + margin: 2px 0 0; + font-size: 14px; + text-align: left; + list-style: none; + background-color: #fff; + -webkit-background-clip: padding-box; + background-clip: padding-box; + border: 1px solid #ccc; + border: 1px solid rgba(0, 0, 0, .15); + border-radius: 4px; + -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, .175); + box-shadow: 0 6px 12px rgba(0, 0, 0, .175); +} +.dropdown-menu.pull-right { + right: 0; + left: auto; +} +.dropdown-menu .divider { + height: 1px; + margin: 9px 0; + overflow: hidden; + background-color: #e5e5e5; +} +.dropdown-menu > li > a { + display: block; + padding: 3px 20px; + clear: both; + font-weight: normal; + line-height: 1.42857143; + color: #333; + white-space: nowrap; +} +.dropdown-menu > li > a:hover, +.dropdown-menu > li > a:focus { + color: #262626; + text-decoration: none; + background-color: #f5f5f5; +} +.dropdown-menu > .active > a, +.dropdown-menu > .active > a:hover, +.dropdown-menu > .active > a:focus { + color: #fff; + text-decoration: none; + background-color: #337ab7; + outline: 0; +} +.dropdown-menu > .disabled > a, +.dropdown-menu > .disabled > a:hover, +.dropdown-menu > .disabled > a:focus { + color: #777; +} +.dropdown-menu > .disabled > a:hover, +.dropdown-menu > .disabled > a:focus { + text-decoration: none; + cursor: not-allowed; + background-color: transparent; + background-image: none; + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); +} +.open > .dropdown-menu { + display: block; +} +.open > a { + outline: 0; +} +.dropdown-menu-right { + right: 0; + left: auto; +} +.dropdown-menu-left { + right: auto; + left: 0; +} +.dropdown-header { + display: block; + padding: 3px 20px; + font-size: 12px; + line-height: 1.42857143; + color: #777; + white-space: nowrap; +} +.dropdown-backdrop { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 990; +} +.pull-right > .dropdown-menu { + right: 0; + left: auto; +} +.dropup .caret, +.navbar-fixed-bottom .dropdown .caret { + content: ""; + border-top: 0; + border-bottom: 4px dashed; + border-bottom: 4px solid \9; +} +.dropup .dropdown-menu, +.navbar-fixed-bottom .dropdown .dropdown-menu { + top: auto; + bottom: 100%; + margin-bottom: 2px; +} +@media (min-width: 768px) { + .navbar-right .dropdown-menu { + right: 0; + left: auto; + } + .navbar-right .dropdown-menu-left { + right: auto; + left: 0; + } +} +.btn-group, +.btn-group-vertical { + position: relative; + display: inline-block; + vertical-align: middle; +} +.btn-group > .btn, +.btn-group-vertical > .btn { + position: relative; + float: left; +} +.btn-group > .btn:hover, +.btn-group-vertical > .btn:hover, +.btn-group > .btn:focus, +.btn-group-vertical > .btn:focus, +.btn-group > .btn:active, +.btn-group-vertical > .btn:active, +.btn-group > .btn.active, +.btn-group-vertical > .btn.active { + z-index: 2; +} +.btn-group .btn + .btn, +.btn-group .btn + .btn-group, +.btn-group .btn-group + .btn, +.btn-group .btn-group + .btn-group { + margin-left: -1px; +} +.btn-toolbar { + margin-left: -5px; +} +.btn-toolbar .btn, +.btn-toolbar .btn-group, +.btn-toolbar .input-group { + float: left; +} +.btn-toolbar > .btn, +.btn-toolbar > .btn-group, +.btn-toolbar > .input-group { + margin-left: 5px; +} +.btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) { + border-radius: 0; +} +.btn-group > .btn:first-child { + margin-left: 0; +} +.btn-group > .btn:first-child:not(:last-child):not(.dropdown-toggle) { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} +.btn-group > .btn:last-child:not(:first-child), +.btn-group > .dropdown-toggle:not(:first-child) { + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} +.btn-group > .btn-group { + float: left; +} +.btn-group > .btn-group:not(:first-child):not(:last-child) > .btn { + border-radius: 0; +} +.btn-group > .btn-group:first-child:not(:last-child) > .btn:last-child, +.btn-group > .btn-group:first-child:not(:last-child) > .dropdown-toggle { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} +.btn-group > .btn-group:last-child:not(:first-child) > .btn:first-child { + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} +.btn-group .dropdown-toggle:active, +.btn-group.open .dropdown-toggle { + outline: 0; +} +.btn-group > .btn + .dropdown-toggle { + padding-right: 8px; + padding-left: 8px; +} +.btn-group > .btn-lg + .dropdown-toggle { + padding-right: 12px; + padding-left: 12px; +} +.btn-group.open .dropdown-toggle { + -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); + box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); +} +.btn-group.open .dropdown-toggle.btn-link { + -webkit-box-shadow: none; + box-shadow: none; +} +.btn .caret { + margin-left: 0; +} +.btn-lg .caret { + border-width: 5px 5px 0; + border-bottom-width: 0; +} +.dropup .btn-lg .caret { + border-width: 0 5px 5px; +} +.btn-group-vertical > .btn, +.btn-group-vertical > .btn-group, +.btn-group-vertical > .btn-group > .btn { + display: block; + float: none; + width: 100%; + max-width: 100%; +} +.btn-group-vertical > .btn-group > .btn { + float: none; +} +.btn-group-vertical > .btn + .btn, +.btn-group-vertical > .btn + .btn-group, +.btn-group-vertical > .btn-group + .btn, +.btn-group-vertical > .btn-group + .btn-group { + margin-top: -1px; + margin-left: 0; +} +.btn-group-vertical > .btn:not(:first-child):not(:last-child) { + border-radius: 0; +} +.btn-group-vertical > .btn:first-child:not(:last-child) { + border-top-left-radius: 4px; + border-top-right-radius: 4px; + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} +.btn-group-vertical > .btn:last-child:not(:first-child) { + border-top-left-radius: 0; + border-top-right-radius: 0; + border-bottom-right-radius: 4px; + border-bottom-left-radius: 4px; +} +.btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn { + border-radius: 0; +} +.btn-group-vertical > .btn-group:first-child:not(:last-child) > .btn:last-child, +.btn-group-vertical > .btn-group:first-child:not(:last-child) > .dropdown-toggle { + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} +.btn-group-vertical > .btn-group:last-child:not(:first-child) > .btn:first-child { + border-top-left-radius: 0; + border-top-right-radius: 0; +} +.btn-group-justified { + display: table; + width: 100%; + table-layout: fixed; + border-collapse: separate; +} +.btn-group-justified > .btn, +.btn-group-justified > .btn-group { + display: table-cell; + float: none; + width: 1%; +} +.btn-group-justified > .btn-group .btn { + width: 100%; +} +.btn-group-justified > .btn-group .dropdown-menu { + left: auto; +} +[data-toggle="buttons"] > .btn input[type="radio"], +[data-toggle="buttons"] > .btn-group > .btn input[type="radio"], +[data-toggle="buttons"] > .btn input[type="checkbox"], +[data-toggle="buttons"] > .btn-group > .btn input[type="checkbox"] { + position: absolute; + clip: rect(0, 0, 0, 0); + pointer-events: none; +} +.input-group { + position: relative; + display: table; + border-collapse: separate; +} +.input-group[class*="col-"] { + float: none; + padding-right: 0; + padding-left: 0; +} +.input-group .form-control { + position: relative; + z-index: 2; + float: left; + width: 100%; + margin-bottom: 0; +} +.input-group .form-control:focus { + z-index: 3; +} +.input-group-lg > .form-control, +.input-group-lg > .input-group-addon, +.input-group-lg > .input-group-btn > .btn { + height: 46px; + padding: 10px 16px; + font-size: 18px; + line-height: 1.3333333; + border-radius: 6px; +} +select.input-group-lg > .form-control, +select.input-group-lg > .input-group-addon, +select.input-group-lg > .input-group-btn > .btn { + height: 46px; + line-height: 46px; +} +textarea.input-group-lg > .form-control, +textarea.input-group-lg > .input-group-addon, +textarea.input-group-lg > .input-group-btn > .btn, +select[multiple].input-group-lg > .form-control, +select[multiple].input-group-lg > .input-group-addon, +select[multiple].input-group-lg > .input-group-btn > .btn { + height: auto; +} +.input-group-sm > .form-control, +.input-group-sm > .input-group-addon, +.input-group-sm > .input-group-btn > .btn { + height: 30px; + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} +select.input-group-sm > .form-control, +select.input-group-sm > .input-group-addon, +select.input-group-sm > .input-group-btn > .btn { + height: 30px; + line-height: 30px; +} +textarea.input-group-sm > .form-control, +textarea.input-group-sm > .input-group-addon, +textarea.input-group-sm > .input-group-btn > .btn, +select[multiple].input-group-sm > .form-control, +select[multiple].input-group-sm > .input-group-addon, +select[multiple].input-group-sm > .input-group-btn > .btn { + height: auto; +} +.input-group-addon, +.input-group-btn, +.input-group .form-control { + display: table-cell; +} +.input-group-addon:not(:first-child):not(:last-child), +.input-group-btn:not(:first-child):not(:last-child), +.input-group .form-control:not(:first-child):not(:last-child) { + border-radius: 0; +} +.input-group-addon, +.input-group-btn { + width: 1%; + white-space: nowrap; + vertical-align: middle; +} +.input-group-addon { + padding: 6px 12px; + font-size: 14px; + font-weight: normal; + line-height: 1; + color: #555; + text-align: center; + background-color: #eee; + border: 1px solid #ccc; + border-radius: 4px; +} +.input-group-addon.input-sm { + padding: 5px 10px; + font-size: 12px; + border-radius: 3px; +} +.input-group-addon.input-lg { + padding: 10px 16px; + font-size: 18px; + border-radius: 6px; +} +.input-group-addon input[type="radio"], +.input-group-addon input[type="checkbox"] { + margin-top: 0; +} +.input-group .form-control:first-child, +.input-group-addon:first-child, +.input-group-btn:first-child > .btn, +.input-group-btn:first-child > .btn-group > .btn, +.input-group-btn:first-child > .dropdown-toggle, +.input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle), +.input-group-btn:last-child > .btn-group:not(:last-child) > .btn { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} +.input-group-addon:first-child { + border-right: 0; +} +.input-group .form-control:last-child, +.input-group-addon:last-child, +.input-group-btn:last-child > .btn, +.input-group-btn:last-child > .btn-group > .btn, +.input-group-btn:last-child > .dropdown-toggle, +.input-group-btn:first-child > .btn:not(:first-child), +.input-group-btn:first-child > .btn-group:not(:first-child) > .btn { + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} +.input-group-addon:last-child { + border-left: 0; +} +.input-group-btn { + position: relative; + font-size: 0; + white-space: nowrap; +} +.input-group-btn > .btn { + position: relative; +} +.input-group-btn > .btn + .btn { + margin-left: -1px; +} +.input-group-btn > .btn:hover, +.input-group-btn > .btn:focus, +.input-group-btn > .btn:active { + z-index: 2; +} +.input-group-btn:first-child > .btn, +.input-group-btn:first-child > .btn-group { + margin-right: -1px; +} +.input-group-btn:last-child > .btn, +.input-group-btn:last-child > .btn-group { + z-index: 2; + margin-left: -1px; +} +.nav { + padding-left: 0; + margin-bottom: 0; + list-style: none; +} +.nav > li { + position: relative; + display: block; +} +.nav > li > a { + position: relative; + display: block; + padding: 10px 15px; +} +.nav > li > a:hover, +.nav > li > a:focus { + text-decoration: none; + background-color: #eee; +} +.nav > li.disabled > a { + color: #777; +} +.nav > li.disabled > a:hover, +.nav > li.disabled > a:focus { + color: #777; + text-decoration: none; + cursor: not-allowed; + background-color: transparent; +} +.nav .open > a, +.nav .open > a:hover, +.nav .open > a:focus { + background-color: #eee; + border-color: #337ab7; +} +.nav .nav-divider { + height: 1px; + margin: 9px 0; + overflow: hidden; + background-color: #e5e5e5; +} +.nav > li > a > img { + max-width: none; +} +.nav-tabs { + border-bottom: 1px solid #ddd; +} +.nav-tabs > li { + float: left; + margin-bottom: -1px; +} +.nav-tabs > li > a { + margin-right: 2px; + line-height: 1.42857143; + border: 1px solid transparent; + border-radius: 4px 4px 0 0; +} +.nav-tabs > li > a:hover { + border-color: #eee #eee #ddd; +} +.nav-tabs > li.active > a, +.nav-tabs > li.active > a:hover, +.nav-tabs > li.active > a:focus { + color: #555; + cursor: default; + background-color: #fff; + border: 1px solid #ddd; + border-bottom-color: transparent; +} +.nav-tabs.nav-justified { + width: 100%; + border-bottom: 0; +} +.nav-tabs.nav-justified > li { + float: none; +} +.nav-tabs.nav-justified > li > a { + margin-bottom: 5px; + text-align: center; +} +.nav-tabs.nav-justified > .dropdown .dropdown-menu { + top: auto; + left: auto; +} +@media (min-width: 768px) { + .nav-tabs.nav-justified > li { + display: table-cell; + width: 1%; + } + .nav-tabs.nav-justified > li > a { + margin-bottom: 0; + } +} +.nav-tabs.nav-justified > li > a { + margin-right: 0; + border-radius: 4px; +} +.nav-tabs.nav-justified > .active > a, +.nav-tabs.nav-justified > .active > a:hover, +.nav-tabs.nav-justified > .active > a:focus { + border: 1px solid #ddd; +} +@media (min-width: 768px) { + .nav-tabs.nav-justified > li > a { + border-bottom: 1px solid #ddd; + border-radius: 4px 4px 0 0; + } + .nav-tabs.nav-justified > .active > a, + .nav-tabs.nav-justified > .active > a:hover, + .nav-tabs.nav-justified > .active > a:focus { + border-bottom-color: #fff; + } +} +.nav-pills > li { + float: left; +} +.nav-pills > li > a { + border-radius: 4px; +} +.nav-pills > li + li { + margin-left: 2px; +} +.nav-pills > li.active > a, +.nav-pills > li.active > a:hover, +.nav-pills > li.active > a:focus { + color: #fff; + background-color: #337ab7; +} +.nav-stacked > li { + float: none; +} +.nav-stacked > li + li { + margin-top: 2px; + margin-left: 0; +} +.nav-justified { + width: 100%; +} +.nav-justified > li { + float: none; +} +.nav-justified > li > a { + margin-bottom: 5px; + text-align: center; +} +.nav-justified > .dropdown .dropdown-menu { + top: auto; + left: auto; +} +@media (min-width: 768px) { + .nav-justified > li { + display: table-cell; + width: 1%; + } + .nav-justified > li > a { + margin-bottom: 0; + } +} +.nav-tabs-justified { + border-bottom: 0; +} +.nav-tabs-justified > li > a { + margin-right: 0; + border-radius: 4px; +} +.nav-tabs-justified > .active > a, +.nav-tabs-justified > .active > a:hover, +.nav-tabs-justified > .active > a:focus { + border: 1px solid #ddd; +} +@media (min-width: 768px) { + .nav-tabs-justified > li > a { + border-bottom: 1px solid #ddd; + border-radius: 4px 4px 0 0; + } + .nav-tabs-justified > .active > a, + .nav-tabs-justified > .active > a:hover, + .nav-tabs-justified > .active > a:focus { + border-bottom-color: #fff; + } +} +.tab-content > .tab-pane { + display: none; +} +.tab-content > .active { + display: block; +} +.nav-tabs .dropdown-menu { + margin-top: -1px; + border-top-left-radius: 0; + border-top-right-radius: 0; +} +.navbar { + position: relative; + min-height: 50px; + margin-bottom: 20px; + border: 1px solid transparent; +} +@media (min-width: 768px) { + .navbar { + border-radius: 4px; + } +} +@media (min-width: 768px) { + .navbar-header { + float: left; + } +} +.navbar-collapse { + padding-right: 15px; + padding-left: 15px; + overflow-x: visible; + -webkit-overflow-scrolling: touch; + border-top: 1px solid transparent; + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1); +} +.navbar-collapse.in { + overflow-y: auto; +} +@media (min-width: 768px) { + .navbar-collapse { + width: auto; + border-top: 0; + -webkit-box-shadow: none; + box-shadow: none; + } + .navbar-collapse.collapse { + display: block !important; + height: auto !important; + padding-bottom: 0; + overflow: visible !important; + } + .navbar-collapse.in { + overflow-y: visible; + } + .navbar-fixed-top .navbar-collapse, + .navbar-static-top .navbar-collapse, + .navbar-fixed-bottom .navbar-collapse { + padding-right: 0; + padding-left: 0; + } +} +.navbar-fixed-top .navbar-collapse, +.navbar-fixed-bottom .navbar-collapse { + max-height: 340px; +} +@media (max-device-width: 480px) and (orientation: landscape) { + .navbar-fixed-top .navbar-collapse, + .navbar-fixed-bottom .navbar-collapse { + max-height: 200px; + } +} +.container > .navbar-header, +.container-fluid > .navbar-header, +.container > .navbar-collapse, +.container-fluid > .navbar-collapse { + margin-right: -15px; + margin-left: -15px; +} +@media (min-width: 768px) { + .container > .navbar-header, + .container-fluid > .navbar-header, + .container > .navbar-collapse, + .container-fluid > .navbar-collapse { + margin-right: 0; + margin-left: 0; + } +} +.navbar-static-top { + z-index: 1000; + border-width: 0 0 1px; +} +@media (min-width: 768px) { + .navbar-static-top { + border-radius: 0; + } +} +.navbar-fixed-top, +.navbar-fixed-bottom { + position: fixed; + right: 0; + left: 0; + z-index: 1030; +} +@media (min-width: 768px) { + .navbar-fixed-top, + .navbar-fixed-bottom { + border-radius: 0; + } +} +.navbar-fixed-top { + top: 0; + border-width: 0 0 1px; +} +.navbar-fixed-bottom { + bottom: 0; + margin-bottom: 0; + border-width: 1px 0 0; +} +.navbar-brand { + float: left; + height: 50px; + padding: 15px 15px; + font-size: 18px; + line-height: 20px; +} +.navbar-brand:hover, +.navbar-brand:focus { + text-decoration: none; +} +.navbar-brand > img { + display: block; +} +@media (min-width: 768px) { + .navbar > .container .navbar-brand, + .navbar > .container-fluid .navbar-brand { + margin-left: -15px; + } +} +.navbar-toggle { + position: relative; + float: right; + padding: 9px 10px; + margin-top: 8px; + margin-right: 15px; + margin-bottom: 8px; + background-color: transparent; + background-image: none; + border: 1px solid transparent; + border-radius: 4px; +} +.navbar-toggle:focus { + outline: 0; +} +.navbar-toggle .icon-bar { + display: block; + width: 22px; + height: 2px; + border-radius: 1px; +} +.navbar-toggle .icon-bar + .icon-bar { + margin-top: 4px; +} +@media (min-width: 768px) { + .navbar-toggle { + display: none; + } +} +.navbar-nav { + margin: 7.5px -15px; +} +.navbar-nav > li > a { + padding-top: 10px; + padding-bottom: 10px; + line-height: 20px; +} +@media (max-width: 767px) { + .navbar-nav .open .dropdown-menu { + position: static; + float: none; + width: auto; + margin-top: 0; + background-color: transparent; + border: 0; + -webkit-box-shadow: none; + box-shadow: none; + } + .navbar-nav .open .dropdown-menu > li > a, + .navbar-nav .open .dropdown-menu .dropdown-header { + padding: 5px 15px 5px 25px; + } + .navbar-nav .open .dropdown-menu > li > a { + line-height: 20px; + } + .navbar-nav .open .dropdown-menu > li > a:hover, + .navbar-nav .open .dropdown-menu > li > a:focus { + background-image: none; + } +} +@media (min-width: 768px) { + .navbar-nav { + float: left; + margin: 0; + } + .navbar-nav > li { + float: left; + } + .navbar-nav > li > a { + padding-top: 15px; + padding-bottom: 15px; + } +} +.navbar-form { + padding: 10px 15px; + margin-top: 8px; + margin-right: -15px; + margin-bottom: 8px; + margin-left: -15px; + border-top: 1px solid transparent; + border-bottom: 1px solid transparent; + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1), 0 1px 0 rgba(255, 255, 255, .1); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1), 0 1px 0 rgba(255, 255, 255, .1); +} +@media (min-width: 768px) { + .navbar-form .form-group { + display: inline-block; + margin-bottom: 0; + vertical-align: middle; + } + .navbar-form .form-control { + display: inline-block; + width: auto; + vertical-align: middle; + } + .navbar-form .form-control-static { + display: inline-block; + } + .navbar-form .input-group { + display: inline-table; + vertical-align: middle; + } + .navbar-form .input-group .input-group-addon, + .navbar-form .input-group .input-group-btn, + .navbar-form .input-group .form-control { + width: auto; + } + .navbar-form .input-group > .form-control { + width: 100%; + } + .navbar-form .control-label { + margin-bottom: 0; + vertical-align: middle; + } + .navbar-form .radio, + .navbar-form .checkbox { + display: inline-block; + margin-top: 0; + margin-bottom: 0; + vertical-align: middle; + } + .navbar-form .radio label, + .navbar-form .checkbox label { + padding-left: 0; + } + .navbar-form .radio input[type="radio"], + .navbar-form .checkbox input[type="checkbox"] { + position: relative; + margin-left: 0; + } + .navbar-form .has-feedback .form-control-feedback { + top: 0; + } +} +@media (max-width: 767px) { + .navbar-form .form-group { + margin-bottom: 5px; + } + .navbar-form .form-group:last-child { + margin-bottom: 0; + } +} +@media (min-width: 768px) { + .navbar-form { + width: auto; + padding-top: 0; + padding-bottom: 0; + margin-right: 0; + margin-left: 0; + border: 0; + -webkit-box-shadow: none; + box-shadow: none; + } +} +.navbar-nav > li > .dropdown-menu { + margin-top: 0; + border-top-left-radius: 0; + border-top-right-radius: 0; +} +.navbar-fixed-bottom .navbar-nav > li > .dropdown-menu { + margin-bottom: 0; + border-top-left-radius: 4px; + border-top-right-radius: 4px; + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} +.navbar-btn { + margin-top: 8px; + margin-bottom: 8px; +} +.navbar-btn.btn-sm { + margin-top: 10px; + margin-bottom: 10px; +} +.navbar-btn.btn-xs { + margin-top: 14px; + margin-bottom: 14px; +} +.navbar-text { + margin-top: 15px; + margin-bottom: 15px; +} +@media (min-width: 768px) { + .navbar-text { + float: left; + margin-right: 15px; + margin-left: 15px; + } +} +@media (min-width: 768px) { + .navbar-left { + float: left !important; + } + .navbar-right { + float: right !important; + margin-right: -15px; + } + .navbar-right ~ .navbar-right { + margin-right: 0; + } +} +.navbar-default { + background-color: #f8f8f8; + border-color: #e7e7e7; +} +.navbar-default .navbar-brand { + color: #777; +} +.navbar-default .navbar-brand:hover, +.navbar-default .navbar-brand:focus { + color: #5e5e5e; + background-color: transparent; +} +.navbar-default .navbar-text { + color: #777; +} +.navbar-default .navbar-nav > li > a { + color: #777; +} +.navbar-default .navbar-nav > li > a:hover, +.navbar-default .navbar-nav > li > a:focus { + color: #333; + background-color: transparent; +} +.navbar-default .navbar-nav > .active > a, +.navbar-default .navbar-nav > .active > a:hover, +.navbar-default .navbar-nav > .active > a:focus { + color: #555; + background-color: #e7e7e7; +} +.navbar-default .navbar-nav > .disabled > a, +.navbar-default .navbar-nav > .disabled > a:hover, +.navbar-default .navbar-nav > .disabled > a:focus { + color: #ccc; + background-color: transparent; +} +.navbar-default .navbar-toggle { + border-color: #ddd; +} +.navbar-default .navbar-toggle:hover, +.navbar-default .navbar-toggle:focus { + background-color: #ddd; +} +.navbar-default .navbar-toggle .icon-bar { + background-color: #888; +} +.navbar-default .navbar-collapse, +.navbar-default .navbar-form { + border-color: #e7e7e7; +} +.navbar-default .navbar-nav > .open > a, +.navbar-default .navbar-nav > .open > a:hover, +.navbar-default .navbar-nav > .open > a:focus { + color: #555; + background-color: #e7e7e7; +} +@media (max-width: 767px) { + .navbar-default .navbar-nav .open .dropdown-menu > li > a { + color: #777; + } + .navbar-default .navbar-nav .open .dropdown-menu > li > a:hover, + .navbar-default .navbar-nav .open .dropdown-menu > li > a:focus { + color: #333; + background-color: transparent; + } + .navbar-default .navbar-nav .open .dropdown-menu > .active > a, + .navbar-default .navbar-nav .open .dropdown-menu > .active > a:hover, + .navbar-default .navbar-nav .open .dropdown-menu > .active > a:focus { + color: #555; + background-color: #e7e7e7; + } + .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a, + .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:hover, + .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:focus { + color: #ccc; + background-color: transparent; + } +} +.navbar-default .navbar-link { + color: #777; +} +.navbar-default .navbar-link:hover { + color: #333; +} +.navbar-default .btn-link { + color: #777; +} +.navbar-default .btn-link:hover, +.navbar-default .btn-link:focus { + color: #333; +} +.navbar-default .btn-link[disabled]:hover, +fieldset[disabled] .navbar-default .btn-link:hover, +.navbar-default .btn-link[disabled]:focus, +fieldset[disabled] .navbar-default .btn-link:focus { + color: #ccc; +} +.navbar-inverse { + background-color: #222; + border-color: #080808; +} +.navbar-inverse .navbar-brand { + color: #9d9d9d; +} +.navbar-inverse .navbar-brand:hover, +.navbar-inverse .navbar-brand:focus { + color: #fff; + background-color: transparent; +} +.navbar-inverse .navbar-text { + color: #9d9d9d; +} +.navbar-inverse .navbar-nav > li > a { + color: #9d9d9d; +} +.navbar-inverse .navbar-nav > li > a:hover, +.navbar-inverse .navbar-nav > li > a:focus { + color: #fff; + background-color: transparent; +} +.navbar-inverse .navbar-nav > .active > a, +.navbar-inverse .navbar-nav > .active > a:hover, +.navbar-inverse .navbar-nav > .active > a:focus { + color: #fff; + background-color: #080808; +} +.navbar-inverse .navbar-nav > .disabled > a, +.navbar-inverse .navbar-nav > .disabled > a:hover, +.navbar-inverse .navbar-nav > .disabled > a:focus { + color: #444; + background-color: transparent; +} +.navbar-inverse .navbar-toggle { + border-color: #333; +} +.navbar-inverse .navbar-toggle:hover, +.navbar-inverse .navbar-toggle:focus { + background-color: #333; +} +.navbar-inverse .navbar-toggle .icon-bar { + background-color: #fff; +} +.navbar-inverse .navbar-collapse, +.navbar-inverse .navbar-form { + border-color: #101010; +} +.navbar-inverse .navbar-nav > .open > a, +.navbar-inverse .navbar-nav > .open > a:hover, +.navbar-inverse .navbar-nav > .open > a:focus { + color: #fff; + background-color: #080808; +} +@media (max-width: 767px) { + .navbar-inverse .navbar-nav .open .dropdown-menu > .dropdown-header { + border-color: #080808; + } + .navbar-inverse .navbar-nav .open .dropdown-menu .divider { + background-color: #080808; + } + .navbar-inverse .navbar-nav .open .dropdown-menu > li > a { + color: #9d9d9d; + } + .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:hover, + .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:focus { + color: #fff; + background-color: transparent; + } + .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a, + .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:hover, + .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:focus { + color: #fff; + background-color: #080808; + } + .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a, + .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:hover, + .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:focus { + color: #444; + background-color: transparent; + } +} +.navbar-inverse .navbar-link { + color: #9d9d9d; +} +.navbar-inverse .navbar-link:hover { + color: #fff; +} +.navbar-inverse .btn-link { + color: #9d9d9d; +} +.navbar-inverse .btn-link:hover, +.navbar-inverse .btn-link:focus { + color: #fff; +} +.navbar-inverse .btn-link[disabled]:hover, +fieldset[disabled] .navbar-inverse .btn-link:hover, +.navbar-inverse .btn-link[disabled]:focus, +fieldset[disabled] .navbar-inverse .btn-link:focus { + color: #444; +} +.breadcrumb { + padding: 8px 15px; + margin-bottom: 20px; + list-style: none; + background-color: #f5f5f5; + border-radius: 4px; +} +.breadcrumb > li { + display: inline-block; +} +.breadcrumb > li + li:before { + padding: 0 5px; + color: #ccc; + content: "/\00a0"; +} +.breadcrumb > .active { + color: #777; +} +.pagination { + display: inline-block; + padding-left: 0; + margin: 20px 0; + border-radius: 4px; +} +.pagination > li { + display: inline; +} +.pagination > li > a, +.pagination > li > span { + position: relative; + float: left; + padding: 6px 12px; + margin-left: -1px; + line-height: 1.42857143; + color: #337ab7; + text-decoration: none; + background-color: #fff; + border: 1px solid #ddd; +} +.pagination > li:first-child > a, +.pagination > li:first-child > span { + margin-left: 0; + border-top-left-radius: 4px; + border-bottom-left-radius: 4px; +} +.pagination > li:last-child > a, +.pagination > li:last-child > span { + border-top-right-radius: 4px; + border-bottom-right-radius: 4px; +} +.pagination > li > a:hover, +.pagination > li > span:hover, +.pagination > li > a:focus, +.pagination > li > span:focus { + z-index: 2; + color: #23527c; + background-color: #eee; + border-color: #ddd; +} +.pagination > .active > a, +.pagination > .active > span, +.pagination > .active > a:hover, +.pagination > .active > span:hover, +.pagination > .active > a:focus, +.pagination > .active > span:focus { + z-index: 3; + color: #fff; + cursor: default; + background-color: #337ab7; + border-color: #337ab7; +} +.pagination > .disabled > span, +.pagination > .disabled > span:hover, +.pagination > .disabled > span:focus, +.pagination > .disabled > a, +.pagination > .disabled > a:hover, +.pagination > .disabled > a:focus { + color: #777; + cursor: not-allowed; + background-color: #fff; + border-color: #ddd; +} +.pagination-lg > li > a, +.pagination-lg > li > span { + padding: 10px 16px; + font-size: 18px; + line-height: 1.3333333; +} +.pagination-lg > li:first-child > a, +.pagination-lg > li:first-child > span { + border-top-left-radius: 6px; + border-bottom-left-radius: 6px; +} +.pagination-lg > li:last-child > a, +.pagination-lg > li:last-child > span { + border-top-right-radius: 6px; + border-bottom-right-radius: 6px; +} +.pagination-sm > li > a, +.pagination-sm > li > span { + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; +} +.pagination-sm > li:first-child > a, +.pagination-sm > li:first-child > span { + border-top-left-radius: 3px; + border-bottom-left-radius: 3px; +} +.pagination-sm > li:last-child > a, +.pagination-sm > li:last-child > span { + border-top-right-radius: 3px; + border-bottom-right-radius: 3px; +} +.pager { + padding-left: 0; + margin: 20px 0; + text-align: center; + list-style: none; +} +.pager li { + display: inline; +} +.pager li > a, +.pager li > span { + display: inline-block; + padding: 5px 14px; + background-color: #fff; + border: 1px solid #ddd; + border-radius: 15px; +} +.pager li > a:hover, +.pager li > a:focus { + text-decoration: none; + background-color: #eee; +} +.pager .next > a, +.pager .next > span { + float: right; +} +.pager .previous > a, +.pager .previous > span { + float: left; +} +.pager .disabled > a, +.pager .disabled > a:hover, +.pager .disabled > a:focus, +.pager .disabled > span { + color: #777; + cursor: not-allowed; + background-color: #fff; +} +.label { + display: inline; + padding: .2em .6em .3em; + font-size: 75%; + font-weight: bold; + line-height: 1; + color: #fff; + text-align: center; + white-space: nowrap; + vertical-align: baseline; + border-radius: .25em; +} +a.label:hover, +a.label:focus { + color: #fff; + text-decoration: none; + cursor: pointer; +} +.label:empty { + display: none; +} +.btn .label { + position: relative; + top: -1px; +} +.label-default { + background-color: #777; +} +.label-default[href]:hover, +.label-default[href]:focus { + background-color: #5e5e5e; +} +.label-primary { + background-color: #337ab7; +} +.label-primary[href]:hover, +.label-primary[href]:focus { + background-color: #286090; +} +.label-success { + background-color: #5cb85c; +} +.label-success[href]:hover, +.label-success[href]:focus { + background-color: #449d44; +} +.label-info { + background-color: #5bc0de; +} +.label-info[href]:hover, +.label-info[href]:focus { + background-color: #31b0d5; +} +.label-warning { + background-color: #f0ad4e; +} +.label-warning[href]:hover, +.label-warning[href]:focus { + background-color: #ec971f; +} +.label-danger { + background-color: #d9534f; +} +.label-danger[href]:hover, +.label-danger[href]:focus { + background-color: #c9302c; +} +.badge { + display: inline-block; + min-width: 10px; + padding: 3px 7px; + font-size: 12px; + font-weight: bold; + line-height: 1; + color: #fff; + text-align: center; + white-space: nowrap; + vertical-align: middle; + background-color: #777; + border-radius: 10px; +} +.badge:empty { + display: none; +} +.btn .badge { + position: relative; + top: -1px; +} +.btn-xs .badge, +.btn-group-xs > .btn .badge { + top: 0; + padding: 1px 5px; +} +a.badge:hover, +a.badge:focus { + color: #fff; + text-decoration: none; + cursor: pointer; +} +.list-group-item.active > .badge, +.nav-pills > .active > a > .badge { + color: #337ab7; + background-color: #fff; +} +.list-group-item > .badge { + float: right; +} +.list-group-item > .badge + .badge { + margin-right: 5px; +} +.nav-pills > li > a > .badge { + margin-left: 3px; +} +.jumbotron { + padding-top: 30px; + padding-bottom: 30px; + margin-bottom: 30px; + color: inherit; + background-color: #eee; +} +.jumbotron h1, +.jumbotron .h1 { + color: inherit; +} +.jumbotron p { + margin-bottom: 15px; + font-size: 21px; + font-weight: 200; +} +.jumbotron > hr { + border-top-color: #d5d5d5; +} +.container .jumbotron, +.container-fluid .jumbotron { + padding-right: 15px; + padding-left: 15px; + border-radius: 6px; +} +.jumbotron .container { + max-width: 100%; +} +@media screen and (min-width: 768px) { + .jumbotron { + padding-top: 48px; + padding-bottom: 48px; + } + .container .jumbotron, + .container-fluid .jumbotron { + padding-right: 60px; + padding-left: 60px; + } + .jumbotron h1, + .jumbotron .h1 { + font-size: 63px; + } +} +.thumbnail { + display: block; + padding: 4px; + margin-bottom: 20px; + line-height: 1.42857143; + background-color: #fff; + border: 1px solid #ddd; + border-radius: 4px; + -webkit-transition: border .2s ease-in-out; + -o-transition: border .2s ease-in-out; + transition: border .2s ease-in-out; +} +.thumbnail > img, +.thumbnail a > img { + margin-right: auto; + margin-left: auto; +} +a.thumbnail:hover, +a.thumbnail:focus, +a.thumbnail.active { + border-color: #337ab7; +} +.thumbnail .caption { + padding: 9px; + color: #333; +} +.alert { + padding: 15px; + margin-bottom: 20px; + border: 1px solid transparent; + border-radius: 4px; +} +.alert h4 { + margin-top: 0; + color: inherit; +} +.alert .alert-link { + font-weight: bold; +} +.alert > p, +.alert > ul { + margin-bottom: 0; +} +.alert > p + p { + margin-top: 5px; +} +.alert-dismissable, +.alert-dismissible { + padding-right: 35px; +} +.alert-dismissable .close, +.alert-dismissible .close { + position: relative; + top: -2px; + right: -21px; + color: inherit; +} +.alert-success { + color: #3c763d; + background-color: #dff0d8; + border-color: #d6e9c6; +} +.alert-success hr { + border-top-color: #c9e2b3; +} +.alert-success .alert-link { + color: #2b542c; +} +.alert-info { + color: #31708f; + background-color: #d9edf7; + border-color: #bce8f1; +} +.alert-info hr { + border-top-color: #a6e1ec; +} +.alert-info .alert-link { + color: #245269; +} +.alert-warning { + color: #8a6d3b; + background-color: #fcf8e3; + border-color: #faebcc; +} +.alert-warning hr { + border-top-color: #f7e1b5; +} +.alert-warning .alert-link { + color: #66512c; +} +.alert-danger { + color: #a94442; + background-color: #f2dede; + border-color: #ebccd1; +} +.alert-danger hr { + border-top-color: #e4b9c0; +} +.alert-danger .alert-link { + color: #843534; +} +@-webkit-keyframes progress-bar-stripes { + from { + background-position: 40px 0; + } + to { + background-position: 0 0; + } +} +@-o-keyframes progress-bar-stripes { + from { + background-position: 40px 0; + } + to { + background-position: 0 0; + } +} +@keyframes progress-bar-stripes { + from { + background-position: 40px 0; + } + to { + background-position: 0 0; + } +} +.progress { + height: 20px; + margin-bottom: 20px; + overflow: hidden; + background-color: #f5f5f5; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, .1); + box-shadow: inset 0 1px 2px rgba(0, 0, 0, .1); +} +.progress-bar { + float: left; + width: 0; + height: 100%; + font-size: 12px; + line-height: 20px; + color: #fff; + text-align: center; + background-color: #337ab7; + -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .15); + box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .15); + -webkit-transition: width .6s ease; + -o-transition: width .6s ease; + transition: width .6s ease; +} +.progress-striped .progress-bar, +.progress-bar-striped { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + -webkit-background-size: 40px 40px; + background-size: 40px 40px; +} +.progress.active .progress-bar, +.progress-bar.active { + -webkit-animation: progress-bar-stripes 2s linear infinite; + -o-animation: progress-bar-stripes 2s linear infinite; + animation: progress-bar-stripes 2s linear infinite; +} +.progress-bar-success { + background-color: #5cb85c; +} +.progress-striped .progress-bar-success { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); +} +.progress-bar-info { + background-color: #5bc0de; +} +.progress-striped .progress-bar-info { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); +} +.progress-bar-warning { + background-color: #f0ad4e; +} +.progress-striped .progress-bar-warning { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); +} +.progress-bar-danger { + background-color: #d9534f; +} +.progress-striped .progress-bar-danger { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); +} +.media { + margin-top: 15px; +} +.media:first-child { + margin-top: 0; +} +.media, +.media-body { + overflow: hidden; + zoom: 1; +} +.media-body { + width: 10000px; +} +.media-object { + display: block; +} +.media-object.img-thumbnail { + max-width: none; +} +.media-right, +.media > .pull-right { + padding-left: 10px; +} +.media-left, +.media > .pull-left { + padding-right: 10px; +} +.media-left, +.media-right, +.media-body { + display: table-cell; + vertical-align: top; +} +.media-middle { + vertical-align: middle; +} +.media-bottom { + vertical-align: bottom; +} +.media-heading { + margin-top: 0; + margin-bottom: 5px; +} +.media-list { + padding-left: 0; + list-style: none; +} +.list-group { + padding-left: 0; + margin-bottom: 20px; +} +.list-group-item { + position: relative; + display: block; + padding: 10px 15px; + margin-bottom: -1px; + background-color: #fff; + border: 1px solid #ddd; +} +.list-group-item:first-child { + border-top-left-radius: 4px; + border-top-right-radius: 4px; +} +.list-group-item:last-child { + margin-bottom: 0; + border-bottom-right-radius: 4px; + border-bottom-left-radius: 4px; +} +a.list-group-item, +button.list-group-item { + color: #555; +} +a.list-group-item .list-group-item-heading, +button.list-group-item .list-group-item-heading { + color: #333; +} +a.list-group-item:hover, +button.list-group-item:hover, +a.list-group-item:focus, +button.list-group-item:focus { + color: #555; + text-decoration: none; + background-color: #f5f5f5; +} +button.list-group-item { + width: 100%; + text-align: left; +} +.list-group-item.disabled, +.list-group-item.disabled:hover, +.list-group-item.disabled:focus { + color: #777; + cursor: not-allowed; + background-color: #eee; +} +.list-group-item.disabled .list-group-item-heading, +.list-group-item.disabled:hover .list-group-item-heading, +.list-group-item.disabled:focus .list-group-item-heading { + color: inherit; +} +.list-group-item.disabled .list-group-item-text, +.list-group-item.disabled:hover .list-group-item-text, +.list-group-item.disabled:focus .list-group-item-text { + color: #777; +} +.list-group-item.active, +.list-group-item.active:hover, +.list-group-item.active:focus { + z-index: 2; + color: #fff; + background-color: #337ab7; + border-color: #337ab7; +} +.list-group-item.active .list-group-item-heading, +.list-group-item.active:hover .list-group-item-heading, +.list-group-item.active:focus .list-group-item-heading, +.list-group-item.active .list-group-item-heading > small, +.list-group-item.active:hover .list-group-item-heading > small, +.list-group-item.active:focus .list-group-item-heading > small, +.list-group-item.active .list-group-item-heading > .small, +.list-group-item.active:hover .list-group-item-heading > .small, +.list-group-item.active:focus .list-group-item-heading > .small { + color: inherit; +} +.list-group-item.active .list-group-item-text, +.list-group-item.active:hover .list-group-item-text, +.list-group-item.active:focus .list-group-item-text { + color: #c7ddef; +} +.list-group-item-success { + color: #3c763d; + background-color: #dff0d8; +} +a.list-group-item-success, +button.list-group-item-success { + color: #3c763d; +} +a.list-group-item-success .list-group-item-heading, +button.list-group-item-success .list-group-item-heading { + color: inherit; +} +a.list-group-item-success:hover, +button.list-group-item-success:hover, +a.list-group-item-success:focus, +button.list-group-item-success:focus { + color: #3c763d; + background-color: #d0e9c6; +} +a.list-group-item-success.active, +button.list-group-item-success.active, +a.list-group-item-success.active:hover, +button.list-group-item-success.active:hover, +a.list-group-item-success.active:focus, +button.list-group-item-success.active:focus { + color: #fff; + background-color: #3c763d; + border-color: #3c763d; +} +.list-group-item-info { + color: #31708f; + background-color: #d9edf7; +} +a.list-group-item-info, +button.list-group-item-info { + color: #31708f; +} +a.list-group-item-info .list-group-item-heading, +button.list-group-item-info .list-group-item-heading { + color: inherit; +} +a.list-group-item-info:hover, +button.list-group-item-info:hover, +a.list-group-item-info:focus, +button.list-group-item-info:focus { + color: #31708f; + background-color: #c4e3f3; +} +a.list-group-item-info.active, +button.list-group-item-info.active, +a.list-group-item-info.active:hover, +button.list-group-item-info.active:hover, +a.list-group-item-info.active:focus, +button.list-group-item-info.active:focus { + color: #fff; + background-color: #31708f; + border-color: #31708f; +} +.list-group-item-warning { + color: #8a6d3b; + background-color: #fcf8e3; +} +a.list-group-item-warning, +button.list-group-item-warning { + color: #8a6d3b; +} +a.list-group-item-warning .list-group-item-heading, +button.list-group-item-warning .list-group-item-heading { + color: inherit; +} +a.list-group-item-warning:hover, +button.list-group-item-warning:hover, +a.list-group-item-warning:focus, +button.list-group-item-warning:focus { + color: #8a6d3b; + background-color: #faf2cc; +} +a.list-group-item-warning.active, +button.list-group-item-warning.active, +a.list-group-item-warning.active:hover, +button.list-group-item-warning.active:hover, +a.list-group-item-warning.active:focus, +button.list-group-item-warning.active:focus { + color: #fff; + background-color: #8a6d3b; + border-color: #8a6d3b; +} +.list-group-item-danger { + color: #a94442; + background-color: #f2dede; +} +a.list-group-item-danger, +button.list-group-item-danger { + color: #a94442; +} +a.list-group-item-danger .list-group-item-heading, +button.list-group-item-danger .list-group-item-heading { + color: inherit; +} +a.list-group-item-danger:hover, +button.list-group-item-danger:hover, +a.list-group-item-danger:focus, +button.list-group-item-danger:focus { + color: #a94442; + background-color: #ebcccc; +} +a.list-group-item-danger.active, +button.list-group-item-danger.active, +a.list-group-item-danger.active:hover, +button.list-group-item-danger.active:hover, +a.list-group-item-danger.active:focus, +button.list-group-item-danger.active:focus { + color: #fff; + background-color: #a94442; + border-color: #a94442; +} +.list-group-item-heading { + margin-top: 0; + margin-bottom: 5px; +} +.list-group-item-text { + margin-bottom: 0; + line-height: 1.3; +} +.panel { + margin-bottom: 20px; + background-color: #fff; + border: 1px solid transparent; + border-radius: 4px; + -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, .05); + box-shadow: 0 1px 1px rgba(0, 0, 0, .05); +} +.panel-body { + padding: 15px; +} +.panel-heading { + padding: 10px 15px; + border-bottom: 1px solid transparent; + border-top-left-radius: 3px; + border-top-right-radius: 3px; +} +.panel-heading > .dropdown .dropdown-toggle { + color: inherit; +} +.panel-title { + margin-top: 0; + margin-bottom: 0; + font-size: 16px; + color: inherit; +} +.panel-title > a, +.panel-title > small, +.panel-title > .small, +.panel-title > small > a, +.panel-title > .small > a { + color: inherit; +} +.panel-footer { + padding: 10px 15px; + background-color: #f5f5f5; + border-top: 1px solid #ddd; + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px; +} +.panel > .list-group, +.panel > .panel-collapse > .list-group { + margin-bottom: 0; +} +.panel > .list-group .list-group-item, +.panel > .panel-collapse > .list-group .list-group-item { + border-width: 1px 0; + border-radius: 0; +} +.panel > .list-group:first-child .list-group-item:first-child, +.panel > .panel-collapse > .list-group:first-child .list-group-item:first-child { + border-top: 0; + border-top-left-radius: 3px; + border-top-right-radius: 3px; +} +.panel > .list-group:last-child .list-group-item:last-child, +.panel > .panel-collapse > .list-group:last-child .list-group-item:last-child { + border-bottom: 0; + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px; +} +.panel > .panel-heading + .panel-collapse > .list-group .list-group-item:first-child { + border-top-left-radius: 0; + border-top-right-radius: 0; +} +.panel-heading + .list-group .list-group-item:first-child { + border-top-width: 0; +} +.list-group + .panel-footer { + border-top-width: 0; +} +.panel > .table, +.panel > .table-responsive > .table, +.panel > .panel-collapse > .table { + margin-bottom: 0; +} +.panel > .table caption, +.panel > .table-responsive > .table caption, +.panel > .panel-collapse > .table caption { + padding-right: 15px; + padding-left: 15px; +} +.panel > .table:first-child, +.panel > .table-responsive:first-child > .table:first-child { + border-top-left-radius: 3px; + border-top-right-radius: 3px; +} +.panel > .table:first-child > thead:first-child > tr:first-child, +.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child, +.panel > .table:first-child > tbody:first-child > tr:first-child, +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child { + border-top-left-radius: 3px; + border-top-right-radius: 3px; +} +.panel > .table:first-child > thead:first-child > tr:first-child td:first-child, +.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:first-child, +.panel > .table:first-child > tbody:first-child > tr:first-child td:first-child, +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:first-child, +.panel > .table:first-child > thead:first-child > tr:first-child th:first-child, +.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:first-child, +.panel > .table:first-child > tbody:first-child > tr:first-child th:first-child, +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:first-child { + border-top-left-radius: 3px; +} +.panel > .table:first-child > thead:first-child > tr:first-child td:last-child, +.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:last-child, +.panel > .table:first-child > tbody:first-child > tr:first-child td:last-child, +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:last-child, +.panel > .table:first-child > thead:first-child > tr:first-child th:last-child, +.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:last-child, +.panel > .table:first-child > tbody:first-child > tr:first-child th:last-child, +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:last-child { + border-top-right-radius: 3px; +} +.panel > .table:last-child, +.panel > .table-responsive:last-child > .table:last-child { + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px; +} +.panel > .table:last-child > tbody:last-child > tr:last-child, +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child, +.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child { + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px; +} +.panel > .table:last-child > tbody:last-child > tr:last-child td:first-child, +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:first-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child td:first-child, +.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:first-child, +.panel > .table:last-child > tbody:last-child > tr:last-child th:first-child, +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:first-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child th:first-child, +.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:first-child { + border-bottom-left-radius: 3px; +} +.panel > .table:last-child > tbody:last-child > tr:last-child td:last-child, +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:last-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child td:last-child, +.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:last-child, +.panel > .table:last-child > tbody:last-child > tr:last-child th:last-child, +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:last-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child th:last-child, +.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:last-child { + border-bottom-right-radius: 3px; +} +.panel > .panel-body + .table, +.panel > .panel-body + .table-responsive, +.panel > .table + .panel-body, +.panel > .table-responsive + .panel-body { + border-top: 1px solid #ddd; +} +.panel > .table > tbody:first-child > tr:first-child th, +.panel > .table > tbody:first-child > tr:first-child td { + border-top: 0; +} +.panel > .table-bordered, +.panel > .table-responsive > .table-bordered { + border: 0; +} +.panel > .table-bordered > thead > tr > th:first-child, +.panel > .table-responsive > .table-bordered > thead > tr > th:first-child, +.panel > .table-bordered > tbody > tr > th:first-child, +.panel > .table-responsive > .table-bordered > tbody > tr > th:first-child, +.panel > .table-bordered > tfoot > tr > th:first-child, +.panel > .table-responsive > .table-bordered > tfoot > tr > th:first-child, +.panel > .table-bordered > thead > tr > td:first-child, +.panel > .table-responsive > .table-bordered > thead > tr > td:first-child, +.panel > .table-bordered > tbody > tr > td:first-child, +.panel > .table-responsive > .table-bordered > tbody > tr > td:first-child, +.panel > .table-bordered > tfoot > tr > td:first-child, +.panel > .table-responsive > .table-bordered > tfoot > tr > td:first-child { + border-left: 0; +} +.panel > .table-bordered > thead > tr > th:last-child, +.panel > .table-responsive > .table-bordered > thead > tr > th:last-child, +.panel > .table-bordered > tbody > tr > th:last-child, +.panel > .table-responsive > .table-bordered > tbody > tr > th:last-child, +.panel > .table-bordered > tfoot > tr > th:last-child, +.panel > .table-responsive > .table-bordered > tfoot > tr > th:last-child, +.panel > .table-bordered > thead > tr > td:last-child, +.panel > .table-responsive > .table-bordered > thead > tr > td:last-child, +.panel > .table-bordered > tbody > tr > td:last-child, +.panel > .table-responsive > .table-bordered > tbody > tr > td:last-child, +.panel > .table-bordered > tfoot > tr > td:last-child, +.panel > .table-responsive > .table-bordered > tfoot > tr > td:last-child { + border-right: 0; +} +.panel > .table-bordered > thead > tr:first-child > td, +.panel > .table-responsive > .table-bordered > thead > tr:first-child > td, +.panel > .table-bordered > tbody > tr:first-child > td, +.panel > .table-responsive > .table-bordered > tbody > tr:first-child > td, +.panel > .table-bordered > thead > tr:first-child > th, +.panel > .table-responsive > .table-bordered > thead > tr:first-child > th, +.panel > .table-bordered > tbody > tr:first-child > th, +.panel > .table-responsive > .table-bordered > tbody > tr:first-child > th { + border-bottom: 0; +} +.panel > .table-bordered > tbody > tr:last-child > td, +.panel > .table-responsive > .table-bordered > tbody > tr:last-child > td, +.panel > .table-bordered > tfoot > tr:last-child > td, +.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > td, +.panel > .table-bordered > tbody > tr:last-child > th, +.panel > .table-responsive > .table-bordered > tbody > tr:last-child > th, +.panel > .table-bordered > tfoot > tr:last-child > th, +.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > th { + border-bottom: 0; +} +.panel > .table-responsive { + margin-bottom: 0; + border: 0; +} +.panel-group { + margin-bottom: 20px; +} +.panel-group .panel { + margin-bottom: 0; + border-radius: 4px; +} +.panel-group .panel + .panel { + margin-top: 5px; +} +.panel-group .panel-heading { + border-bottom: 0; +} +.panel-group .panel-heading + .panel-collapse > .panel-body, +.panel-group .panel-heading + .panel-collapse > .list-group { + border-top: 1px solid #ddd; +} +.panel-group .panel-footer { + border-top: 0; +} +.panel-group .panel-footer + .panel-collapse .panel-body { + border-bottom: 1px solid #ddd; +} +.panel-default { + border-color: #ddd; +} +.panel-default > .panel-heading { + color: #333; + background-color: #f5f5f5; + border-color: #ddd; +} +.panel-default > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #ddd; +} +.panel-default > .panel-heading .badge { + color: #f5f5f5; + background-color: #333; +} +.panel-default > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #ddd; +} +.panel-primary { + border-color: #337ab7; +} +.panel-primary > .panel-heading { + color: #fff; + background-color: #337ab7; + border-color: #337ab7; +} +.panel-primary > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #337ab7; +} +.panel-primary > .panel-heading .badge { + color: #337ab7; + background-color: #fff; +} +.panel-primary > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #337ab7; +} +.panel-success { + border-color: #d6e9c6; +} +.panel-success > .panel-heading { + color: #3c763d; + background-color: #dff0d8; + border-color: #d6e9c6; +} +.panel-success > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #d6e9c6; +} +.panel-success > .panel-heading .badge { + color: #dff0d8; + background-color: #3c763d; +} +.panel-success > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #d6e9c6; +} +.panel-info { + border-color: #bce8f1; +} +.panel-info > .panel-heading { + color: #31708f; + background-color: #d9edf7; + border-color: #bce8f1; +} +.panel-info > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #bce8f1; +} +.panel-info > .panel-heading .badge { + color: #d9edf7; + background-color: #31708f; +} +.panel-info > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #bce8f1; +} +.panel-warning { + border-color: #faebcc; +} +.panel-warning > .panel-heading { + color: #8a6d3b; + background-color: #fcf8e3; + border-color: #faebcc; +} +.panel-warning > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #faebcc; +} +.panel-warning > .panel-heading .badge { + color: #fcf8e3; + background-color: #8a6d3b; +} +.panel-warning > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #faebcc; +} +.panel-danger { + border-color: #ebccd1; +} +.panel-danger > .panel-heading { + color: #a94442; + background-color: #f2dede; + border-color: #ebccd1; +} +.panel-danger > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #ebccd1; +} +.panel-danger > .panel-heading .badge { + color: #f2dede; + background-color: #a94442; +} +.panel-danger > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #ebccd1; +} +.embed-responsive { + position: relative; + display: block; + height: 0; + padding: 0; + overflow: hidden; +} +.embed-responsive .embed-responsive-item, +.embed-responsive iframe, +.embed-responsive embed, +.embed-responsive object, +.embed-responsive video { + position: absolute; + top: 0; + bottom: 0; + left: 0; + width: 100%; + height: 100%; + border: 0; +} +.embed-responsive-16by9 { + padding-bottom: 56.25%; +} +.embed-responsive-4by3 { + padding-bottom: 75%; +} +.well { + min-height: 20px; + padding: 19px; + margin-bottom: 20px; + background-color: #f5f5f5; + border: 1px solid #e3e3e3; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .05); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .05); +} +.well blockquote { + border-color: #ddd; + border-color: rgba(0, 0, 0, .15); +} +.well-lg { + padding: 24px; + border-radius: 6px; +} +.well-sm { + padding: 9px; + border-radius: 3px; +} +.close { + float: right; + font-size: 21px; + font-weight: bold; + line-height: 1; + color: #000; + text-shadow: 0 1px 0 #fff; + filter: alpha(opacity=20); + opacity: .2; +} +.close:hover, +.close:focus { + color: #000; + text-decoration: none; + cursor: pointer; + filter: alpha(opacity=50); + opacity: .5; +} +button.close { + -webkit-appearance: none; + padding: 0; + cursor: pointer; + background: transparent; + border: 0; +} +.modal-open { + overflow: hidden; +} +.modal { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 1050; + display: none; + overflow: hidden; + -webkit-overflow-scrolling: touch; + outline: 0; +} +.modal.fade .modal-dialog { + -webkit-transition: -webkit-transform .3s ease-out; + -o-transition: -o-transform .3s ease-out; + transition: transform .3s ease-out; + -webkit-transform: translate(0, -25%); + -ms-transform: translate(0, -25%); + -o-transform: translate(0, -25%); + transform: translate(0, -25%); +} +.modal.in .modal-dialog { + -webkit-transform: translate(0, 0); + -ms-transform: translate(0, 0); + -o-transform: translate(0, 0); + transform: translate(0, 0); +} +.modal-open .modal { + overflow-x: hidden; + overflow-y: auto; +} +.modal-dialog { + position: relative; + width: auto; + margin: 10px; +} +.modal-content { + position: relative; + background-color: #fff; + -webkit-background-clip: padding-box; + background-clip: padding-box; + border: 1px solid #999; + border: 1px solid rgba(0, 0, 0, .2); + border-radius: 6px; + outline: 0; + -webkit-box-shadow: 0 3px 9px rgba(0, 0, 0, .5); + box-shadow: 0 3px 9px rgba(0, 0, 0, .5); +} +.modal-backdrop { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 1040; + background-color: #000; +} +.modal-backdrop.fade { + filter: alpha(opacity=0); + opacity: 0; +} +.modal-backdrop.in { + filter: alpha(opacity=50); + opacity: .5; +} +.modal-header { + padding: 15px; + border-bottom: 1px solid #e5e5e5; +} +.modal-header .close { + margin-top: -2px; +} +.modal-title { + margin: 0; + line-height: 1.42857143; +} +.modal-body { + position: relative; + padding: 15px; +} +.modal-footer { + padding: 15px; + text-align: right; + border-top: 1px solid #e5e5e5; +} +.modal-footer .btn + .btn { + margin-bottom: 0; + margin-left: 5px; +} +.modal-footer .btn-group .btn + .btn { + margin-left: -1px; +} +.modal-footer .btn-block + .btn-block { + margin-left: 0; +} +.modal-scrollbar-measure { + position: absolute; + top: -9999px; + width: 50px; + height: 50px; + overflow: scroll; +} +@media (min-width: 768px) { + .modal-dialog { + width: 600px; + margin: 30px auto; + } + .modal-content { + -webkit-box-shadow: 0 5px 15px rgba(0, 0, 0, .5); + box-shadow: 0 5px 15px rgba(0, 0, 0, .5); + } + .modal-sm { + width: 300px; + } +} +@media (min-width: 992px) { + .modal-lg { + width: 900px; + } +} +.tooltip { + position: absolute; + z-index: 1070; + display: block; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 12px; + font-style: normal; + font-weight: normal; + line-height: 1.42857143; + text-align: left; + text-align: start; + text-decoration: none; + text-shadow: none; + text-transform: none; + letter-spacing: normal; + word-break: normal; + word-spacing: normal; + word-wrap: normal; + white-space: normal; + filter: alpha(opacity=0); + opacity: 0; + + line-break: auto; +} +.tooltip.in { + filter: alpha(opacity=90); + opacity: .9; +} +.tooltip.top { + padding: 5px 0; + margin-top: -3px; +} +.tooltip.right { + padding: 0 5px; + margin-left: 3px; +} +.tooltip.bottom { + padding: 5px 0; + margin-top: 3px; +} +.tooltip.left { + padding: 0 5px; + margin-left: -3px; +} +.tooltip-inner { + max-width: 200px; + padding: 3px 8px; + color: #fff; + text-align: center; + background-color: #000; + border-radius: 4px; +} +.tooltip-arrow { + position: absolute; + width: 0; + height: 0; + border-color: transparent; + border-style: solid; +} +.tooltip.top .tooltip-arrow { + bottom: 0; + left: 50%; + margin-left: -5px; + border-width: 5px 5px 0; + border-top-color: #000; +} +.tooltip.top-left .tooltip-arrow { + right: 5px; + bottom: 0; + margin-bottom: -5px; + border-width: 5px 5px 0; + border-top-color: #000; +} +.tooltip.top-right .tooltip-arrow { + bottom: 0; + left: 5px; + margin-bottom: -5px; + border-width: 5px 5px 0; + border-top-color: #000; +} +.tooltip.right .tooltip-arrow { + top: 50%; + left: 0; + margin-top: -5px; + border-width: 5px 5px 5px 0; + border-right-color: #000; +} +.tooltip.left .tooltip-arrow { + top: 50%; + right: 0; + margin-top: -5px; + border-width: 5px 0 5px 5px; + border-left-color: #000; +} +.tooltip.bottom .tooltip-arrow { + top: 0; + left: 50%; + margin-left: -5px; + border-width: 0 5px 5px; + border-bottom-color: #000; +} +.tooltip.bottom-left .tooltip-arrow { + top: 0; + right: 5px; + margin-top: -5px; + border-width: 0 5px 5px; + border-bottom-color: #000; +} +.tooltip.bottom-right .tooltip-arrow { + top: 0; + left: 5px; + margin-top: -5px; + border-width: 0 5px 5px; + border-bottom-color: #000; +} +.popover { + position: absolute; + top: 0; + left: 0; + z-index: 1060; + display: none; + max-width: 276px; + padding: 1px; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 14px; + font-style: normal; + font-weight: normal; + line-height: 1.42857143; + text-align: left; + text-align: start; + text-decoration: none; + text-shadow: none; + text-transform: none; + letter-spacing: normal; + word-break: normal; + word-spacing: normal; + word-wrap: normal; + white-space: normal; + background-color: #fff; + -webkit-background-clip: padding-box; + background-clip: padding-box; + border: 1px solid #ccc; + border: 1px solid rgba(0, 0, 0, .2); + border-radius: 6px; + -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, .2); + box-shadow: 0 5px 10px rgba(0, 0, 0, .2); + + line-break: auto; +} +.popover.top { + margin-top: -10px; +} +.popover.right { + margin-left: 10px; +} +.popover.bottom { + margin-top: 10px; +} +.popover.left { + margin-left: -10px; +} +.popover-title { + padding: 8px 14px; + margin: 0; + font-size: 14px; + background-color: #f7f7f7; + border-bottom: 1px solid #ebebeb; + border-radius: 5px 5px 0 0; +} +.popover-content { + padding: 9px 14px; +} +.popover > .arrow, +.popover > .arrow:after { + position: absolute; + display: block; + width: 0; + height: 0; + border-color: transparent; + border-style: solid; +} +.popover > .arrow { + border-width: 11px; +} +.popover > .arrow:after { + content: ""; + border-width: 10px; +} +.popover.top > .arrow { + bottom: -11px; + left: 50%; + margin-left: -11px; + border-top-color: #999; + border-top-color: rgba(0, 0, 0, .25); + border-bottom-width: 0; +} +.popover.top > .arrow:after { + bottom: 1px; + margin-left: -10px; + content: " "; + border-top-color: #fff; + border-bottom-width: 0; +} +.popover.right > .arrow { + top: 50%; + left: -11px; + margin-top: -11px; + border-right-color: #999; + border-right-color: rgba(0, 0, 0, .25); + border-left-width: 0; +} +.popover.right > .arrow:after { + bottom: -10px; + left: 1px; + content: " "; + border-right-color: #fff; + border-left-width: 0; +} +.popover.bottom > .arrow { + top: -11px; + left: 50%; + margin-left: -11px; + border-top-width: 0; + border-bottom-color: #999; + border-bottom-color: rgba(0, 0, 0, .25); +} +.popover.bottom > .arrow:after { + top: 1px; + margin-left: -10px; + content: " "; + border-top-width: 0; + border-bottom-color: #fff; +} +.popover.left > .arrow { + top: 50%; + right: -11px; + margin-top: -11px; + border-right-width: 0; + border-left-color: #999; + border-left-color: rgba(0, 0, 0, .25); +} +.popover.left > .arrow:after { + right: 1px; + bottom: -10px; + content: " "; + border-right-width: 0; + border-left-color: #fff; +} +.carousel { + position: relative; +} +.carousel-inner { + position: relative; + width: 100%; + overflow: hidden; +} +.carousel-inner > .item { + position: relative; + display: none; + -webkit-transition: .6s ease-in-out left; + -o-transition: .6s ease-in-out left; + transition: .6s ease-in-out left; +} +.carousel-inner > .item > img, +.carousel-inner > .item > a > img { + line-height: 1; +} +@media all and (transform-3d), (-webkit-transform-3d) { + .carousel-inner > .item { + -webkit-transition: -webkit-transform .6s ease-in-out; + -o-transition: -o-transform .6s ease-in-out; + transition: transform .6s ease-in-out; + + -webkit-backface-visibility: hidden; + backface-visibility: hidden; + -webkit-perspective: 1000px; + perspective: 1000px; + } + .carousel-inner > .item.next, + .carousel-inner > .item.active.right { + left: 0; + -webkit-transform: translate3d(100%, 0, 0); + transform: translate3d(100%, 0, 0); + } + .carousel-inner > .item.prev, + .carousel-inner > .item.active.left { + left: 0; + -webkit-transform: translate3d(-100%, 0, 0); + transform: translate3d(-100%, 0, 0); + } + .carousel-inner > .item.next.left, + .carousel-inner > .item.prev.right, + .carousel-inner > .item.active { + left: 0; + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } +} +.carousel-inner > .active, +.carousel-inner > .next, +.carousel-inner > .prev { + display: block; +} +.carousel-inner > .active { + left: 0; +} +.carousel-inner > .next, +.carousel-inner > .prev { + position: absolute; + top: 0; + width: 100%; +} +.carousel-inner > .next { + left: 100%; +} +.carousel-inner > .prev { + left: -100%; +} +.carousel-inner > .next.left, +.carousel-inner > .prev.right { + left: 0; +} +.carousel-inner > .active.left { + left: -100%; +} +.carousel-inner > .active.right { + left: 100%; +} +.carousel-control { + position: absolute; + top: 0; + bottom: 0; + left: 0; + width: 15%; + font-size: 20px; + color: #fff; + text-align: center; + text-shadow: 0 1px 2px rgba(0, 0, 0, .6); + background-color: rgba(0, 0, 0, 0); + filter: alpha(opacity=50); + opacity: .5; +} +.carousel-control.left { + background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, .5) 0%, rgba(0, 0, 0, .0001) 100%); + background-image: -o-linear-gradient(left, rgba(0, 0, 0, .5) 0%, rgba(0, 0, 0, .0001) 100%); + background-image: -webkit-gradient(linear, left top, right top, from(rgba(0, 0, 0, .5)), to(rgba(0, 0, 0, .0001))); + background-image: linear-gradient(to right, rgba(0, 0, 0, .5) 0%, rgba(0, 0, 0, .0001) 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1); + background-repeat: repeat-x; +} +.carousel-control.right { + right: 0; + left: auto; + background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, .0001) 0%, rgba(0, 0, 0, .5) 100%); + background-image: -o-linear-gradient(left, rgba(0, 0, 0, .0001) 0%, rgba(0, 0, 0, .5) 100%); + background-image: -webkit-gradient(linear, left top, right top, from(rgba(0, 0, 0, .0001)), to(rgba(0, 0, 0, .5))); + background-image: linear-gradient(to right, rgba(0, 0, 0, .0001) 0%, rgba(0, 0, 0, .5) 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1); + background-repeat: repeat-x; +} +.carousel-control:hover, +.carousel-control:focus { + color: #fff; + text-decoration: none; + filter: alpha(opacity=90); + outline: 0; + opacity: .9; +} +.carousel-control .icon-prev, +.carousel-control .icon-next, +.carousel-control .glyphicon-chevron-left, +.carousel-control .glyphicon-chevron-right { + position: absolute; + top: 50%; + z-index: 5; + display: inline-block; + margin-top: -10px; +} +.carousel-control .icon-prev, +.carousel-control .glyphicon-chevron-left { + left: 50%; + margin-left: -10px; +} +.carousel-control .icon-next, +.carousel-control .glyphicon-chevron-right { + right: 50%; + margin-right: -10px; +} +.carousel-control .icon-prev, +.carousel-control .icon-next { + width: 20px; + height: 20px; + font-family: serif; + line-height: 1; +} +.carousel-control .icon-prev:before { + content: '\2039'; +} +.carousel-control .icon-next:before { + content: '\203a'; +} +.carousel-indicators { + position: absolute; + bottom: 10px; + left: 50%; + z-index: 15; + width: 60%; + padding-left: 0; + margin-left: -30%; + text-align: center; + list-style: none; +} +.carousel-indicators li { + display: inline-block; + width: 10px; + height: 10px; + margin: 1px; + text-indent: -999px; + cursor: pointer; + background-color: #000 \9; + background-color: rgba(0, 0, 0, 0); + border: 1px solid #fff; + border-radius: 10px; +} +.carousel-indicators .active { + width: 12px; + height: 12px; + margin: 0; + background-color: #fff; +} +.carousel-caption { + position: absolute; + right: 15%; + bottom: 20px; + left: 15%; + z-index: 10; + padding-top: 20px; + padding-bottom: 20px; + color: #fff; + text-align: center; + text-shadow: 0 1px 2px rgba(0, 0, 0, .6); +} +.carousel-caption .btn { + text-shadow: none; +} +@media screen and (min-width: 768px) { + .carousel-control .glyphicon-chevron-left, + .carousel-control .glyphicon-chevron-right, + .carousel-control .icon-prev, + .carousel-control .icon-next { + width: 30px; + height: 30px; + margin-top: -10px; + font-size: 30px; + } + .carousel-control .glyphicon-chevron-left, + .carousel-control .icon-prev { + margin-left: -10px; + } + .carousel-control .glyphicon-chevron-right, + .carousel-control .icon-next { + margin-right: -10px; + } + .carousel-caption { + right: 20%; + left: 20%; + padding-bottom: 30px; + } + .carousel-indicators { + bottom: 20px; + } +} +.clearfix:before, +.clearfix:after, +.dl-horizontal dd:before, +.dl-horizontal dd:after, +.container:before, +.container:after, +.container-fluid:before, +.container-fluid:after, +.row:before, +.row:after, +.form-horizontal .form-group:before, +.form-horizontal .form-group:after, +.btn-toolbar:before, +.btn-toolbar:after, +.btn-group-vertical > .btn-group:before, +.btn-group-vertical > .btn-group:after, +.nav:before, +.nav:after, +.navbar:before, +.navbar:after, +.navbar-header:before, +.navbar-header:after, +.navbar-collapse:before, +.navbar-collapse:after, +.pager:before, +.pager:after, +.panel-body:before, +.panel-body:after, +.modal-header:before, +.modal-header:after, +.modal-footer:before, +.modal-footer:after { + display: table; + content: " "; +} +.clearfix:after, +.dl-horizontal dd:after, +.container:after, +.container-fluid:after, +.row:after, +.form-horizontal .form-group:after, +.btn-toolbar:after, +.btn-group-vertical > .btn-group:after, +.nav:after, +.navbar:after, +.navbar-header:after, +.navbar-collapse:after, +.pager:after, +.panel-body:after, +.modal-header:after, +.modal-footer:after { + clear: both; +} +.center-block { + display: block; + margin-right: auto; + margin-left: auto; +} +.pull-right { + float: right !important; +} +.pull-left { + float: left !important; +} +.hide { + display: none !important; +} +.show { + display: block !important; +} +.invisible { + visibility: hidden; +} +.text-hide { + font: 0/0 a; + color: transparent; + text-shadow: none; + background-color: transparent; + border: 0; +} +.hidden { + display: none !important; +} +.affix { + position: fixed; +} +@-ms-viewport { + width: device-width; +} +.visible-xs, +.visible-sm, +.visible-md, +.visible-lg { + display: none !important; +} +.visible-xs-block, +.visible-xs-inline, +.visible-xs-inline-block, +.visible-sm-block, +.visible-sm-inline, +.visible-sm-inline-block, +.visible-md-block, +.visible-md-inline, +.visible-md-inline-block, +.visible-lg-block, +.visible-lg-inline, +.visible-lg-inline-block { + display: none !important; +} +@media (max-width: 767px) { + .visible-xs { + display: block !important; + } + table.visible-xs { + display: table !important; + } + tr.visible-xs { + display: table-row !important; + } + th.visible-xs, + td.visible-xs { + display: table-cell !important; + } +} +@media (max-width: 767px) { + .visible-xs-block { + display: block !important; + } +} +@media (max-width: 767px) { + .visible-xs-inline { + display: inline !important; + } +} +@media (max-width: 767px) { + .visible-xs-inline-block { + display: inline-block !important; + } +} +@media (min-width: 768px) and (max-width: 991px) { + .visible-sm { + display: block !important; + } + table.visible-sm { + display: table !important; + } + tr.visible-sm { + display: table-row !important; + } + th.visible-sm, + td.visible-sm { + display: table-cell !important; + } +} +@media (min-width: 768px) and (max-width: 991px) { + .visible-sm-block { + display: block !important; + } +} +@media (min-width: 768px) and (max-width: 991px) { + .visible-sm-inline { + display: inline !important; + } +} +@media (min-width: 768px) and (max-width: 991px) { + .visible-sm-inline-block { + display: inline-block !important; + } +} +@media (min-width: 992px) and (max-width: 1199px) { + .visible-md { + display: block !important; + } + table.visible-md { + display: table !important; + } + tr.visible-md { + display: table-row !important; + } + th.visible-md, + td.visible-md { + display: table-cell !important; + } +} +@media (min-width: 992px) and (max-width: 1199px) { + .visible-md-block { + display: block !important; + } +} +@media (min-width: 992px) and (max-width: 1199px) { + .visible-md-inline { + display: inline !important; + } +} +@media (min-width: 992px) and (max-width: 1199px) { + .visible-md-inline-block { + display: inline-block !important; + } +} +@media (min-width: 1200px) { + .visible-lg { + display: block !important; + } + table.visible-lg { + display: table !important; + } + tr.visible-lg { + display: table-row !important; + } + th.visible-lg, + td.visible-lg { + display: table-cell !important; + } +} +@media (min-width: 1200px) { + .visible-lg-block { + display: block !important; + } +} +@media (min-width: 1200px) { + .visible-lg-inline { + display: inline !important; + } +} +@media (min-width: 1200px) { + .visible-lg-inline-block { + display: inline-block !important; + } +} +@media (max-width: 767px) { + .hidden-xs { + display: none !important; + } +} +@media (min-width: 768px) and (max-width: 991px) { + .hidden-sm { + display: none !important; + } +} +@media (min-width: 992px) and (max-width: 1199px) { + .hidden-md { + display: none !important; + } +} +@media (min-width: 1200px) { + .hidden-lg { + display: none !important; + } +} +.visible-print { + display: none !important; +} +@media print { + .visible-print { + display: block !important; + } + table.visible-print { + display: table !important; + } + tr.visible-print { + display: table-row !important; + } + th.visible-print, + td.visible-print { + display: table-cell !important; + } +} +.visible-print-block { + display: none !important; +} +@media print { + .visible-print-block { + display: block !important; + } +} +.visible-print-inline { + display: none !important; +} +@media print { + .visible-print-inline { + display: inline !important; + } +} +.visible-print-inline-block { + display: none !important; +} +@media print { + .visible-print-inline-block { + display: inline-block !important; + } +} +@media print { + .hidden-print { + display: none !important; + } +} +/*# sourceMappingURL=bootstrap.css.map */ diff --git a/web/css/bootstrap-3.3.7.min.css b/web/css/bootstrap-3.3.7.min.css deleted file mode 100644 index ed3905e0e..000000000 --- a/web/css/bootstrap-3.3.7.min.css +++ /dev/null @@ -1,6 +0,0 @@ -/*! - * Bootstrap v3.3.7 (http://getbootstrap.com) - * Copyright 2011-2016 Twitter, Inc. - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - *//*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{margin:.67em 0;font-size:2em}mark{color:#000;background:#ff0}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{height:0;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{margin:0;font:inherit;color:inherit}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}input{line-height:normal}input[type=checkbox],input[type=radio]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{padding:.35em .625em .75em;margin:0 2px;border:1px solid silver}legend{padding:0;border:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-spacing:0;border-collapse:collapse}td,th{padding:0}/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */@media print{*,:after,:before{color:#000!important;text-shadow:none!important;background:0 0!important;-webkit-box-shadow:none!important;box-shadow:none!important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="javascript:"]:after,a[href^="#"]:after{content:""}blockquote,pre{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}img{max-width:100%!important}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}.navbar{display:none}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000!important}.label{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered td,.table-bordered th{border:1px solid #ddd!important}}@font-face{font-family:'Glyphicons Halflings';src:url(../fonts/glyphicons-halflings-regular.eot);src:url(../fonts/glyphicons-halflings-regular.eot?#iefix) format('embedded-opentype'),url(../fonts/glyphicons-halflings-regular.woff2) format('woff2'),url(../fonts/glyphicons-halflings-regular.woff) format('woff'),url(../fonts/glyphicons-halflings-regular.ttf) format('truetype'),url(../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular) format('svg')}.glyphicon{position:relative;top:1px;display:inline-block;font-family:'Glyphicons Halflings';font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.glyphicon-asterisk:before{content:"\002a"}.glyphicon-plus:before{content:"\002b"}.glyphicon-eur:before,.glyphicon-euro:before{content:"\20ac"}.glyphicon-minus:before{content:"\2212"}.glyphicon-cloud:before{content:"\2601"}.glyphicon-envelope:before{content:"\2709"}.glyphicon-pencil:before{content:"\270f"}.glyphicon-glass:before{content:"\e001"}.glyphicon-music:before{content:"\e002"}.glyphicon-search:before{content:"\e003"}.glyphicon-heart:before{content:"\e005"}.glyphicon-star:before{content:"\e006"}.glyphicon-star-empty:before{content:"\e007"}.glyphicon-user:before{content:"\e008"}.glyphicon-film:before{content:"\e009"}.glyphicon-th-large:before{content:"\e010"}.glyphicon-th:before{content:"\e011"}.glyphicon-th-list:before{content:"\e012"}.glyphicon-ok:before{content:"\e013"}.glyphicon-remove:before{content:"\e014"}.glyphicon-zoom-in:before{content:"\e015"}.glyphicon-zoom-out:before{content:"\e016"}.glyphicon-off:before{content:"\e017"}.glyphicon-signal:before{content:"\e018"}.glyphicon-cog:before{content:"\e019"}.glyphicon-trash:before{content:"\e020"}.glyphicon-home:before{content:"\e021"}.glyphicon-file:before{content:"\e022"}.glyphicon-time:before{content:"\e023"}.glyphicon-road:before{content:"\e024"}.glyphicon-download-alt:before{content:"\e025"}.glyphicon-download:before{content:"\e026"}.glyphicon-upload:before{content:"\e027"}.glyphicon-inbox:before{content:"\e028"}.glyphicon-play-circle:before{content:"\e029"}.glyphicon-repeat:before{content:"\e030"}.glyphicon-refresh:before{content:"\e031"}.glyphicon-list-alt:before{content:"\e032"}.glyphicon-lock:before{content:"\e033"}.glyphicon-flag:before{content:"\e034"}.glyphicon-headphones:before{content:"\e035"}.glyphicon-volume-off:before{content:"\e036"}.glyphicon-volume-down:before{content:"\e037"}.glyphicon-volume-up:before{content:"\e038"}.glyphicon-qrcode:before{content:"\e039"}.glyphicon-barcode:before{content:"\e040"}.glyphicon-tag:before{content:"\e041"}.glyphicon-tags:before{content:"\e042"}.glyphicon-book:before{content:"\e043"}.glyphicon-bookmark:before{content:"\e044"}.glyphicon-print:before{content:"\e045"}.glyphicon-camera:before{content:"\e046"}.glyphicon-font:before{content:"\e047"}.glyphicon-bold:before{content:"\e048"}.glyphicon-italic:before{content:"\e049"}.glyphicon-text-height:before{content:"\e050"}.glyphicon-text-width:before{content:"\e051"}.glyphicon-align-left:before{content:"\e052"}.glyphicon-align-center:before{content:"\e053"}.glyphicon-align-right:before{content:"\e054"}.glyphicon-align-justify:before{content:"\e055"}.glyphicon-list:before{content:"\e056"}.glyphicon-indent-left:before{content:"\e057"}.glyphicon-indent-right:before{content:"\e058"}.glyphicon-facetime-video:before{content:"\e059"}.glyphicon-picture:before{content:"\e060"}.glyphicon-map-marker:before{content:"\e062"}.glyphicon-adjust:before{content:"\e063"}.glyphicon-tint:before{content:"\e064"}.glyphicon-edit:before{content:"\e065"}.glyphicon-share:before{content:"\e066"}.glyphicon-check:before{content:"\e067"}.glyphicon-move:before{content:"\e068"}.glyphicon-step-backward:before{content:"\e069"}.glyphicon-fast-backward:before{content:"\e070"}.glyphicon-backward:before{content:"\e071"}.glyphicon-play:before{content:"\e072"}.glyphicon-pause:before{content:"\e073"}.glyphicon-stop:before{content:"\e074"}.glyphicon-forward:before{content:"\e075"}.glyphicon-fast-forward:before{content:"\e076"}.glyphicon-step-forward:before{content:"\e077"}.glyphicon-eject:before{content:"\e078"}.glyphicon-chevron-left:before{content:"\e079"}.glyphicon-chevron-right:before{content:"\e080"}.glyphicon-plus-sign:before{content:"\e081"}.glyphicon-minus-sign:before{content:"\e082"}.glyphicon-remove-sign:before{content:"\e083"}.glyphicon-ok-sign:before{content:"\e084"}.glyphicon-question-sign:before{content:"\e085"}.glyphicon-info-sign:before{content:"\e086"}.glyphicon-screenshot:before{content:"\e087"}.glyphicon-remove-circle:before{content:"\e088"}.glyphicon-ok-circle:before{content:"\e089"}.glyphicon-ban-circle:before{content:"\e090"}.glyphicon-arrow-left:before{content:"\e091"}.glyphicon-arrow-right:before{content:"\e092"}.glyphicon-arrow-up:before{content:"\e093"}.glyphicon-arrow-down:before{content:"\e094"}.glyphicon-share-alt:before{content:"\e095"}.glyphicon-resize-full:before{content:"\e096"}.glyphicon-resize-small:before{content:"\e097"}.glyphicon-exclamation-sign:before{content:"\e101"}.glyphicon-gift:before{content:"\e102"}.glyphicon-leaf:before{content:"\e103"}.glyphicon-fire:before{content:"\e104"}.glyphicon-eye-open:before{content:"\e105"}.glyphicon-eye-close:before{content:"\e106"}.glyphicon-warning-sign:before{content:"\e107"}.glyphicon-plane:before{content:"\e108"}.glyphicon-calendar:before{content:"\e109"}.glyphicon-random:before{content:"\e110"}.glyphicon-comment:before{content:"\e111"}.glyphicon-magnet:before{content:"\e112"}.glyphicon-chevron-up:before{content:"\e113"}.glyphicon-chevron-down:before{content:"\e114"}.glyphicon-retweet:before{content:"\e115"}.glyphicon-shopping-cart:before{content:"\e116"}.glyphicon-folder-close:before{content:"\e117"}.glyphicon-folder-open:before{content:"\e118"}.glyphicon-resize-vertical:before{content:"\e119"}.glyphicon-resize-horizontal:before{content:"\e120"}.glyphicon-hdd:before{content:"\e121"}.glyphicon-bullhorn:before{content:"\e122"}.glyphicon-bell:before{content:"\e123"}.glyphicon-certificate:before{content:"\e124"}.glyphicon-thumbs-up:before{content:"\e125"}.glyphicon-thumbs-down:before{content:"\e126"}.glyphicon-hand-right:before{content:"\e127"}.glyphicon-hand-left:before{content:"\e128"}.glyphicon-hand-up:before{content:"\e129"}.glyphicon-hand-down:before{content:"\e130"}.glyphicon-circle-arrow-right:before{content:"\e131"}.glyphicon-circle-arrow-left:before{content:"\e132"}.glyphicon-circle-arrow-up:before{content:"\e133"}.glyphicon-circle-arrow-down:before{content:"\e134"}.glyphicon-globe:before{content:"\e135"}.glyphicon-wrench:before{content:"\e136"}.glyphicon-tasks:before{content:"\e137"}.glyphicon-filter:before{content:"\e138"}.glyphicon-briefcase:before{content:"\e139"}.glyphicon-fullscreen:before{content:"\e140"}.glyphicon-dashboard:before{content:"\e141"}.glyphicon-paperclip:before{content:"\e142"}.glyphicon-heart-empty:before{content:"\e143"}.glyphicon-link:before{content:"\e144"}.glyphicon-phone:before{content:"\e145"}.glyphicon-pushpin:before{content:"\e146"}.glyphicon-usd:before{content:"\e148"}.glyphicon-gbp:before{content:"\e149"}.glyphicon-sort:before{content:"\e150"}.glyphicon-sort-by-alphabet:before{content:"\e151"}.glyphicon-sort-by-alphabet-alt:before{content:"\e152"}.glyphicon-sort-by-order:before{content:"\e153"}.glyphicon-sort-by-order-alt:before{content:"\e154"}.glyphicon-sort-by-attributes:before{content:"\e155"}.glyphicon-sort-by-attributes-alt:before{content:"\e156"}.glyphicon-unchecked:before{content:"\e157"}.glyphicon-expand:before{content:"\e158"}.glyphicon-collapse-down:before{content:"\e159"}.glyphicon-collapse-up:before{content:"\e160"}.glyphicon-log-in:before{content:"\e161"}.glyphicon-flash:before{content:"\e162"}.glyphicon-log-out:before{content:"\e163"}.glyphicon-new-window:before{content:"\e164"}.glyphicon-record:before{content:"\e165"}.glyphicon-save:before{content:"\e166"}.glyphicon-open:before{content:"\e167"}.glyphicon-saved:before{content:"\e168"}.glyphicon-import:before{content:"\e169"}.glyphicon-export:before{content:"\e170"}.glyphicon-send:before{content:"\e171"}.glyphicon-floppy-disk:before{content:"\e172"}.glyphicon-floppy-saved:before{content:"\e173"}.glyphicon-floppy-remove:before{content:"\e174"}.glyphicon-floppy-save:before{content:"\e175"}.glyphicon-floppy-open:before{content:"\e176"}.glyphicon-credit-card:before{content:"\e177"}.glyphicon-transfer:before{content:"\e178"}.glyphicon-cutlery:before{content:"\e179"}.glyphicon-header:before{content:"\e180"}.glyphicon-compressed:before{content:"\e181"}.glyphicon-earphone:before{content:"\e182"}.glyphicon-phone-alt:before{content:"\e183"}.glyphicon-tower:before{content:"\e184"}.glyphicon-stats:before{content:"\e185"}.glyphicon-sd-video:before{content:"\e186"}.glyphicon-hd-video:before{content:"\e187"}.glyphicon-subtitles:before{content:"\e188"}.glyphicon-sound-stereo:before{content:"\e189"}.glyphicon-sound-dolby:before{content:"\e190"}.glyphicon-sound-5-1:before{content:"\e191"}.glyphicon-sound-6-1:before{content:"\e192"}.glyphicon-sound-7-1:before{content:"\e193"}.glyphicon-copyright-mark:before{content:"\e194"}.glyphicon-registration-mark:before{content:"\e195"}.glyphicon-cloud-download:before{content:"\e197"}.glyphicon-cloud-upload:before{content:"\e198"}.glyphicon-tree-conifer:before{content:"\e199"}.glyphicon-tree-deciduous:before{content:"\e200"}.glyphicon-cd:before{content:"\e201"}.glyphicon-save-file:before{content:"\e202"}.glyphicon-open-file:before{content:"\e203"}.glyphicon-level-up:before{content:"\e204"}.glyphicon-copy:before{content:"\e205"}.glyphicon-paste:before{content:"\e206"}.glyphicon-alert:before{content:"\e209"}.glyphicon-equalizer:before{content:"\e210"}.glyphicon-king:before{content:"\e211"}.glyphicon-queen:before{content:"\e212"}.glyphicon-pawn:before{content:"\e213"}.glyphicon-bishop:before{content:"\e214"}.glyphicon-knight:before{content:"\e215"}.glyphicon-baby-formula:before{content:"\e216"}.glyphicon-tent:before{content:"\26fa"}.glyphicon-blackboard:before{content:"\e218"}.glyphicon-bed:before{content:"\e219"}.glyphicon-apple:before{content:"\f8ff"}.glyphicon-erase:before{content:"\e221"}.glyphicon-hourglass:before{content:"\231b"}.glyphicon-lamp:before{content:"\e223"}.glyphicon-duplicate:before{content:"\e224"}.glyphicon-piggy-bank:before{content:"\e225"}.glyphicon-scissors:before{content:"\e226"}.glyphicon-bitcoin:before{content:"\e227"}.glyphicon-btc:before{content:"\e227"}.glyphicon-xbt:before{content:"\e227"}.glyphicon-yen:before{content:"\00a5"}.glyphicon-jpy:before{content:"\00a5"}.glyphicon-ruble:before{content:"\20bd"}.glyphicon-rub:before{content:"\20bd"}.glyphicon-scale:before{content:"\e230"}.glyphicon-ice-lolly:before{content:"\e231"}.glyphicon-ice-lolly-tasted:before{content:"\e232"}.glyphicon-education:before{content:"\e233"}.glyphicon-option-horizontal:before{content:"\e234"}.glyphicon-option-vertical:before{content:"\e235"}.glyphicon-menu-hamburger:before{content:"\e236"}.glyphicon-modal-window:before{content:"\e237"}.glyphicon-oil:before{content:"\e238"}.glyphicon-grain:before{content:"\e239"}.glyphicon-sunglasses:before{content:"\e240"}.glyphicon-text-size:before{content:"\e241"}.glyphicon-text-color:before{content:"\e242"}.glyphicon-text-background:before{content:"\e243"}.glyphicon-object-align-top:before{content:"\e244"}.glyphicon-object-align-bottom:before{content:"\e245"}.glyphicon-object-align-horizontal:before{content:"\e246"}.glyphicon-object-align-left:before{content:"\e247"}.glyphicon-object-align-vertical:before{content:"\e248"}.glyphicon-object-align-right:before{content:"\e249"}.glyphicon-triangle-right:before{content:"\e250"}.glyphicon-triangle-left:before{content:"\e251"}.glyphicon-triangle-bottom:before{content:"\e252"}.glyphicon-triangle-top:before{content:"\e253"}.glyphicon-console:before{content:"\e254"}.glyphicon-superscript:before{content:"\e255"}.glyphicon-subscript:before{content:"\e256"}.glyphicon-menu-left:before{content:"\e257"}.glyphicon-menu-right:before{content:"\e258"}.glyphicon-menu-down:before{content:"\e259"}.glyphicon-menu-up:before{content:"\e260"}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}:after,:before{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#333;background-color:#fff}button,input,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#337ab7;text-decoration:none}a:focus,a:hover{color:#23527c;text-decoration:underline}a:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.carousel-inner>.item>a>img,.carousel-inner>.item>img,.img-responsive,.thumbnail a>img,.thumbnail>img{display:block;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{display:inline-block;max-width:100%;height:auto;padding:4px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}[role=button]{cursor:pointer}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{font-family:inherit;font-weight:500;line-height:1.1;color:inherit}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-weight:400;line-height:1;color:#777}.h1,.h2,.h3,h1,h2,h3{margin-top:20px;margin-bottom:10px}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small{font-size:65%}.h4,.h5,.h6,h4,h5,h6{margin-top:10px;margin-bottom:10px}.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-size:75%}.h1,h1{font-size:36px}.h2,h2{font-size:30px}.h3,h3{font-size:24px}.h4,h4{font-size:18px}.h5,h5{font-size:14px}.h6,h6{font-size:12px}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:16px;font-weight:300;line-height:1.4}@media (min-width:768px){.lead{font-size:21px}}.small,small{font-size:85%}.mark,mark{padding:.2em;background-color:#fcf8e3}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-muted{color:#777}.text-primary{color:#337ab7}a.text-primary:focus,a.text-primary:hover{color:#286090}.text-success{color:#3c763d}a.text-success:focus,a.text-success:hover{color:#2b542c}.text-info{color:#31708f}a.text-info:focus,a.text-info:hover{color:#245269}.text-warning{color:#8a6d3b}a.text-warning:focus,a.text-warning:hover{color:#66512c}.text-danger{color:#a94442}a.text-danger:focus,a.text-danger:hover{color:#843534}.bg-primary{color:#fff;background-color:#337ab7}a.bg-primary:focus,a.bg-primary:hover{background-color:#286090}.bg-success{background-color:#dff0d8}a.bg-success:focus,a.bg-success:hover{background-color:#c1e2b3}.bg-info{background-color:#d9edf7}a.bg-info:focus,a.bg-info:hover{background-color:#afd9ee}.bg-warning{background-color:#fcf8e3}a.bg-warning:focus,a.bg-warning:hover{background-color:#f7ecb5}.bg-danger{background-color:#f2dede}a.bg-danger:focus,a.bg-danger:hover{background-color:#e4b9b9}.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #eee}ol,ul{margin-top:0;margin-bottom:10px}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;margin-left:-5px;list-style:none}.list-inline>li{display:inline-block;padding-right:5px;padding-left:5px}dl{margin-top:0;margin-bottom:20px}dd,dt{line-height:1.42857143}dt{font-weight:700}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;overflow:hidden;clear:left;text-align:right;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[data-original-title],abbr[title]{cursor:help;border-bottom:1px dotted #777}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10px 20px;margin:0 0 20px;font-size:17.5px;border-left:5px solid #eee}blockquote ol:last-child,blockquote p:last-child,blockquote ul:last-child{margin-bottom:0}blockquote .small,blockquote footer,blockquote small{display:block;font-size:80%;line-height:1.42857143;color:#777}blockquote .small:before,blockquote footer:before,blockquote small:before{content:'\2014 \00A0'}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;text-align:right;border-right:5px solid #eee;border-left:0}.blockquote-reverse .small:before,.blockquote-reverse footer:before,.blockquote-reverse small:before,blockquote.pull-right .small:before,blockquote.pull-right footer:before,blockquote.pull-right small:before{content:''}.blockquote-reverse .small:after,.blockquote-reverse footer:after,.blockquote-reverse small:after,blockquote.pull-right .small:after,blockquote.pull-right footer:after,blockquote.pull-right small:after{content:'\00A0 \2014'}address{margin-bottom:20px;font-style:normal;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;border-radius:4px}kbd{padding:2px 4px;font-size:90%;color:#fff;background-color:#333;border-radius:3px;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.25);box-shadow:inset 0 -1px 0 rgba(0,0,0,.25)}kbd kbd{padding:0;font-size:100%;font-weight:700;-webkit-box-shadow:none;box-shadow:none}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:1.42857143;color:#333;word-break:break-all;word-wrap:break-word;background-color:#f5f5f5;border:1px solid #ccc;border-radius:4px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{margin-right:-15px;margin-left:-15px}.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{position:relative;min-height:1px;padding-right:15px;padding-left:15px}.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:auto}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:auto}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0}@media (min-width:768px){.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:auto}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:auto}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0}}@media (min-width:992px){.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:auto}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:auto}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0}}@media (min-width:1200px){.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:auto}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:auto}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0}}table{background-color:transparent}caption{padding-top:8px;padding-bottom:8px;color:#777;text-align:left}th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:20px}.table>tbody>tr>td,.table>tbody>tr>th,.table>tfoot>tr>td,.table>tfoot>tr>th,.table>thead>tr>td,.table>thead>tr>th{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #ddd}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ddd}.table>caption+thead>tr:first-child>td,.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>td,.table>thead:first-child>tr:first-child>th{border-top:0}.table>tbody+tbody{border-top:2px solid #ddd}.table .table{background-color:#fff}.table-condensed>tbody>tr>td,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>td,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>thead>tr>th{padding:5px}.table-bordered{border:1px solid #ddd}.table-bordered>tbody>tr>td,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>td,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border:1px solid #ddd}.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border-bottom-width:2px}.table-striped>tbody>tr:nth-of-type(odd){background-color:#f9f9f9}.table-hover>tbody>tr:hover{background-color:#f5f5f5}table col[class*=col-]{position:static;display:table-column;float:none}table td[class*=col-],table th[class*=col-]{position:static;display:table-cell;float:none}.table>tbody>tr.active>td,.table>tbody>tr.active>th,.table>tbody>tr>td.active,.table>tbody>tr>th.active,.table>tfoot>tr.active>td,.table>tfoot>tr.active>th,.table>tfoot>tr>td.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>thead>tr.active>th,.table>thead>tr>td.active,.table>thead>tr>th.active{background-color:#f5f5f5}.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr.active:hover>th,.table-hover>tbody>tr:hover>.active,.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover{background-color:#e8e8e8}.table>tbody>tr.success>td,.table>tbody>tr.success>th,.table>tbody>tr>td.success,.table>tbody>tr>th.success,.table>tfoot>tr.success>td,.table>tfoot>tr.success>th,.table>tfoot>tr>td.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>thead>tr.success>th,.table>thead>tr>td.success,.table>thead>tr>th.success{background-color:#dff0d8}.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr.success:hover>th,.table-hover>tbody>tr:hover>.success,.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover{background-color:#d0e9c6}.table>tbody>tr.info>td,.table>tbody>tr.info>th,.table>tbody>tr>td.info,.table>tbody>tr>th.info,.table>tfoot>tr.info>td,.table>tfoot>tr.info>th,.table>tfoot>tr>td.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>thead>tr.info>th,.table>thead>tr>td.info,.table>thead>tr>th.info{background-color:#d9edf7}.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr.info:hover>th,.table-hover>tbody>tr:hover>.info,.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover{background-color:#c4e3f3}.table>tbody>tr.warning>td,.table>tbody>tr.warning>th,.table>tbody>tr>td.warning,.table>tbody>tr>th.warning,.table>tfoot>tr.warning>td,.table>tfoot>tr.warning>th,.table>tfoot>tr>td.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>thead>tr.warning>th,.table>thead>tr>td.warning,.table>thead>tr>th.warning{background-color:#fcf8e3}.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr.warning:hover>th,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover{background-color:#faf2cc}.table>tbody>tr.danger>td,.table>tbody>tr.danger>th,.table>tbody>tr>td.danger,.table>tbody>tr>th.danger,.table>tfoot>tr.danger>td,.table>tfoot>tr.danger>th,.table>tfoot>tr>td.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>thead>tr.danger>th,.table>thead>tr>td.danger,.table>thead>tr>th.danger{background-color:#f2dede}.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr.danger:hover>th,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover{background-color:#ebcccc}.table-responsive{min-height:.01%;overflow-x:auto}@media screen and (max-width:767px){.table-responsive{width:100%;margin-bottom:15px;overflow-y:hidden;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #ddd}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>td,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>thead>tr>th{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:inherit;color:#333;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:700}input[type=search]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type=checkbox],input[type=radio]{margin:4px 0 0;margin-top:1px\9;line-height:normal}input[type=file]{display:block}input[type=range]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type=file]:focus,input[type=checkbox]:focus,input[type=radio]:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:7px;font-size:14px;line-height:1.42857143;color:#555}.form-control{display:block;width:100%;height:34px;padding:6px 12px;font-size:14px;line-height:1.42857143;color:#555;background-color:#fff;background-image:none;border:1px solid #ccc;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075);-webkit-transition:border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s;-o-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.form-control:focus{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)}.form-control::-moz-placeholder{color:#999;opacity:1}.form-control:-ms-input-placeholder{color:#999}.form-control::-webkit-input-placeholder{color:#999}.form-control::-ms-expand{background-color:transparent;border:0}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{background-color:#eee;opacity:1}.form-control[disabled],fieldset[disabled] .form-control{cursor:not-allowed}textarea.form-control{height:auto}input[type=search]{-webkit-appearance:none}@media screen and (-webkit-min-device-pixel-ratio:0){input[type=date].form-control,input[type=time].form-control,input[type=datetime-local].form-control,input[type=month].form-control{line-height:34px}.input-group-sm input[type=date],.input-group-sm input[type=time],.input-group-sm input[type=datetime-local],.input-group-sm input[type=month],input[type=date].input-sm,input[type=time].input-sm,input[type=datetime-local].input-sm,input[type=month].input-sm{line-height:30px}.input-group-lg input[type=date],.input-group-lg input[type=time],.input-group-lg input[type=datetime-local],.input-group-lg input[type=month],input[type=date].input-lg,input[type=time].input-lg,input[type=datetime-local].input-lg,input[type=month].input-lg{line-height:46px}}.form-group{margin-bottom:15px}.checkbox,.radio{position:relative;display:block;margin-top:10px;margin-bottom:10px}.checkbox label,.radio label{min-height:20px;padding-left:20px;margin-bottom:0;font-weight:400;cursor:pointer}.checkbox input[type=checkbox],.checkbox-inline input[type=checkbox],.radio input[type=radio],.radio-inline input[type=radio]{position:absolute;margin-top:4px\9;margin-left:-20px}.checkbox+.checkbox,.radio+.radio{margin-top:-5px}.checkbox-inline,.radio-inline{position:relative;display:inline-block;padding-left:20px;margin-bottom:0;font-weight:400;vertical-align:middle;cursor:pointer}.checkbox-inline+.checkbox-inline,.radio-inline+.radio-inline{margin-top:0;margin-left:10px}fieldset[disabled] input[type=checkbox],fieldset[disabled] input[type=radio],input[type=checkbox].disabled,input[type=checkbox][disabled],input[type=radio].disabled,input[type=radio][disabled]{cursor:not-allowed}.checkbox-inline.disabled,.radio-inline.disabled,fieldset[disabled] .checkbox-inline,fieldset[disabled] .radio-inline{cursor:not-allowed}.checkbox.disabled label,.radio.disabled label,fieldset[disabled] .checkbox label,fieldset[disabled] .radio label{cursor:not-allowed}.form-control-static{min-height:34px;padding-top:7px;padding-bottom:7px;margin-bottom:0}.form-control-static.input-lg,.form-control-static.input-sm{padding-right:0;padding-left:0}.input-sm{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-sm{height:30px;line-height:30px}select[multiple].input-sm,textarea.input-sm{height:auto}.form-group-sm .form-control{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.form-group-sm select.form-control{height:30px;line-height:30px}.form-group-sm select[multiple].form-control,.form-group-sm textarea.form-control{height:auto}.form-group-sm .form-control-static{height:30px;min-height:32px;padding:6px 10px;font-size:12px;line-height:1.5}.input-lg{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-lg{height:46px;line-height:46px}select[multiple].input-lg,textarea.input-lg{height:auto}.form-group-lg .form-control{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.form-group-lg select.form-control{height:46px;line-height:46px}.form-group-lg select[multiple].form-control,.form-group-lg textarea.form-control{height:auto}.form-group-lg .form-control-static{height:46px;min-height:38px;padding:11px 16px;font-size:18px;line-height:1.3333333}.has-feedback{position:relative}.has-feedback .form-control{padding-right:42.5px}.form-control-feedback{position:absolute;top:0;right:0;z-index:2;display:block;width:34px;height:34px;line-height:34px;text-align:center;pointer-events:none}.form-group-lg .form-control+.form-control-feedback,.input-group-lg+.form-control-feedback,.input-lg+.form-control-feedback{width:46px;height:46px;line-height:46px}.form-group-sm .form-control+.form-control-feedback,.input-group-sm+.form-control-feedback,.input-sm+.form-control-feedback{width:30px;height:30px;line-height:30px}.has-success .checkbox,.has-success .checkbox-inline,.has-success .control-label,.has-success .help-block,.has-success .radio,.has-success .radio-inline,.has-success.checkbox label,.has-success.checkbox-inline label,.has-success.radio label,.has-success.radio-inline label{color:#3c763d}.has-success .form-control{border-color:#3c763d;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-success .form-control:focus{border-color:#2b542c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168}.has-success .input-group-addon{color:#3c763d;background-color:#dff0d8;border-color:#3c763d}.has-success .form-control-feedback{color:#3c763d}.has-warning .checkbox,.has-warning .checkbox-inline,.has-warning .control-label,.has-warning .help-block,.has-warning .radio,.has-warning .radio-inline,.has-warning.checkbox label,.has-warning.checkbox-inline label,.has-warning.radio label,.has-warning.radio-inline label{color:#8a6d3b}.has-warning .form-control{border-color:#8a6d3b;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-warning .form-control:focus{border-color:#66512c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b}.has-warning .input-group-addon{color:#8a6d3b;background-color:#fcf8e3;border-color:#8a6d3b}.has-warning .form-control-feedback{color:#8a6d3b}.has-error .checkbox,.has-error .checkbox-inline,.has-error .control-label,.has-error .help-block,.has-error .radio,.has-error .radio-inline,.has-error.checkbox label,.has-error.checkbox-inline label,.has-error.radio label,.has-error.radio-inline label{color:#a94442}.has-error .form-control{border-color:#a94442;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-error .form-control:focus{border-color:#843534;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483}.has-error .input-group-addon{color:#a94442;background-color:#f2dede;border-color:#a94442}.has-error .form-control-feedback{color:#a94442}.has-feedback label~.form-control-feedback{top:25px}.has-feedback label.sr-only~.form-control-feedback{top:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#737373}@media (min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-static{display:inline-block}.form-inline .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .form-control,.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn{width:auto}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .checkbox,.form-inline .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.form-inline .checkbox label,.form-inline .radio label{padding-left:0}.form-inline .checkbox input[type=checkbox],.form-inline .radio input[type=radio]{position:relative;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .checkbox,.form-horizontal .checkbox-inline,.form-horizontal .radio,.form-horizontal .radio-inline{padding-top:7px;margin-top:0;margin-bottom:0}.form-horizontal .checkbox,.form-horizontal .radio{min-height:27px}.form-horizontal .form-group{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.form-horizontal .control-label{padding-top:7px;margin-bottom:0;text-align:right}}.form-horizontal .has-feedback .form-control-feedback{right:15px}@media (min-width:768px){.form-horizontal .form-group-lg .control-label{padding-top:11px;font-size:18px}}@media (min-width:768px){.form-horizontal .form-group-sm .control-label{padding-top:6px;font-size:12px}}.btn{display:inline-block;padding:6px 12px;margin-bottom:0;font-size:14px;font-weight:400;line-height:1.42857143;text-align:center;white-space:nowrap;vertical-align:middle;-ms-touch-action:manipulation;touch-action:manipulation;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-image:none;border:1px solid transparent;border-radius:4px}.btn.active.focus,.btn.active:focus,.btn.focus,.btn:active.focus,.btn:active:focus,.btn:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn.focus,.btn:focus,.btn:hover{color:#333;text-decoration:none}.btn.active,.btn:active{background-image:none;outline:0;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;filter:alpha(opacity=65);-webkit-box-shadow:none;box-shadow:none;opacity:.65}a.btn.disabled,fieldset[disabled] a.btn{pointer-events:none}.btn-default{color:#333;background-color:#fff;border-color:#ccc}.btn-default.focus,.btn-default:focus{color:#333;background-color:#e6e6e6;border-color:#8c8c8c}.btn-default:hover{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default.active.focus,.btn-default.active:focus,.btn-default.active:hover,.btn-default:active.focus,.btn-default:active:focus,.btn-default:active:hover,.open>.dropdown-toggle.btn-default.focus,.open>.dropdown-toggle.btn-default:focus,.open>.dropdown-toggle.btn-default:hover{color:#333;background-color:#d4d4d4;border-color:#8c8c8c}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{background-image:none}.btn-default.disabled.focus,.btn-default.disabled:focus,.btn-default.disabled:hover,.btn-default[disabled].focus,.btn-default[disabled]:focus,.btn-default[disabled]:hover,fieldset[disabled] .btn-default.focus,fieldset[disabled] .btn-default:focus,fieldset[disabled] .btn-default:hover{background-color:#fff;border-color:#ccc}.btn-default .badge{color:#fff;background-color:#333}.btn-primary{color:#fff;background-color:#337ab7;border-color:#2e6da4}.btn-primary.focus,.btn-primary:focus{color:#fff;background-color:#286090;border-color:#122b40}.btn-primary:hover{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary.active.focus,.btn-primary.active:focus,.btn-primary.active:hover,.btn-primary:active.focus,.btn-primary:active:focus,.btn-primary:active:hover,.open>.dropdown-toggle.btn-primary.focus,.open>.dropdown-toggle.btn-primary:focus,.open>.dropdown-toggle.btn-primary:hover{color:#fff;background-color:#204d74;border-color:#122b40}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{background-image:none}.btn-primary.disabled.focus,.btn-primary.disabled:focus,.btn-primary.disabled:hover,.btn-primary[disabled].focus,.btn-primary[disabled]:focus,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary.focus,fieldset[disabled] .btn-primary:focus,fieldset[disabled] .btn-primary:hover{background-color:#337ab7;border-color:#2e6da4}.btn-primary .badge{color:#337ab7;background-color:#fff}.btn-success{color:#fff;background-color:#5cb85c;border-color:#4cae4c}.btn-success.focus,.btn-success:focus{color:#fff;background-color:#449d44;border-color:#255625}.btn-success:hover{color:#fff;background-color:#449d44;border-color:#398439}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{color:#fff;background-color:#449d44;border-color:#398439}.btn-success.active.focus,.btn-success.active:focus,.btn-success.active:hover,.btn-success:active.focus,.btn-success:active:focus,.btn-success:active:hover,.open>.dropdown-toggle.btn-success.focus,.open>.dropdown-toggle.btn-success:focus,.open>.dropdown-toggle.btn-success:hover{color:#fff;background-color:#398439;border-color:#255625}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{background-image:none}.btn-success.disabled.focus,.btn-success.disabled:focus,.btn-success.disabled:hover,.btn-success[disabled].focus,.btn-success[disabled]:focus,.btn-success[disabled]:hover,fieldset[disabled] .btn-success.focus,fieldset[disabled] .btn-success:focus,fieldset[disabled] .btn-success:hover{background-color:#5cb85c;border-color:#4cae4c}.btn-success .badge{color:#5cb85c;background-color:#fff}.btn-info{color:#fff;background-color:#5bc0de;border-color:#46b8da}.btn-info.focus,.btn-info:focus{color:#fff;background-color:#31b0d5;border-color:#1b6d85}.btn-info:hover{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info.active.focus,.btn-info.active:focus,.btn-info.active:hover,.btn-info:active.focus,.btn-info:active:focus,.btn-info:active:hover,.open>.dropdown-toggle.btn-info.focus,.open>.dropdown-toggle.btn-info:focus,.open>.dropdown-toggle.btn-info:hover{color:#fff;background-color:#269abc;border-color:#1b6d85}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{background-image:none}.btn-info.disabled.focus,.btn-info.disabled:focus,.btn-info.disabled:hover,.btn-info[disabled].focus,.btn-info[disabled]:focus,.btn-info[disabled]:hover,fieldset[disabled] .btn-info.focus,fieldset[disabled] .btn-info:focus,fieldset[disabled] .btn-info:hover{background-color:#5bc0de;border-color:#46b8da}.btn-info .badge{color:#5bc0de;background-color:#fff}.btn-warning{color:#fff;background-color:#f0ad4e;border-color:#eea236}.btn-warning.focus,.btn-warning:focus{color:#fff;background-color:#ec971f;border-color:#985f0d}.btn-warning:hover{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning.active.focus,.btn-warning.active:focus,.btn-warning.active:hover,.btn-warning:active.focus,.btn-warning:active:focus,.btn-warning:active:hover,.open>.dropdown-toggle.btn-warning.focus,.open>.dropdown-toggle.btn-warning:focus,.open>.dropdown-toggle.btn-warning:hover{color:#fff;background-color:#d58512;border-color:#985f0d}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{background-image:none}.btn-warning.disabled.focus,.btn-warning.disabled:focus,.btn-warning.disabled:hover,.btn-warning[disabled].focus,.btn-warning[disabled]:focus,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning.focus,fieldset[disabled] .btn-warning:focus,fieldset[disabled] .btn-warning:hover{background-color:#f0ad4e;border-color:#eea236}.btn-warning .badge{color:#f0ad4e;background-color:#fff}.btn-danger{color:#fff;background-color:#d9534f;border-color:#d43f3a}.btn-danger.focus,.btn-danger:focus{color:#fff;background-color:#c9302c;border-color:#761c19}.btn-danger:hover{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger.active.focus,.btn-danger.active:focus,.btn-danger.active:hover,.btn-danger:active.focus,.btn-danger:active:focus,.btn-danger:active:hover,.open>.dropdown-toggle.btn-danger.focus,.open>.dropdown-toggle.btn-danger:focus,.open>.dropdown-toggle.btn-danger:hover{color:#fff;background-color:#ac2925;border-color:#761c19}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{background-image:none}.btn-danger.disabled.focus,.btn-danger.disabled:focus,.btn-danger.disabled:hover,.btn-danger[disabled].focus,.btn-danger[disabled]:focus,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger.focus,fieldset[disabled] .btn-danger:focus,fieldset[disabled] .btn-danger:hover{background-color:#d9534f;border-color:#d43f3a}.btn-danger .badge{color:#d9534f;background-color:#fff}.btn-link{font-weight:400;color:#337ab7;border-radius:0}.btn-link,.btn-link.active,.btn-link:active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.btn-link,.btn-link:active,.btn-link:focus,.btn-link:hover{border-color:transparent}.btn-link:focus,.btn-link:hover{color:#23527c;text-decoration:underline;background-color:transparent}.btn-link[disabled]:focus,.btn-link[disabled]:hover,fieldset[disabled] .btn-link:focus,fieldset[disabled] .btn-link:hover{color:#777;text-decoration:none}.btn-group-lg>.btn,.btn-lg{padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.btn-group-sm>.btn,.btn-sm{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.btn-group-xs>.btn,.btn-xs{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.fade{opacity:0;-webkit-transition:opacity .15s linear;-o-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}tr.collapse.in{display:table-row}tbody.collapse.in{display:table-row-group}.collapsing{position:relative;height:0;overflow:hidden;-webkit-transition-timing-function:ease;-o-transition-timing-function:ease;transition-timing-function:ease;-webkit-transition-duration:.35s;-o-transition-duration:.35s;transition-duration:.35s;-webkit-transition-property:height,visibility;-o-transition-property:height,visibility;transition-property:height,visibility}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px dashed;border-top:4px solid\9;border-right:4px solid transparent;border-left:4px solid transparent}.dropdown,.dropup{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;font-size:14px;text-align:left;list-style:none;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.15);border-radius:4px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,.175);box-shadow:0 6px 12px rgba(0,0,0,.175)}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.42857143;color:#333;white-space:nowrap}.dropdown-menu>li>a:focus,.dropdown-menu>li>a:hover{color:#262626;text-decoration:none;background-color:#f5f5f5}.dropdown-menu>.active>a,.dropdown-menu>.active>a:focus,.dropdown-menu>.active>a:hover{color:#fff;text-decoration:none;background-color:#337ab7;outline:0}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{color:#777}.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{text-decoration:none;cursor:not-allowed;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{right:0;left:auto}.dropdown-menu-left{right:auto;left:0}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.42857143;color:#777;white-space:nowrap}.dropdown-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{content:"";border-top:0;border-bottom:4px dashed;border-bottom:4px solid\9}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:2px}@media (min-width:768px){.navbar-right .dropdown-menu{right:0;left:auto}.navbar-right .dropdown-menu-left{right:auto;left:0}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;float:left}.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:hover,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus,.btn-group>.btn:hover{z-index:2}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar .btn,.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-bottom-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-right:8px;padding-left:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-right:12px;padding-left:12px}.btn-group.open .dropdown-toggle{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-group.open .dropdown-toggle.btn-link{-webkit-box-shadow:none;box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-top-left-radius:0;border-top-right-radius:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-top-right-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{display:table-cell;float:none;width:1%}.btn-group-justified>.btn-group .btn{width:100%}.btn-group-justified>.btn-group .dropdown-menu{left:auto}[data-toggle=buttons]>.btn input[type=checkbox],[data-toggle=buttons]>.btn input[type=radio],[data-toggle=buttons]>.btn-group>.btn input[type=checkbox],[data-toggle=buttons]>.btn-group>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*=col-]{float:none;padding-right:0;padding-left:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group .form-control:focus{z-index:3}.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:46px;line-height:46px}select[multiple].input-group-lg>.form-control,select[multiple].input-group-lg>.input-group-addon,select[multiple].input-group-lg>.input-group-btn>.btn,textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.input-group-lg>.input-group-btn>.btn{height:auto}.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-group-sm>.form-control,select.input-group-sm>.input-group-addon,select.input-group-sm>.input-group-btn>.btn{height:30px;line-height:30px}select[multiple].input-group-sm>.form-control,select[multiple].input-group-sm>.input-group-addon,select[multiple].input-group-sm>.input-group-btn>.btn,textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn{height:auto}.input-group .form-control,.input-group-addon,.input-group-btn{display:table-cell}.input-group .form-control:not(:first-child):not(:last-child),.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:6px 12px;font-size:14px;font-weight:400;line-height:1;color:#555;text-align:center;background-color:#eee;border:1px solid #ccc;border-radius:4px}.input-group-addon.input-sm{padding:5px 10px;font-size:12px;border-radius:3px}.input-group-addon.input-lg{padding:10px 16px;font-size:18px;border-radius:6px}.input-group-addon input[type=checkbox],.input-group-addon input[type=radio]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn-group:not(:last-child)>.btn,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:first-child>.btn-group:not(:first-child)>.btn,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:active,.input-group-btn>.btn:focus,.input-group-btn>.btn:hover{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{z-index:2;margin-left:-1px}.nav{padding-left:0;margin-bottom:0;list-style:none}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:focus,.nav>li>a:hover{text-decoration:none;background-color:#eee}.nav>li.disabled>a{color:#777}.nav>li.disabled>a:focus,.nav>li.disabled>a:hover{color:#777;text-decoration:none;cursor:not-allowed;background-color:transparent}.nav .open>a,.nav .open>a:focus,.nav .open>a:hover{background-color:#eee;border-color:#337ab7}.nav .nav-divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eee #eee #ddd}.nav-tabs>li.active>a,.nav-tabs>li.active>a:focus,.nav-tabs>li.active>a:hover{color:#555;cursor:default;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-tabs.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs.nav-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border-bottom-color:#fff}}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:4px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:focus,.nav-pills>li.active>a:hover{color:#fff;background-color:#337ab7}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{float:none}.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border-bottom-color:#fff}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.navbar{position:relative;min-height:50px;margin-bottom:20px;border:1px solid transparent}@media (min-width:768px){.navbar{border-radius:4px}}@media (min-width:768px){.navbar-header{float:left}}.navbar-collapse{padding-right:15px;padding-left:15px;overflow-x:visible;-webkit-overflow-scrolling:touch;border-top:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1)}.navbar-collapse.in{overflow-y:auto}@media (min-width:768px){.navbar-collapse{width:auto;border-top:0;-webkit-box-shadow:none;box-shadow:none}.navbar-collapse.collapse{display:block!important;height:auto!important;padding-bottom:0;overflow:visible!important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse{padding-right:0;padding-left:0}}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:340px}@media (max-device-width:480px) and (orientation:landscape){.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:200px}}.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media (min-width:768px){.navbar-static-top{border-radius:0}}.navbar-fixed-bottom,.navbar-fixed-top{position:fixed;right:0;left:0;z-index:1030}@media (min-width:768px){.navbar-fixed-bottom,.navbar-fixed-top{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.navbar-brand{float:left;height:50px;padding:15px 15px;font-size:18px;line-height:20px}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-brand>img{display:block}@media (min-width:768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;padding:9px 10px;margin-top:8px;margin-right:15px;margin-bottom:8px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:4px}.navbar-toggle:focus{outline:0}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width:768px){.navbar-toggle{display:none}}.navbar-nav{margin:7.5px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:20px}@media (max-width:767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;-webkit-box-shadow:none;box-shadow:none}.navbar-nav .open .dropdown-menu .dropdown-header,.navbar-nav .open .dropdown-menu>li>a{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:20px}.navbar-nav .open .dropdown-menu>li>a:focus,.navbar-nav .open .dropdown-menu>li>a:hover{background-image:none}}@media (min-width:768px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:15px;padding-bottom:15px}}.navbar-form{padding:10px 15px;margin-top:8px;margin-right:-15px;margin-bottom:8px;margin-left:-15px;border-top:1px solid transparent;border-bottom:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1)}@media (min-width:768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.navbar-form .form-control-static{display:inline-block}.navbar-form .input-group{display:inline-table;vertical-align:middle}.navbar-form .input-group .form-control,.navbar-form .input-group .input-group-addon,.navbar-form .input-group .input-group-btn{width:auto}.navbar-form .input-group>.form-control{width:100%}.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.navbar-form .checkbox,.navbar-form .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.navbar-form .checkbox label,.navbar-form .radio label{padding-left:0}.navbar-form .checkbox input[type=checkbox],.navbar-form .radio input[type=radio]{position:relative;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width:767px){.navbar-form .form-group{margin-bottom:5px}.navbar-form .form-group:last-child{margin-bottom:0}}@media (min-width:768px){.navbar-form{width:auto;padding-top:0;padding-bottom:0;margin-right:0;margin-left:0;border:0;-webkit-box-shadow:none;box-shadow:none}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-left-radius:0;border-top-right-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{margin-bottom:0;border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:8px;margin-bottom:8px}.navbar-btn.btn-sm{margin-top:10px;margin-bottom:10px}.navbar-btn.btn-xs{margin-top:14px;margin-bottom:14px}.navbar-text{margin-top:15px;margin-bottom:15px}@media (min-width:768px){.navbar-text{float:left;margin-right:15px;margin-left:15px}}@media (min-width:768px){.navbar-left{float:left!important}.navbar-right{float:right!important;margin-right:-15px}.navbar-right~.navbar-right{margin-right:0}}.navbar-default{background-color:#f8f8f8;border-color:#e7e7e7}.navbar-default .navbar-brand{color:#777}.navbar-default .navbar-brand:focus,.navbar-default .navbar-brand:hover{color:#5e5e5e;background-color:transparent}.navbar-default .navbar-text{color:#777}.navbar-default .navbar-nav>li>a{color:#777}.navbar-default .navbar-nav>li>a:focus,.navbar-default .navbar-nav>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:focus,.navbar-default .navbar-nav>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:focus,.navbar-default .navbar-nav>.disabled>a:hover{color:#ccc;background-color:transparent}.navbar-default .navbar-toggle{border-color:#ddd}.navbar-default .navbar-toggle:focus,.navbar-default .navbar-toggle:hover{background-color:#ddd}.navbar-default .navbar-toggle .icon-bar{background-color:#888}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:#e7e7e7}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:focus,.navbar-default .navbar-nav>.open>a:hover{color:#555;background-color:#e7e7e7}@media (max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#777}.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#ccc;background-color:transparent}}.navbar-default .navbar-link{color:#777}.navbar-default .navbar-link:hover{color:#333}.navbar-default .btn-link{color:#777}.navbar-default .btn-link:focus,.navbar-default .btn-link:hover{color:#333}.navbar-default .btn-link[disabled]:focus,.navbar-default .btn-link[disabled]:hover,fieldset[disabled] .navbar-default .btn-link:focus,fieldset[disabled] .navbar-default .btn-link:hover{color:#ccc}.navbar-inverse{background-color:#222;border-color:#080808}.navbar-inverse .navbar-brand{color:#9d9d9d}.navbar-inverse .navbar-brand:focus,.navbar-inverse .navbar-brand:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-text{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a:focus,.navbar-inverse .navbar-nav>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:focus,.navbar-inverse .navbar-nav>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:focus,.navbar-inverse .navbar-nav>.disabled>a:hover{color:#444;background-color:transparent}.navbar-inverse .navbar-toggle{border-color:#333}.navbar-inverse .navbar-toggle:focus,.navbar-inverse .navbar-toggle:hover{background-color:#333}.navbar-inverse .navbar-toggle .icon-bar{background-color:#fff}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#101010}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:focus,.navbar-inverse .navbar-nav>.open>a:hover{color:#fff;background-color:#080808}@media (max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#444;background-color:transparent}}.navbar-inverse .navbar-link{color:#9d9d9d}.navbar-inverse .navbar-link:hover{color:#fff}.navbar-inverse .btn-link{color:#9d9d9d}.navbar-inverse .btn-link:focus,.navbar-inverse .btn-link:hover{color:#fff}.navbar-inverse .btn-link[disabled]:focus,.navbar-inverse .btn-link[disabled]:hover,fieldset[disabled] .navbar-inverse .btn-link:focus,fieldset[disabled] .navbar-inverse .btn-link:hover{color:#444}.breadcrumb{padding:8px 15px;margin-bottom:20px;list-style:none;background-color:#f5f5f5;border-radius:4px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{padding:0 5px;color:#ccc;content:"/\00a0"}.breadcrumb>.active{color:#777}.pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:6px 12px;margin-left:-1px;line-height:1.42857143;color:#337ab7;text-decoration:none;background-color:#fff;border:1px solid #ddd}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-top-left-radius:4px;border-bottom-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-top-right-radius:4px;border-bottom-right-radius:4px}.pagination>li>a:focus,.pagination>li>a:hover,.pagination>li>span:focus,.pagination>li>span:hover{z-index:2;color:#23527c;background-color:#eee;border-color:#ddd}.pagination>.active>a,.pagination>.active>a:focus,.pagination>.active>a:hover,.pagination>.active>span,.pagination>.active>span:focus,.pagination>.active>span:hover{z-index:3;color:#fff;cursor:default;background-color:#337ab7;border-color:#337ab7}.pagination>.disabled>a,.pagination>.disabled>a:focus,.pagination>.disabled>a:hover,.pagination>.disabled>span,.pagination>.disabled>span:focus,.pagination>.disabled>span:hover{color:#777;cursor:not-allowed;background-color:#fff;border-color:#ddd}.pagination-lg>li>a,.pagination-lg>li>span{padding:10px 16px;font-size:18px;line-height:1.3333333}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-top-left-radius:6px;border-bottom-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-top-right-radius:6px;border-bottom-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:12px;line-height:1.5}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-top-left-radius:3px;border-bottom-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-top-right-radius:3px;border-bottom-right-radius:3px}.pager{padding-left:0;margin:20px 0;text-align:center;list-style:none}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;border-radius:15px}.pager li>a:focus,.pager li>a:hover{text-decoration:none;background-color:#eee}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:focus,.pager .disabled>a:hover,.pager .disabled>span{color:#777;cursor:not-allowed;background-color:#fff}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}a.label:focus,a.label:hover{color:#fff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#777}.label-default[href]:focus,.label-default[href]:hover{background-color:#5e5e5e}.label-primary{background-color:#337ab7}.label-primary[href]:focus,.label-primary[href]:hover{background-color:#286090}.label-success{background-color:#5cb85c}.label-success[href]:focus,.label-success[href]:hover{background-color:#449d44}.label-info{background-color:#5bc0de}.label-info[href]:focus,.label-info[href]:hover{background-color:#31b0d5}.label-warning{background-color:#f0ad4e}.label-warning[href]:focus,.label-warning[href]:hover{background-color:#ec971f}.label-danger{background-color:#d9534f}.label-danger[href]:focus,.label-danger[href]:hover{background-color:#c9302c}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:middle;background-color:#777;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.btn-group-xs>.btn .badge,.btn-xs .badge{top:0;padding:1px 5px}a.badge:focus,a.badge:hover{color:#fff;text-decoration:none;cursor:pointer}.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#337ab7;background-color:#fff}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}.nav-pills>li>a>.badge{margin-left:3px}.jumbotron{padding-top:30px;padding-bottom:30px;margin-bottom:30px;color:inherit;background-color:#eee}.jumbotron .h1,.jumbotron h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:21px;font-weight:200}.jumbotron>hr{border-top-color:#d5d5d5}.container .jumbotron,.container-fluid .jumbotron{padding-right:15px;padding-left:15px;border-radius:6px}.jumbotron .container{max-width:100%}@media screen and (min-width:768px){.jumbotron{padding-top:48px;padding-bottom:48px}.container .jumbotron,.container-fluid .jumbotron{padding-right:60px;padding-left:60px}.jumbotron .h1,.jumbotron h1{font-size:63px}}.thumbnail{display:block;padding:4px;margin-bottom:20px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:border .2s ease-in-out;-o-transition:border .2s ease-in-out;transition:border .2s ease-in-out}.thumbnail a>img,.thumbnail>img{margin-right:auto;margin-left:auto}a.thumbnail.active,a.thumbnail:focus,a.thumbnail:hover{border-color:#337ab7}.thumbnail .caption{padding:9px;color:#333}.alert{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:700}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable,.alert-dismissible{padding-right:35px}.alert-dismissable .close,.alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.alert-success hr{border-top-color:#c9e2b3}.alert-success .alert-link{color:#2b542c}.alert-info{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.alert-info hr{border-top-color:#a6e1ec}.alert-info .alert-link{color:#245269}.alert-warning{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.alert-warning hr{border-top-color:#f7e1b5}.alert-warning .alert-link{color:#66512c}.alert-danger{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.alert-danger hr{border-top-color:#e4b9c0}.alert-danger .alert-link{color:#843534}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-o-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:20px;margin-bottom:20px;overflow:hidden;background-color:#f5f5f5;border-radius:4px;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.1);box-shadow:inset 0 1px 2px rgba(0,0,0,.1)}.progress-bar{float:left;width:0;height:100%;font-size:12px;line-height:20px;color:#fff;text-align:center;background-color:#337ab7;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);-webkit-transition:width .6s ease;-o-transition:width .6s ease;transition:width .6s ease}.progress-bar-striped,.progress-striped .progress-bar{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);-webkit-background-size:40px 40px;background-size:40px 40px}.progress-bar.active,.progress.active .progress-bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar-success{background-color:#5cb85c}.progress-striped .progress-bar-success{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-info{background-color:#5bc0de}.progress-striped .progress-bar-info{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-warning{background-color:#f0ad4e}.progress-striped .progress-bar-warning{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-danger{background-color:#d9534f}.progress-striped .progress-bar-danger{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.media{margin-top:15px}.media:first-child{margin-top:0}.media,.media-body{overflow:hidden;zoom:1}.media-body{width:10000px}.media-object{display:block}.media-object.img-thumbnail{max-width:none}.media-right,.media>.pull-right{padding-left:10px}.media-left,.media>.pull-left{padding-right:10px}.media-body,.media-left,.media-right{display:table-cell;vertical-align:top}.media-middle{vertical-align:middle}.media-bottom{vertical-align:bottom}.media-heading{margin-top:0;margin-bottom:5px}.media-list{padding-left:0;list-style:none}.list-group{padding-left:0;margin-bottom:20px}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#fff;border:1px solid #ddd}.list-group-item:first-child{border-top-left-radius:4px;border-top-right-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}a.list-group-item,button.list-group-item{color:#555}a.list-group-item .list-group-item-heading,button.list-group-item .list-group-item-heading{color:#333}a.list-group-item:focus,a.list-group-item:hover,button.list-group-item:focus,button.list-group-item:hover{color:#555;text-decoration:none;background-color:#f5f5f5}button.list-group-item{width:100%;text-align:left}.list-group-item.disabled,.list-group-item.disabled:focus,.list-group-item.disabled:hover{color:#777;cursor:not-allowed;background-color:#eee}.list-group-item.disabled .list-group-item-heading,.list-group-item.disabled:focus .list-group-item-heading,.list-group-item.disabled:hover .list-group-item-heading{color:inherit}.list-group-item.disabled .list-group-item-text,.list-group-item.disabled:focus .list-group-item-text,.list-group-item.disabled:hover .list-group-item-text{color:#777}.list-group-item.active,.list-group-item.active:focus,.list-group-item.active:hover{z-index:2;color:#fff;background-color:#337ab7;border-color:#337ab7}.list-group-item.active .list-group-item-heading,.list-group-item.active .list-group-item-heading>.small,.list-group-item.active .list-group-item-heading>small,.list-group-item.active:focus .list-group-item-heading,.list-group-item.active:focus .list-group-item-heading>.small,.list-group-item.active:focus .list-group-item-heading>small,.list-group-item.active:hover .list-group-item-heading,.list-group-item.active:hover .list-group-item-heading>.small,.list-group-item.active:hover .list-group-item-heading>small{color:inherit}.list-group-item.active .list-group-item-text,.list-group-item.active:focus .list-group-item-text,.list-group-item.active:hover .list-group-item-text{color:#c7ddef}.list-group-item-success{color:#3c763d;background-color:#dff0d8}a.list-group-item-success,button.list-group-item-success{color:#3c763d}a.list-group-item-success .list-group-item-heading,button.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:focus,a.list-group-item-success:hover,button.list-group-item-success:focus,button.list-group-item-success:hover{color:#3c763d;background-color:#d0e9c6}a.list-group-item-success.active,a.list-group-item-success.active:focus,a.list-group-item-success.active:hover,button.list-group-item-success.active,button.list-group-item-success.active:focus,button.list-group-item-success.active:hover{color:#fff;background-color:#3c763d;border-color:#3c763d}.list-group-item-info{color:#31708f;background-color:#d9edf7}a.list-group-item-info,button.list-group-item-info{color:#31708f}a.list-group-item-info .list-group-item-heading,button.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:focus,a.list-group-item-info:hover,button.list-group-item-info:focus,button.list-group-item-info:hover{color:#31708f;background-color:#c4e3f3}a.list-group-item-info.active,a.list-group-item-info.active:focus,a.list-group-item-info.active:hover,button.list-group-item-info.active,button.list-group-item-info.active:focus,button.list-group-item-info.active:hover{color:#fff;background-color:#31708f;border-color:#31708f}.list-group-item-warning{color:#8a6d3b;background-color:#fcf8e3}a.list-group-item-warning,button.list-group-item-warning{color:#8a6d3b}a.list-group-item-warning .list-group-item-heading,button.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:focus,a.list-group-item-warning:hover,button.list-group-item-warning:focus,button.list-group-item-warning:hover{color:#8a6d3b;background-color:#faf2cc}a.list-group-item-warning.active,a.list-group-item-warning.active:focus,a.list-group-item-warning.active:hover,button.list-group-item-warning.active,button.list-group-item-warning.active:focus,button.list-group-item-warning.active:hover{color:#fff;background-color:#8a6d3b;border-color:#8a6d3b}.list-group-item-danger{color:#a94442;background-color:#f2dede}a.list-group-item-danger,button.list-group-item-danger{color:#a94442}a.list-group-item-danger .list-group-item-heading,button.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:focus,a.list-group-item-danger:hover,button.list-group-item-danger:focus,button.list-group-item-danger:hover{color:#a94442;background-color:#ebcccc}a.list-group-item-danger.active,a.list-group-item-danger.active:focus,a.list-group-item-danger.active:hover,button.list-group-item-danger.active,button.list-group-item-danger.active:focus,button.list-group-item-danger.active:hover{color:#fff;background-color:#a94442;border-color:#a94442}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:20px;background-color:#fff;border:1px solid transparent;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,.05);box-shadow:0 1px 1px rgba(0,0,0,.05)}.panel-body{padding:15px}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-left-radius:3px;border-top-right-radius:3px}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:16px;color:inherit}.panel-title>.small,.panel-title>.small>a,.panel-title>a,.panel-title>small,.panel-title>small>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#f5f5f5;border-top:1px solid #ddd;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.list-group,.panel>.panel-collapse>.list-group{margin-bottom:0}.panel>.list-group .list-group-item,.panel>.panel-collapse>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child,.panel>.panel-collapse>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-left-radius:3px;border-top-right-radius:3px}.panel>.list-group:last-child .list-group-item:last-child,.panel>.panel-collapse>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.panel-heading+.panel-collapse>.list-group .list-group-item:first-child{border-top-left-radius:0;border-top-right-radius:0}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.list-group+.panel-footer{border-top-width:0}.panel>.panel-collapse>.table,.panel>.table,.panel>.table-responsive>.table{margin-bottom:0}.panel>.panel-collapse>.table caption,.panel>.table caption,.panel>.table-responsive>.table caption{padding-right:15px;padding-left:15px}.panel>.table-responsive:first-child>.table:first-child,.panel>.table:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child,.panel>.table:first-child>thead:first-child>tr:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child{border-top-left-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child{border-top-right-radius:3px}.panel>.table-responsive:last-child>.table:last-child,.panel>.table:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:3px}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive,.panel>.table+.panel-body,.panel>.table-responsive+.panel-body{border-top:1px solid #ddd}.panel>.table>tbody:first-child>tr:first-child td,.panel>.table>tbody:first-child>tr:first-child th{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{margin-bottom:0;border:0}.panel-group{margin-bottom:20px}.panel-group .panel{margin-bottom:0;border-radius:4px}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse>.list-group,.panel-group .panel-heading+.panel-collapse>.panel-body{border-top:1px solid #ddd}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #ddd}.panel-default{border-color:#ddd}.panel-default>.panel-heading{color:#333;background-color:#f5f5f5;border-color:#ddd}.panel-default>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ddd}.panel-default>.panel-heading .badge{color:#f5f5f5;background-color:#333}.panel-default>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ddd}.panel-primary{border-color:#337ab7}.panel-primary>.panel-heading{color:#fff;background-color:#337ab7;border-color:#337ab7}.panel-primary>.panel-heading+.panel-collapse>.panel-body{border-top-color:#337ab7}.panel-primary>.panel-heading .badge{color:#337ab7;background-color:#fff}.panel-primary>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#337ab7}.panel-success{border-color:#d6e9c6}.panel-success>.panel-heading{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.panel-success>.panel-heading+.panel-collapse>.panel-body{border-top-color:#d6e9c6}.panel-success>.panel-heading .badge{color:#dff0d8;background-color:#3c763d}.panel-success>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#d6e9c6}.panel-info{border-color:#bce8f1}.panel-info>.panel-heading{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.panel-info>.panel-heading+.panel-collapse>.panel-body{border-top-color:#bce8f1}.panel-info>.panel-heading .badge{color:#d9edf7;background-color:#31708f}.panel-info>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#bce8f1}.panel-warning{border-color:#faebcc}.panel-warning>.panel-heading{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.panel-warning>.panel-heading+.panel-collapse>.panel-body{border-top-color:#faebcc}.panel-warning>.panel-heading .badge{color:#fcf8e3;background-color:#8a6d3b}.panel-warning>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#faebcc}.panel-danger{border-color:#ebccd1}.panel-danger>.panel-heading{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.panel-danger>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ebccd1}.panel-danger>.panel-heading .badge{color:#f2dede;background-color:#a94442}.panel-danger>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ebccd1}.embed-responsive{position:relative;display:block;height:0;padding:0;overflow:hidden}.embed-responsive .embed-responsive-item,.embed-responsive embed,.embed-responsive iframe,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-16by9{padding-bottom:56.25%}.embed-responsive-4by3{padding-bottom:75%}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);box-shadow:inset 0 1px 1px rgba(0,0,0,.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,.15)}.well-lg{padding:24px;border-radius:6px}.well-sm{padding:9px;border-radius:3px}.close{float:right;font-size:21px;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;filter:alpha(opacity=20);opacity:.2}.close:focus,.close:hover{color:#000;text-decoration:none;cursor:pointer;filter:alpha(opacity=50);opacity:.5}button.close{-webkit-appearance:none;padding:0;cursor:pointer;background:0 0;border:0}.modal-open{overflow:hidden}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;display:none;overflow:hidden;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transition:-webkit-transform .3s ease-out;-o-transition:-o-transform .3s ease-out;transition:transform .3s ease-out;-webkit-transform:translate(0,-25%);-ms-transform:translate(0,-25%);-o-transform:translate(0,-25%);transform:translate(0,-25%)}.modal.in .modal-dialog{-webkit-transform:translate(0,0);-ms-transform:translate(0,0);-o-transform:translate(0,0);transform:translate(0,0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #999;border:1px solid rgba(0,0,0,.2);border-radius:6px;outline:0;-webkit-box-shadow:0 3px 9px rgba(0,0,0,.5);box-shadow:0 3px 9px rgba(0,0,0,.5)}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{filter:alpha(opacity=0);opacity:0}.modal-backdrop.in{filter:alpha(opacity=50);opacity:.5}.modal-header{padding:15px;border-bottom:1px solid #e5e5e5}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.42857143}.modal-body{position:relative;padding:15px}.modal-footer{padding:15px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{-webkit-box-shadow:0 5px 15px rgba(0,0,0,.5);box-shadow:0 5px 15px rgba(0,0,0,.5)}.modal-sm{width:300px}}@media (min-width:992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:1070;display:block;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:12px;font-style:normal;font-weight:400;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;filter:alpha(opacity=0);opacity:0;line-break:auto}.tooltip.in{filter:alpha(opacity=90);opacity:.9}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;background-color:#000;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-left .tooltip-arrow{right:5px;bottom:0;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-right .tooltip-arrow{bottom:0;left:5px;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-left .tooltip-arrow{top:0;right:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-right .tooltip-arrow{top:0;left:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.popover{position:absolute;top:0;left:0;z-index:1060;display:none;max-width:276px;padding:1px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;font-style:normal;font-weight:400;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.2);border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,.2);box-shadow:0 5px 10px rgba(0,0,0,.2);line-break:auto}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover-title{padding:8px 14px;margin:0;font-size:14px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.popover>.arrow,.popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover>.arrow{border-width:11px}.popover>.arrow:after{content:"";border-width:10px}.popover.top>.arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#999;border-top-color:rgba(0,0,0,.25);border-bottom-width:0}.popover.top>.arrow:after{bottom:1px;margin-left:-10px;content:" ";border-top-color:#fff;border-bottom-width:0}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:#999;border-right-color:rgba(0,0,0,.25);border-left-width:0}.popover.right>.arrow:after{bottom:-10px;left:1px;content:" ";border-right-color:#fff;border-left-width:0}.popover.bottom>.arrow{top:-11px;left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,.25)}.popover.bottom>.arrow:after{top:1px;margin-left:-10px;content:" ";border-top-width:0;border-bottom-color:#fff}.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#999;border-left-color:rgba(0,0,0,.25)}.popover.left>.arrow:after{right:1px;bottom:-10px;content:" ";border-right-width:0;border-left-color:#fff}.carousel{position:relative}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.item{position:relative;display:none;-webkit-transition:.6s ease-in-out left;-o-transition:.6s ease-in-out left;transition:.6s ease-in-out left}.carousel-inner>.item>a>img,.carousel-inner>.item>img{line-height:1}@media all and (transform-3d),(-webkit-transform-3d){.carousel-inner>.item{-webkit-transition:-webkit-transform .6s ease-in-out;-o-transition:-o-transform .6s ease-in-out;transition:transform .6s ease-in-out;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-perspective:1000px;perspective:1000px}.carousel-inner>.item.active.right,.carousel-inner>.item.next{left:0;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}.carousel-inner>.item.active.left,.carousel-inner>.item.prev{left:0;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}.carousel-inner>.item.active,.carousel-inner>.item.next.left,.carousel-inner>.item.prev.right{left:0;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;bottom:0;left:0;width:15%;font-size:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6);background-color:rgba(0,0,0,0);filter:alpha(opacity=50);opacity:.5}.carousel-control.left{background-image:-webkit-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.5)),to(rgba(0,0,0,.0001)));background-image:linear-gradient(to right,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);background-repeat:repeat-x}.carousel-control.right{right:0;left:auto;background-image:-webkit-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.0001)),to(rgba(0,0,0,.5)));background-image:linear-gradient(to right,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);background-repeat:repeat-x}.carousel-control:focus,.carousel-control:hover{color:#fff;text-decoration:none;filter:alpha(opacity=90);outline:0;opacity:.9}.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{position:absolute;top:50%;z-index:5;display:inline-block;margin-top:-10px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{left:50%;margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{right:50%;margin-right:-10px}.carousel-control .icon-next,.carousel-control .icon-prev{width:20px;height:20px;font-family:serif;line-height:1}.carousel-control .icon-prev:before{content:'\2039'}.carousel-control .icon-next:before{content:'\203a'}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;padding-left:0;margin-left:-30%;text-align:center;list-style:none}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;cursor:pointer;background-color:#000\9;background-color:rgba(0,0,0,0);border:1px solid #fff;border-radius:10px}.carousel-indicators .active{width:12px;height:12px;margin:0;background-color:#fff}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width:768px){.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{width:30px;height:30px;margin-top:-10px;font-size:30px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{margin-right:-10px}.carousel-caption{right:20%;left:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.btn-group-vertical>.btn-group:after,.btn-group-vertical>.btn-group:before,.btn-toolbar:after,.btn-toolbar:before,.clearfix:after,.clearfix:before,.container-fluid:after,.container-fluid:before,.container:after,.container:before,.dl-horizontal dd:after,.dl-horizontal dd:before,.form-horizontal .form-group:after,.form-horizontal .form-group:before,.modal-footer:after,.modal-footer:before,.modal-header:after,.modal-header:before,.nav:after,.nav:before,.navbar-collapse:after,.navbar-collapse:before,.navbar-header:after,.navbar-header:before,.navbar:after,.navbar:before,.pager:after,.pager:before,.panel-body:after,.panel-body:before,.row:after,.row:before{display:table;content:" "}.btn-group-vertical>.btn-group:after,.btn-toolbar:after,.clearfix:after,.container-fluid:after,.container:after,.dl-horizontal dd:after,.form-horizontal .form-group:after,.modal-footer:after,.modal-header:after,.nav:after,.navbar-collapse:after,.navbar-header:after,.navbar:after,.pager:after,.panel-body:after,.row:after{clear:both}.center-block{display:block;margin-right:auto;margin-left:auto}.pull-right{float:right!important}.pull-left{float:left!important}.hide{display:none!important}.show{display:block!important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none!important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-lg,.visible-md,.visible-sm,.visible-xs{display:none!important}.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block{display:none!important}@media (max-width:767px){.visible-xs{display:block!important}table.visible-xs{display:table!important}tr.visible-xs{display:table-row!important}td.visible-xs,th.visible-xs{display:table-cell!important}}@media (max-width:767px){.visible-xs-block{display:block!important}}@media (max-width:767px){.visible-xs-inline{display:inline!important}}@media (max-width:767px){.visible-xs-inline-block{display:inline-block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm{display:block!important}table.visible-sm{display:table!important}tr.visible-sm{display:table-row!important}td.visible-sm,th.visible-sm{display:table-cell!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-block{display:block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline{display:inline!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline-block{display:inline-block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md{display:block!important}table.visible-md{display:table!important}tr.visible-md{display:table-row!important}td.visible-md,th.visible-md{display:table-cell!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-block{display:block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline{display:inline!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline-block{display:inline-block!important}}@media (min-width:1200px){.visible-lg{display:block!important}table.visible-lg{display:table!important}tr.visible-lg{display:table-row!important}td.visible-lg,th.visible-lg{display:table-cell!important}}@media (min-width:1200px){.visible-lg-block{display:block!important}}@media (min-width:1200px){.visible-lg-inline{display:inline!important}}@media (min-width:1200px){.visible-lg-inline-block{display:inline-block!important}}@media (max-width:767px){.hidden-xs{display:none!important}}@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none!important}}@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none!important}}@media (min-width:1200px){.hidden-lg{display:none!important}}.visible-print{display:none!important}@media print{.visible-print{display:block!important}table.visible-print{display:table!important}tr.visible-print{display:table-row!important}td.visible-print,th.visible-print{display:table-cell!important}}.visible-print-block{display:none!important}@media print{.visible-print-block{display:block!important}}.visible-print-inline{display:none!important}@media print{.visible-print-inline{display:inline!important}}.visible-print-inline-block{display:none!important}@media print{.visible-print-inline-block{display:inline-block!important}}@media print{.hidden-print{display:none!important}} -/*# sourceMappingURL=bootstrap.min.css.map */
\ No newline at end of file diff --git a/web/css/bootstrap-slate-flat-3.3.7.css b/web/css/bootstrap-slate-flat-3.3.7.css new file mode 100644 index 000000000..98a8c9fcc --- /dev/null +++ b/web/css/bootstrap-slate-flat-3.3.7.css @@ -0,0 +1,7100 @@ +/*! + * bootswatch v3.3.7 + * Homepage: http://bootswatch.com + * Copyright 2012-2016 Thomas Park + * Licensed under MIT + * Based on Bootstrap +*/ +/*! + * Bootstrap v3.3.7 (http://getbootstrap.com) + * Copyright 2011-2016 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */ +/*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */ +html { + font-family: sans-serif; + -ms-text-size-adjust: 100%; + -webkit-text-size-adjust: 100%; +} +body { + margin: 0; +} +article, +aside, +details, +figcaption, +figure, +footer, +header, +hgroup, +main, +menu, +nav, +section, +summary { + display: block; +} +audio, +canvas, +progress, +video { + display: inline-block; + vertical-align: baseline; +} +audio:not([controls]) { + display: none; + height: 0; +} +[hidden], +template { + display: none; +} +a { + background-color: transparent; +} +a:active, +a:hover { + outline: 0; +} +abbr[title] { + border-bottom: 1px dotted; +} +b, +strong { + font-weight: bold; +} +dfn { + font-style: italic; +} +h1 { + font-size: 2em; + margin: 0.67em 0; +} +mark { + background: #ff0; + color: #000; +} +small { + font-size: 80%; +} +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} +sup { + top: -0.5em; +} +sub { + bottom: -0.25em; +} +img { + border: 0; +} +svg:not(:root) { + overflow: hidden; +} +figure { + margin: 1em 40px; +} +hr { + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box; + height: 0; +} +pre { + overflow: auto; +} +code, +kbd, +pre, +samp { + font-family: monospace, monospace; + font-size: 1em; +} +button, +input, +optgroup, +select, +textarea { + color: inherit; + font: inherit; + margin: 0; +} +button { + overflow: visible; +} +button, +select { + text-transform: none; +} +button, +html input[type="button"], +input[type="reset"], +input[type="submit"] { + -webkit-appearance: button; + cursor: pointer; +} +button[disabled], +html input[disabled] { + cursor: default; +} +button::-moz-focus-inner, +input::-moz-focus-inner { + border: 0; + padding: 0; +} +input { + line-height: normal; +} +input[type="checkbox"], +input[type="radio"] { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + padding: 0; +} +input[type="number"]::-webkit-inner-spin-button, +input[type="number"]::-webkit-outer-spin-button { + height: auto; +} +input[type="search"] { + -webkit-appearance: textfield; + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box; +} +input[type="search"]::-webkit-search-cancel-button, +input[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; +} +fieldset { + border: 1px solid #c0c0c0; + margin: 0 2px; + padding: 0.35em 0.625em 0.75em; +} +legend { + border: 0; + padding: 0; +} +textarea { + overflow: auto; +} +optgroup { + font-weight: bold; +} +table { + border-collapse: collapse; + border-spacing: 0; +} +td, +th { + padding: 0; +} +/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */ +@media print { + *, + *:before, + *:after { + background: transparent !important; + color: #000 !important; + -webkit-box-shadow: none !important; + box-shadow: none !important; + text-shadow: none !important; + } + a, + a:visited { + text-decoration: underline; + } + a[href]:after { + content: " (" attr(href) ")"; + } + abbr[title]:after { + content: " (" attr(title) ")"; + } + a[href^="#"]:after, + a[href^="javascript:"]:after { + content: ""; + } + pre, + blockquote { + border: 1px solid #999; + page-break-inside: avoid; + } + thead { + display: table-header-group; + } + tr, + img { + page-break-inside: avoid; + } + img { + max-width: 100% !important; + } + p, + h2, + h3 { + orphans: 3; + widows: 3; + } + h2, + h3 { + page-break-after: avoid; + } + .navbar { + display: none; + } + .btn > .caret, + .dropup > .btn > .caret { + border-top-color: #000 !important; + } + .label { + border: 1px solid #000; + } + .table { + border-collapse: collapse !important; + } + .table td, + .table th { + background-color: #fff !important; + } + .table-bordered th, + .table-bordered td { + border: 1px solid #ddd !important; + } +} +@font-face { + font-family: 'Glyphicons Halflings'; + src: url('../fonts/glyphicons-halflings-regular.eot'); + src: url('../fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'), url('../fonts/glyphicons-halflings-regular.woff2') format('woff2'), url('../fonts/glyphicons-halflings-regular.woff') format('woff'), url('../fonts/glyphicons-halflings-regular.ttf') format('truetype'), url('../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular') format('svg'); +} +.glyphicon { + position: relative; + top: 1px; + display: inline-block; + font-family: 'Glyphicons Halflings'; + font-style: normal; + font-weight: normal; + line-height: 1; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} +.glyphicon-asterisk:before { + content: "\002a"; +} +.glyphicon-plus:before { + content: "\002b"; +} +.glyphicon-euro:before, +.glyphicon-eur:before { + content: "\20ac"; +} +.glyphicon-minus:before { + content: "\2212"; +} +.glyphicon-cloud:before { + content: "\2601"; +} +.glyphicon-envelope:before { + content: "\2709"; +} +.glyphicon-pencil:before { + content: "\270f"; +} +.glyphicon-glass:before { + content: "\e001"; +} +.glyphicon-music:before { + content: "\e002"; +} +.glyphicon-search:before { + content: "\e003"; +} +.glyphicon-heart:before { + content: "\e005"; +} +.glyphicon-star:before { + content: "\e006"; +} +.glyphicon-star-empty:before { + content: "\e007"; +} +.glyphicon-user:before { + content: "\e008"; +} +.glyphicon-film:before { + content: "\e009"; +} +.glyphicon-th-large:before { + content: "\e010"; +} +.glyphicon-th:before { + content: "\e011"; +} +.glyphicon-th-list:before { + content: "\e012"; +} +.glyphicon-ok:before { + content: "\e013"; +} +.glyphicon-remove:before { + content: "\e014"; +} +.glyphicon-zoom-in:before { + content: "\e015"; +} +.glyphicon-zoom-out:before { + content: "\e016"; +} +.glyphicon-off:before { + content: "\e017"; +} +.glyphicon-signal:before { + content: "\e018"; +} +.glyphicon-cog:before { + content: "\e019"; +} +.glyphicon-trash:before { + content: "\e020"; +} +.glyphicon-home:before { + content: "\e021"; +} +.glyphicon-file:before { + content: "\e022"; +} +.glyphicon-time:before { + content: "\e023"; +} +.glyphicon-road:before { + content: "\e024"; +} +.glyphicon-download-alt:before { + content: "\e025"; +} +.glyphicon-download:before { + content: "\e026"; +} +.glyphicon-upload:before { + content: "\e027"; +} +.glyphicon-inbox:before { + content: "\e028"; +} +.glyphicon-play-circle:before { + content: "\e029"; +} +.glyphicon-repeat:before { + content: "\e030"; +} +.glyphicon-refresh:before { + content: "\e031"; +} +.glyphicon-list-alt:before { + content: "\e032"; +} +.glyphicon-lock:before { + content: "\e033"; +} +.glyphicon-flag:before { + content: "\e034"; +} +.glyphicon-headphones:before { + content: "\e035"; +} +.glyphicon-volume-off:before { + content: "\e036"; +} +.glyphicon-volume-down:before { + content: "\e037"; +} +.glyphicon-volume-up:before { + content: "\e038"; +} +.glyphicon-qrcode:before { + content: "\e039"; +} +.glyphicon-barcode:before { + content: "\e040"; +} +.glyphicon-tag:before { + content: "\e041"; +} +.glyphicon-tags:before { + content: "\e042"; +} +.glyphicon-book:before { + content: "\e043"; +} +.glyphicon-bookmark:before { + content: "\e044"; +} +.glyphicon-print:before { + content: "\e045"; +} +.glyphicon-camera:before { + content: "\e046"; +} +.glyphicon-font:before { + content: "\e047"; +} +.glyphicon-bold:before { + content: "\e048"; +} +.glyphicon-italic:before { + content: "\e049"; +} +.glyphicon-text-height:before { + content: "\e050"; +} +.glyphicon-text-width:before { + content: "\e051"; +} +.glyphicon-align-left:before { + content: "\e052"; +} +.glyphicon-align-center:before { + content: "\e053"; +} +.glyphicon-align-right:before { + content: "\e054"; +} +.glyphicon-align-justify:before { + content: "\e055"; +} +.glyphicon-list:before { + content: "\e056"; +} +.glyphicon-indent-left:before { + content: "\e057"; +} +.glyphicon-indent-right:before { + content: "\e058"; +} +.glyphicon-facetime-video:before { + content: "\e059"; +} +.glyphicon-picture:before { + content: "\e060"; +} +.glyphicon-map-marker:before { + content: "\e062"; +} +.glyphicon-adjust:before { + content: "\e063"; +} +.glyphicon-tint:before { + content: "\e064"; +} +.glyphicon-edit:before { + content: "\e065"; +} +.glyphicon-share:before { + content: "\e066"; +} +.glyphicon-check:before { + content: "\e067"; +} +.glyphicon-move:before { + content: "\e068"; +} +.glyphicon-step-backward:before { + content: "\e069"; +} +.glyphicon-fast-backward:before { + content: "\e070"; +} +.glyphicon-backward:before { + content: "\e071"; +} +.glyphicon-play:before { + content: "\e072"; +} +.glyphicon-pause:before { + content: "\e073"; +} +.glyphicon-stop:before { + content: "\e074"; +} +.glyphicon-forward:before { + content: "\e075"; +} +.glyphicon-fast-forward:before { + content: "\e076"; +} +.glyphicon-step-forward:before { + content: "\e077"; +} +.glyphicon-eject:before { + content: "\e078"; +} +.glyphicon-chevron-left:before { + content: "\e079"; +} +.glyphicon-chevron-right:before { + content: "\e080"; +} +.glyphicon-plus-sign:before { + content: "\e081"; +} +.glyphicon-minus-sign:before { + content: "\e082"; +} +.glyphicon-remove-sign:before { + content: "\e083"; +} +.glyphicon-ok-sign:before { + content: "\e084"; +} +.glyphicon-question-sign:before { + content: "\e085"; +} +.glyphicon-info-sign:before { + content: "\e086"; +} +.glyphicon-screenshot:before { + content: "\e087"; +} +.glyphicon-remove-circle:before { + content: "\e088"; +} +.glyphicon-ok-circle:before { + content: "\e089"; +} +.glyphicon-ban-circle:before { + content: "\e090"; +} +.glyphicon-arrow-left:before { + content: "\e091"; +} +.glyphicon-arrow-right:before { + content: "\e092"; +} +.glyphicon-arrow-up:before { + content: "\e093"; +} +.glyphicon-arrow-down:before { + content: "\e094"; +} +.glyphicon-share-alt:before { + content: "\e095"; +} +.glyphicon-resize-full:before { + content: "\e096"; +} +.glyphicon-resize-small:before { + content: "\e097"; +} +.glyphicon-exclamation-sign:before { + content: "\e101"; +} +.glyphicon-gift:before { + content: "\e102"; +} +.glyphicon-leaf:before { + content: "\e103"; +} +.glyphicon-fire:before { + content: "\e104"; +} +.glyphicon-eye-open:before { + content: "\e105"; +} +.glyphicon-eye-close:before { + content: "\e106"; +} +.glyphicon-warning-sign:before { + content: "\e107"; +} +.glyphicon-plane:before { + content: "\e108"; +} +.glyphicon-calendar:before { + content: "\e109"; +} +.glyphicon-random:before { + content: "\e110"; +} +.glyphicon-comment:before { + content: "\e111"; +} +.glyphicon-magnet:before { + content: "\e112"; +} +.glyphicon-chevron-up:before { + content: "\e113"; +} +.glyphicon-chevron-down:before { + content: "\e114"; +} +.glyphicon-retweet:before { + content: "\e115"; +} +.glyphicon-shopping-cart:before { + content: "\e116"; +} +.glyphicon-folder-close:before { + content: "\e117"; +} +.glyphicon-folder-open:before { + content: "\e118"; +} +.glyphicon-resize-vertical:before { + content: "\e119"; +} +.glyphicon-resize-horizontal:before { + content: "\e120"; +} +.glyphicon-hdd:before { + content: "\e121"; +} +.glyphicon-bullhorn:before { + content: "\e122"; +} +.glyphicon-bell:before { + content: "\e123"; +} +.glyphicon-certificate:before { + content: "\e124"; +} +.glyphicon-thumbs-up:before { + content: "\e125"; +} +.glyphicon-thumbs-down:before { + content: "\e126"; +} +.glyphicon-hand-right:before { + content: "\e127"; +} +.glyphicon-hand-left:before { + content: "\e128"; +} +.glyphicon-hand-up:before { + content: "\e129"; +} +.glyphicon-hand-down:before { + content: "\e130"; +} +.glyphicon-circle-arrow-right:before { + content: "\e131"; +} +.glyphicon-circle-arrow-left:before { + content: "\e132"; +} +.glyphicon-circle-arrow-up:before { + content: "\e133"; +} +.glyphicon-circle-arrow-down:before { + content: "\e134"; +} +.glyphicon-globe:before { + content: "\e135"; +} +.glyphicon-wrench:before { + content: "\e136"; +} +.glyphicon-tasks:before { + content: "\e137"; +} +.glyphicon-filter:before { + content: "\e138"; +} +.glyphicon-briefcase:before { + content: "\e139"; +} +.glyphicon-fullscreen:before { + content: "\e140"; +} +.glyphicon-dashboard:before { + content: "\e141"; +} +.glyphicon-paperclip:before { + content: "\e142"; +} +.glyphicon-heart-empty:before { + content: "\e143"; +} +.glyphicon-link:before { + content: "\e144"; +} +.glyphicon-phone:before { + content: "\e145"; +} +.glyphicon-pushpin:before { + content: "\e146"; +} +.glyphicon-usd:before { + content: "\e148"; +} +.glyphicon-gbp:before { + content: "\e149"; +} +.glyphicon-sort:before { + content: "\e150"; +} +.glyphicon-sort-by-alphabet:before { + content: "\e151"; +} +.glyphicon-sort-by-alphabet-alt:before { + content: "\e152"; +} +.glyphicon-sort-by-order:before { + content: "\e153"; +} +.glyphicon-sort-by-order-alt:before { + content: "\e154"; +} +.glyphicon-sort-by-attributes:before { + content: "\e155"; +} +.glyphicon-sort-by-attributes-alt:before { + content: "\e156"; +} +.glyphicon-unchecked:before { + content: "\e157"; +} +.glyphicon-expand:before { + content: "\e158"; +} +.glyphicon-collapse-down:before { + content: "\e159"; +} +.glyphicon-collapse-up:before { + content: "\e160"; +} +.glyphicon-log-in:before { + content: "\e161"; +} +.glyphicon-flash:before { + content: "\e162"; +} +.glyphicon-log-out:before { + content: "\e163"; +} +.glyphicon-new-window:before { + content: "\e164"; +} +.glyphicon-record:before { + content: "\e165"; +} +.glyphicon-save:before { + content: "\e166"; +} +.glyphicon-open:before { + content: "\e167"; +} +.glyphicon-saved:before { + content: "\e168"; +} +.glyphicon-import:before { + content: "\e169"; +} +.glyphicon-export:before { + content: "\e170"; +} +.glyphicon-send:before { + content: "\e171"; +} +.glyphicon-floppy-disk:before { + content: "\e172"; +} +.glyphicon-floppy-saved:before { + content: "\e173"; +} +.glyphicon-floppy-remove:before { + content: "\e174"; +} +.glyphicon-floppy-save:before { + content: "\e175"; +} +.glyphicon-floppy-open:before { + content: "\e176"; +} +.glyphicon-credit-card:before { + content: "\e177"; +} +.glyphicon-transfer:before { + content: "\e178"; +} +.glyphicon-cutlery:before { + content: "\e179"; +} +.glyphicon-header:before { + content: "\e180"; +} +.glyphicon-compressed:before { + content: "\e181"; +} +.glyphicon-earphone:before { + content: "\e182"; +} +.glyphicon-phone-alt:before { + content: "\e183"; +} +.glyphicon-tower:before { + content: "\e184"; +} +.glyphicon-stats:before { + content: "\e185"; +} +.glyphicon-sd-video:before { + content: "\e186"; +} +.glyphicon-hd-video:before { + content: "\e187"; +} +.glyphicon-subtitles:before { + content: "\e188"; +} +.glyphicon-sound-stereo:before { + content: "\e189"; +} +.glyphicon-sound-dolby:before { + content: "\e190"; +} +.glyphicon-sound-5-1:before { + content: "\e191"; +} +.glyphicon-sound-6-1:before { + content: "\e192"; +} +.glyphicon-sound-7-1:before { + content: "\e193"; +} +.glyphicon-copyright-mark:before { + content: "\e194"; +} +.glyphicon-registration-mark:before { + content: "\e195"; +} +.glyphicon-cloud-download:before { + content: "\e197"; +} +.glyphicon-cloud-upload:before { + content: "\e198"; +} +.glyphicon-tree-conifer:before { + content: "\e199"; +} +.glyphicon-tree-deciduous:before { + content: "\e200"; +} +.glyphicon-cd:before { + content: "\e201"; +} +.glyphicon-save-file:before { + content: "\e202"; +} +.glyphicon-open-file:before { + content: "\e203"; +} +.glyphicon-level-up:before { + content: "\e204"; +} +.glyphicon-copy:before { + content: "\e205"; +} +.glyphicon-paste:before { + content: "\e206"; +} +.glyphicon-alert:before { + content: "\e209"; +} +.glyphicon-equalizer:before { + content: "\e210"; +} +.glyphicon-king:before { + content: "\e211"; +} +.glyphicon-queen:before { + content: "\e212"; +} +.glyphicon-pawn:before { + content: "\e213"; +} +.glyphicon-bishop:before { + content: "\e214"; +} +.glyphicon-knight:before { + content: "\e215"; +} +.glyphicon-baby-formula:before { + content: "\e216"; +} +.glyphicon-tent:before { + content: "\26fa"; +} +.glyphicon-blackboard:before { + content: "\e218"; +} +.glyphicon-bed:before { + content: "\e219"; +} +.glyphicon-apple:before { + content: "\f8ff"; +} +.glyphicon-erase:before { + content: "\e221"; +} +.glyphicon-hourglass:before { + content: "\231b"; +} +.glyphicon-lamp:before { + content: "\e223"; +} +.glyphicon-duplicate:before { + content: "\e224"; +} +.glyphicon-piggy-bank:before { + content: "\e225"; +} +.glyphicon-scissors:before { + content: "\e226"; +} +.glyphicon-bitcoin:before { + content: "\e227"; +} +.glyphicon-btc:before { + content: "\e227"; +} +.glyphicon-xbt:before { + content: "\e227"; +} +.glyphicon-yen:before { + content: "\00a5"; +} +.glyphicon-jpy:before { + content: "\00a5"; +} +.glyphicon-ruble:before { + content: "\20bd"; +} +.glyphicon-rub:before { + content: "\20bd"; +} +.glyphicon-scale:before { + content: "\e230"; +} +.glyphicon-ice-lolly:before { + content: "\e231"; +} +.glyphicon-ice-lolly-tasted:before { + content: "\e232"; +} +.glyphicon-education:before { + content: "\e233"; +} +.glyphicon-option-horizontal:before { + content: "\e234"; +} +.glyphicon-option-vertical:before { + content: "\e235"; +} +.glyphicon-menu-hamburger:before { + content: "\e236"; +} +.glyphicon-modal-window:before { + content: "\e237"; +} +.glyphicon-oil:before { + content: "\e238"; +} +.glyphicon-grain:before { + content: "\e239"; +} +.glyphicon-sunglasses:before { + content: "\e240"; +} +.glyphicon-text-size:before { + content: "\e241"; +} +.glyphicon-text-color:before { + content: "\e242"; +} +.glyphicon-text-background:before { + content: "\e243"; +} +.glyphicon-object-align-top:before { + content: "\e244"; +} +.glyphicon-object-align-bottom:before { + content: "\e245"; +} +.glyphicon-object-align-horizontal:before { + content: "\e246"; +} +.glyphicon-object-align-left:before { + content: "\e247"; +} +.glyphicon-object-align-vertical:before { + content: "\e248"; +} +.glyphicon-object-align-right:before { + content: "\e249"; +} +.glyphicon-triangle-right:before { + content: "\e250"; +} +.glyphicon-triangle-left:before { + content: "\e251"; +} +.glyphicon-triangle-bottom:before { + content: "\e252"; +} +.glyphicon-triangle-top:before { + content: "\e253"; +} +.glyphicon-console:before { + content: "\e254"; +} +.glyphicon-superscript:before { + content: "\e255"; +} +.glyphicon-subscript:before { + content: "\e256"; +} +.glyphicon-menu-left:before { + content: "\e257"; +} +.glyphicon-menu-right:before { + content: "\e258"; +} +.glyphicon-menu-down:before { + content: "\e259"; +} +.glyphicon-menu-up:before { + content: "\e260"; +} +* { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +*:before, +*:after { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +html { + font-size: 10px; + -webkit-tap-highlight-color: rgba(0, 0, 0, 0); +} +body { + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 14px; + line-height: 1.42857143; + color: #c8c8c8; + background-color: #272b30; +} +input, +button, +select, +textarea { + font-family: inherit; + font-size: inherit; + line-height: inherit; +} +a { + color: #ffffff; + text-decoration: none; +} +a:hover, +a:focus { + color: #ffffff; + text-decoration: underline; +} +a:focus { + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} +figure { + margin: 0; +} +img { + vertical-align: middle; +} +.img-responsive, +.thumbnail > img, +.thumbnail a > img, +.carousel-inner > .item > img, +.carousel-inner > .item > a > img { + display: block; + max-width: 100%; + height: auto; +} +.img-rounded { + border-radius: 6px; +} +.img-thumbnail { + padding: 4px; + line-height: 1.42857143; + background-color: #1c1e22; + border: 1px solid #0c0d0e; + border-radius: 4px; + -webkit-transition: all 0.2s ease-in-out; + -o-transition: all 0.2s ease-in-out; + transition: all 0.2s ease-in-out; + display: inline-block; + max-width: 100%; + height: auto; +} +.img-circle { + border-radius: 50%; +} +hr { + margin-top: 20px; + margin-bottom: 20px; + border: 0; + border-top: 1px solid #1c1e22; +} +.sr-only { + position: absolute; + width: 1px; + height: 1px; + margin: -1px; + padding: 0; + overflow: hidden; + clip: rect(0, 0, 0, 0); + border: 0; +} +.sr-only-focusable:active, +.sr-only-focusable:focus { + position: static; + width: auto; + height: auto; + margin: 0; + overflow: visible; + clip: auto; +} +[role="button"] { + cursor: pointer; +} +h1, +h2, +h3, +h4, +h5, +h6, +.h1, +.h2, +.h3, +.h4, +.h5, +.h6 { + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-weight: 500; + line-height: 1.1; + color: inherit; +} +h1 small, +h2 small, +h3 small, +h4 small, +h5 small, +h6 small, +.h1 small, +.h2 small, +.h3 small, +.h4 small, +.h5 small, +.h6 small, +h1 .small, +h2 .small, +h3 .small, +h4 .small, +h5 .small, +h6 .small, +.h1 .small, +.h2 .small, +.h3 .small, +.h4 .small, +.h5 .small, +.h6 .small { + font-weight: normal; + line-height: 1; + color: #7a8288; +} +h1, +.h1, +h2, +.h2, +h3, +.h3 { + margin-top: 20px; + margin-bottom: 10px; +} +h1 small, +.h1 small, +h2 small, +.h2 small, +h3 small, +.h3 small, +h1 .small, +.h1 .small, +h2 .small, +.h2 .small, +h3 .small, +.h3 .small { + font-size: 65%; +} +h4, +.h4, +h5, +.h5, +h6, +.h6 { + margin-top: 10px; + margin-bottom: 10px; +} +h4 small, +.h4 small, +h5 small, +.h5 small, +h6 small, +.h6 small, +h4 .small, +.h4 .small, +h5 .small, +.h5 .small, +h6 .small, +.h6 .small { + font-size: 75%; +} +h1, +.h1 { + font-size: 36px; +} +h2, +.h2 { + font-size: 30px; +} +h3, +.h3 { + font-size: 24px; +} +h4, +.h4 { + font-size: 18px; +} +h5, +.h5 { + font-size: 14px; +} +h6, +.h6 { + font-size: 12px; +} +p { + margin: 0 0 10px; +} +.lead { + margin-bottom: 20px; + font-size: 16px; + font-weight: 300; + line-height: 1.4; +} +@media (min-width: 768px) { + .lead { + font-size: 21px; + } +} +small, +.small { + font-size: 85%; +} +mark, +.mark { + background-color: #f89406; + padding: .2em; +} +.text-left { + text-align: left; +} +.text-right { + text-align: right; +} +.text-center { + text-align: center; +} +.text-justify { + text-align: justify; +} +.text-nowrap { + white-space: nowrap; +} +.text-lowercase { + text-transform: lowercase; +} +.text-uppercase { + text-transform: uppercase; +} +.text-capitalize { + text-transform: capitalize; +} +.text-muted { + color: #7a8288; +} +.text-primary { + color: #7a8288; +} +a.text-primary:hover, +a.text-primary:focus { + color: #62686d; +} +.text-success { + color: #ffffff; +} +a.text-success:hover, +a.text-success:focus { + color: #e6e6e6; +} +.text-info { + color: #ffffff; +} +a.text-info:hover, +a.text-info:focus { + color: #e6e6e6; +} +.text-warning { + color: #ffffff; +} +a.text-warning:hover, +a.text-warning:focus { + color: #e6e6e6; +} +.text-danger { + color: #ffffff; +} +a.text-danger:hover, +a.text-danger:focus { + color: #e6e6e6; +} +.bg-primary { + color: #fff; + background-color: #7a8288; +} +a.bg-primary:hover, +a.bg-primary:focus { + background-color: #62686d; +} +.bg-success { + background-color: #62c462; +} +a.bg-success:hover, +a.bg-success:focus { + background-color: #42b142; +} +.bg-info { + background-color: #5bc0de; +} +a.bg-info:hover, +a.bg-info:focus { + background-color: #31b0d5; +} +.bg-warning { + background-color: #f89406; +} +a.bg-warning:hover, +a.bg-warning:focus { + background-color: #c67605; +} +.bg-danger { + background-color: #ee5f5b; +} +a.bg-danger:hover, +a.bg-danger:focus { + background-color: #e9322d; +} +.page-header { + padding-bottom: 9px; + margin: 40px 0 20px; + border-bottom: 1px solid #1c1e22; +} +ul, +ol { + margin-top: 0; + margin-bottom: 10px; +} +ul ul, +ol ul, +ul ol, +ol ol { + margin-bottom: 0; +} +.list-unstyled { + padding-left: 0; + list-style: none; +} +.list-inline { + padding-left: 0; + list-style: none; + margin-left: -5px; +} +.list-inline > li { + display: inline-block; + padding-left: 5px; + padding-right: 5px; +} +dl { + margin-top: 0; + margin-bottom: 20px; +} +dt, +dd { + line-height: 1.42857143; +} +dt { + font-weight: bold; +} +dd { + margin-left: 0; +} +@media (min-width: 768px) { + .dl-horizontal dt { + float: left; + width: 160px; + clear: left; + text-align: right; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + .dl-horizontal dd { + margin-left: 180px; + } +} +abbr[title], +abbr[data-original-title] { + cursor: help; + border-bottom: 1px dotted #7a8288; +} +.initialism { + font-size: 90%; + text-transform: uppercase; +} +blockquote { + padding: 10px 20px; + margin: 0 0 20px; + font-size: 17.5px; + border-left: 5px solid #7a8288; +} +blockquote p:last-child, +blockquote ul:last-child, +blockquote ol:last-child { + margin-bottom: 0; +} +blockquote footer, +blockquote small, +blockquote .small { + display: block; + font-size: 80%; + line-height: 1.42857143; + color: #7a8288; +} +blockquote footer:before, +blockquote small:before, +blockquote .small:before { + content: '\2014 \00A0'; +} +.blockquote-reverse, +blockquote.pull-right { + padding-right: 15px; + padding-left: 0; + border-right: 5px solid #7a8288; + border-left: 0; + text-align: right; +} +.blockquote-reverse footer:before, +blockquote.pull-right footer:before, +.blockquote-reverse small:before, +blockquote.pull-right small:before, +.blockquote-reverse .small:before, +blockquote.pull-right .small:before { + content: ''; +} +.blockquote-reverse footer:after, +blockquote.pull-right footer:after, +.blockquote-reverse small:after, +blockquote.pull-right small:after, +.blockquote-reverse .small:after, +blockquote.pull-right .small:after { + content: '\00A0 \2014'; +} +address { + margin-bottom: 20px; + font-style: normal; + line-height: 1.42857143; +} +code, +kbd, +pre, +samp { + font-family: Menlo, Monaco, Consolas, "Courier New", monospace; +} +code { + padding: 2px 4px; + font-size: 90%; + color: #c7254e; + background-color: #f9f2f4; + border-radius: 4px; +} +kbd { + padding: 2px 4px; + font-size: 90%; + color: #ffffff; + background-color: #333333; + border-radius: 3px; + -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.25); + box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.25); +} +kbd kbd { + padding: 0; + font-size: 100%; + font-weight: bold; + -webkit-box-shadow: none; + box-shadow: none; +} +pre { + display: block; + padding: 9.5px; + margin: 0 0 10px; + font-size: 13px; + line-height: 1.42857143; + word-break: break-all; + word-wrap: break-word; + color: #3a3f44; + background-color: #f5f5f5; + border: 1px solid #cccccc; + border-radius: 4px; +} +pre code { + padding: 0; + font-size: inherit; + color: inherit; + white-space: pre-wrap; + background-color: transparent; + border-radius: 0; +} +.pre-scrollable { + max-height: 340px; + overflow-y: scroll; +} +.container { + margin-right: auto; + margin-left: auto; + padding-left: 15px; + padding-right: 15px; +} +@media (min-width: 768px) { + .container { + width: 750px; + } +} +@media (min-width: 992px) { + .container { + width: 970px; + } +} +@media (min-width: 1200px) { + .container { + width: 1170px; + } +} +.container-fluid { + margin-right: auto; + margin-left: auto; + padding-left: 15px; + padding-right: 15px; +} +.row { + margin-left: -15px; + margin-right: -15px; +} +.col-xs-1, .col-sm-1, .col-md-1, .col-lg-1, .col-xs-2, .col-sm-2, .col-md-2, .col-lg-2, .col-xs-3, .col-sm-3, .col-md-3, .col-lg-3, .col-xs-4, .col-sm-4, .col-md-4, .col-lg-4, .col-xs-5, .col-sm-5, .col-md-5, .col-lg-5, .col-xs-6, .col-sm-6, .col-md-6, .col-lg-6, .col-xs-7, .col-sm-7, .col-md-7, .col-lg-7, .col-xs-8, .col-sm-8, .col-md-8, .col-lg-8, .col-xs-9, .col-sm-9, .col-md-9, .col-lg-9, .col-xs-10, .col-sm-10, .col-md-10, .col-lg-10, .col-xs-11, .col-sm-11, .col-md-11, .col-lg-11, .col-xs-12, .col-sm-12, .col-md-12, .col-lg-12 { + position: relative; + min-height: 1px; + padding-left: 15px; + padding-right: 15px; +} +.col-xs-1, .col-xs-2, .col-xs-3, .col-xs-4, .col-xs-5, .col-xs-6, .col-xs-7, .col-xs-8, .col-xs-9, .col-xs-10, .col-xs-11, .col-xs-12 { + float: left; +} +.col-xs-12 { + width: 100%; +} +.col-xs-11 { + width: 91.66666667%; +} +.col-xs-10 { + width: 83.33333333%; +} +.col-xs-9 { + width: 75%; +} +.col-xs-8 { + width: 66.66666667%; +} +.col-xs-7 { + width: 58.33333333%; +} +.col-xs-6 { + width: 50%; +} +.col-xs-5 { + width: 41.66666667%; +} +.col-xs-4 { + width: 33.33333333%; +} +.col-xs-3 { + width: 25%; +} +.col-xs-2 { + width: 16.66666667%; +} +.col-xs-1 { + width: 8.33333333%; +} +.col-xs-pull-12 { + right: 100%; +} +.col-xs-pull-11 { + right: 91.66666667%; +} +.col-xs-pull-10 { + right: 83.33333333%; +} +.col-xs-pull-9 { + right: 75%; +} +.col-xs-pull-8 { + right: 66.66666667%; +} +.col-xs-pull-7 { + right: 58.33333333%; +} +.col-xs-pull-6 { + right: 50%; +} +.col-xs-pull-5 { + right: 41.66666667%; +} +.col-xs-pull-4 { + right: 33.33333333%; +} +.col-xs-pull-3 { + right: 25%; +} +.col-xs-pull-2 { + right: 16.66666667%; +} +.col-xs-pull-1 { + right: 8.33333333%; +} +.col-xs-pull-0 { + right: auto; +} +.col-xs-push-12 { + left: 100%; +} +.col-xs-push-11 { + left: 91.66666667%; +} +.col-xs-push-10 { + left: 83.33333333%; +} +.col-xs-push-9 { + left: 75%; +} +.col-xs-push-8 { + left: 66.66666667%; +} +.col-xs-push-7 { + left: 58.33333333%; +} +.col-xs-push-6 { + left: 50%; +} +.col-xs-push-5 { + left: 41.66666667%; +} +.col-xs-push-4 { + left: 33.33333333%; +} +.col-xs-push-3 { + left: 25%; +} +.col-xs-push-2 { + left: 16.66666667%; +} +.col-xs-push-1 { + left: 8.33333333%; +} +.col-xs-push-0 { + left: auto; +} +.col-xs-offset-12 { + margin-left: 100%; +} +.col-xs-offset-11 { + margin-left: 91.66666667%; +} +.col-xs-offset-10 { + margin-left: 83.33333333%; +} +.col-xs-offset-9 { + margin-left: 75%; +} +.col-xs-offset-8 { + margin-left: 66.66666667%; +} +.col-xs-offset-7 { + margin-left: 58.33333333%; +} +.col-xs-offset-6 { + margin-left: 50%; +} +.col-xs-offset-5 { + margin-left: 41.66666667%; +} +.col-xs-offset-4 { + margin-left: 33.33333333%; +} +.col-xs-offset-3 { + margin-left: 25%; +} +.col-xs-offset-2 { + margin-left: 16.66666667%; +} +.col-xs-offset-1 { + margin-left: 8.33333333%; +} +.col-xs-offset-0 { + margin-left: 0%; +} +@media (min-width: 768px) { + .col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12 { + float: left; + } + .col-sm-12 { + width: 100%; + } + .col-sm-11 { + width: 91.66666667%; + } + .col-sm-10 { + width: 83.33333333%; + } + .col-sm-9 { + width: 75%; + } + .col-sm-8 { + width: 66.66666667%; + } + .col-sm-7 { + width: 58.33333333%; + } + .col-sm-6 { + width: 50%; + } + .col-sm-5 { + width: 41.66666667%; + } + .col-sm-4 { + width: 33.33333333%; + } + .col-sm-3 { + width: 25%; + } + .col-sm-2 { + width: 16.66666667%; + } + .col-sm-1 { + width: 8.33333333%; + } + .col-sm-pull-12 { + right: 100%; + } + .col-sm-pull-11 { + right: 91.66666667%; + } + .col-sm-pull-10 { + right: 83.33333333%; + } + .col-sm-pull-9 { + right: 75%; + } + .col-sm-pull-8 { + right: 66.66666667%; + } + .col-sm-pull-7 { + right: 58.33333333%; + } + .col-sm-pull-6 { + right: 50%; + } + .col-sm-pull-5 { + right: 41.66666667%; + } + .col-sm-pull-4 { + right: 33.33333333%; + } + .col-sm-pull-3 { + right: 25%; + } + .col-sm-pull-2 { + right: 16.66666667%; + } + .col-sm-pull-1 { + right: 8.33333333%; + } + .col-sm-pull-0 { + right: auto; + } + .col-sm-push-12 { + left: 100%; + } + .col-sm-push-11 { + left: 91.66666667%; + } + .col-sm-push-10 { + left: 83.33333333%; + } + .col-sm-push-9 { + left: 75%; + } + .col-sm-push-8 { + left: 66.66666667%; + } + .col-sm-push-7 { + left: 58.33333333%; + } + .col-sm-push-6 { + left: 50%; + } + .col-sm-push-5 { + left: 41.66666667%; + } + .col-sm-push-4 { + left: 33.33333333%; + } + .col-sm-push-3 { + left: 25%; + } + .col-sm-push-2 { + left: 16.66666667%; + } + .col-sm-push-1 { + left: 8.33333333%; + } + .col-sm-push-0 { + left: auto; + } + .col-sm-offset-12 { + margin-left: 100%; + } + .col-sm-offset-11 { + margin-left: 91.66666667%; + } + .col-sm-offset-10 { + margin-left: 83.33333333%; + } + .col-sm-offset-9 { + margin-left: 75%; + } + .col-sm-offset-8 { + margin-left: 66.66666667%; + } + .col-sm-offset-7 { + margin-left: 58.33333333%; + } + .col-sm-offset-6 { + margin-left: 50%; + } + .col-sm-offset-5 { + margin-left: 41.66666667%; + } + .col-sm-offset-4 { + margin-left: 33.33333333%; + } + .col-sm-offset-3 { + margin-left: 25%; + } + .col-sm-offset-2 { + margin-left: 16.66666667%; + } + .col-sm-offset-1 { + margin-left: 8.33333333%; + } + .col-sm-offset-0 { + margin-left: 0%; + } +} +@media (min-width: 992px) { + .col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12 { + float: left; + } + .col-md-12 { + width: 100%; + } + .col-md-11 { + width: 91.66666667%; + } + .col-md-10 { + width: 83.33333333%; + } + .col-md-9 { + width: 75%; + } + .col-md-8 { + width: 66.66666667%; + } + .col-md-7 { + width: 58.33333333%; + } + .col-md-6 { + width: 50%; + } + .col-md-5 { + width: 41.66666667%; + } + .col-md-4 { + width: 33.33333333%; + } + .col-md-3 { + width: 25%; + } + .col-md-2 { + width: 16.66666667%; + } + .col-md-1 { + width: 8.33333333%; + } + .col-md-pull-12 { + right: 100%; + } + .col-md-pull-11 { + right: 91.66666667%; + } + .col-md-pull-10 { + right: 83.33333333%; + } + .col-md-pull-9 { + right: 75%; + } + .col-md-pull-8 { + right: 66.66666667%; + } + .col-md-pull-7 { + right: 58.33333333%; + } + .col-md-pull-6 { + right: 50%; + } + .col-md-pull-5 { + right: 41.66666667%; + } + .col-md-pull-4 { + right: 33.33333333%; + } + .col-md-pull-3 { + right: 25%; + } + .col-md-pull-2 { + right: 16.66666667%; + } + .col-md-pull-1 { + right: 8.33333333%; + } + .col-md-pull-0 { + right: auto; + } + .col-md-push-12 { + left: 100%; + } + .col-md-push-11 { + left: 91.66666667%; + } + .col-md-push-10 { + left: 83.33333333%; + } + .col-md-push-9 { + left: 75%; + } + .col-md-push-8 { + left: 66.66666667%; + } + .col-md-push-7 { + left: 58.33333333%; + } + .col-md-push-6 { + left: 50%; + } + .col-md-push-5 { + left: 41.66666667%; + } + .col-md-push-4 { + left: 33.33333333%; + } + .col-md-push-3 { + left: 25%; + } + .col-md-push-2 { + left: 16.66666667%; + } + .col-md-push-1 { + left: 8.33333333%; + } + .col-md-push-0 { + left: auto; + } + .col-md-offset-12 { + margin-left: 100%; + } + .col-md-offset-11 { + margin-left: 91.66666667%; + } + .col-md-offset-10 { + margin-left: 83.33333333%; + } + .col-md-offset-9 { + margin-left: 75%; + } + .col-md-offset-8 { + margin-left: 66.66666667%; + } + .col-md-offset-7 { + margin-left: 58.33333333%; + } + .col-md-offset-6 { + margin-left: 50%; + } + .col-md-offset-5 { + margin-left: 41.66666667%; + } + .col-md-offset-4 { + margin-left: 33.33333333%; + } + .col-md-offset-3 { + margin-left: 25%; + } + .col-md-offset-2 { + margin-left: 16.66666667%; + } + .col-md-offset-1 { + margin-left: 8.33333333%; + } + .col-md-offset-0 { + margin-left: 0%; + } +} +@media (min-width: 1200px) { + .col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12 { + float: left; + } + .col-lg-12 { + width: 100%; + } + .col-lg-11 { + width: 91.66666667%; + } + .col-lg-10 { + width: 83.33333333%; + } + .col-lg-9 { + width: 75%; + } + .col-lg-8 { + width: 66.66666667%; + } + .col-lg-7 { + width: 58.33333333%; + } + .col-lg-6 { + width: 50%; + } + .col-lg-5 { + width: 41.66666667%; + } + .col-lg-4 { + width: 33.33333333%; + } + .col-lg-3 { + width: 25%; + } + .col-lg-2 { + width: 16.66666667%; + } + .col-lg-1 { + width: 8.33333333%; + } + .col-lg-pull-12 { + right: 100%; + } + .col-lg-pull-11 { + right: 91.66666667%; + } + .col-lg-pull-10 { + right: 83.33333333%; + } + .col-lg-pull-9 { + right: 75%; + } + .col-lg-pull-8 { + right: 66.66666667%; + } + .col-lg-pull-7 { + right: 58.33333333%; + } + .col-lg-pull-6 { + right: 50%; + } + .col-lg-pull-5 { + right: 41.66666667%; + } + .col-lg-pull-4 { + right: 33.33333333%; + } + .col-lg-pull-3 { + right: 25%; + } + .col-lg-pull-2 { + right: 16.66666667%; + } + .col-lg-pull-1 { + right: 8.33333333%; + } + .col-lg-pull-0 { + right: auto; + } + .col-lg-push-12 { + left: 100%; + } + .col-lg-push-11 { + left: 91.66666667%; + } + .col-lg-push-10 { + left: 83.33333333%; + } + .col-lg-push-9 { + left: 75%; + } + .col-lg-push-8 { + left: 66.66666667%; + } + .col-lg-push-7 { + left: 58.33333333%; + } + .col-lg-push-6 { + left: 50%; + } + .col-lg-push-5 { + left: 41.66666667%; + } + .col-lg-push-4 { + left: 33.33333333%; + } + .col-lg-push-3 { + left: 25%; + } + .col-lg-push-2 { + left: 16.66666667%; + } + .col-lg-push-1 { + left: 8.33333333%; + } + .col-lg-push-0 { + left: auto; + } + .col-lg-offset-12 { + margin-left: 100%; + } + .col-lg-offset-11 { + margin-left: 91.66666667%; + } + .col-lg-offset-10 { + margin-left: 83.33333333%; + } + .col-lg-offset-9 { + margin-left: 75%; + } + .col-lg-offset-8 { + margin-left: 66.66666667%; + } + .col-lg-offset-7 { + margin-left: 58.33333333%; + } + .col-lg-offset-6 { + margin-left: 50%; + } + .col-lg-offset-5 { + margin-left: 41.66666667%; + } + .col-lg-offset-4 { + margin-left: 33.33333333%; + } + .col-lg-offset-3 { + margin-left: 25%; + } + .col-lg-offset-2 { + margin-left: 16.66666667%; + } + .col-lg-offset-1 { + margin-left: 8.33333333%; + } + .col-lg-offset-0 { + margin-left: 0%; + } +} +table { + background-color: #2e3338; +} +caption { + padding-top: 8px; + padding-bottom: 8px; + color: #7a8288; + text-align: left; +} +th { + text-align: left; +} +.table { + width: 100%; + max-width: 100%; + margin-bottom: 20px; +} +.table > thead > tr > th, +.table > tbody > tr > th, +.table > tfoot > tr > th, +.table > thead > tr > td, +.table > tbody > tr > td, +.table > tfoot > tr > td { + padding: 8px; + line-height: 1.42857143; + vertical-align: top; + border-top: 1px solid #1c1e22; +} +.table > thead > tr > th { + vertical-align: bottom; + border-bottom: 2px solid #1c1e22; +} +.table > caption + thead > tr:first-child > th, +.table > colgroup + thead > tr:first-child > th, +.table > thead:first-child > tr:first-child > th, +.table > caption + thead > tr:first-child > td, +.table > colgroup + thead > tr:first-child > td, +.table > thead:first-child > tr:first-child > td { + border-top: 0; +} +.table > tbody + tbody { + border-top: 2px solid #1c1e22; +} +.table .table { + background-color: #272b30; +} +.table-condensed > thead > tr > th, +.table-condensed > tbody > tr > th, +.table-condensed > tfoot > tr > th, +.table-condensed > thead > tr > td, +.table-condensed > tbody > tr > td, +.table-condensed > tfoot > tr > td { + padding: 5px; +} +.table-bordered { + border: 1px solid #1c1e22; +} +.table-bordered > thead > tr > th, +.table-bordered > tbody > tr > th, +.table-bordered > tfoot > tr > th, +.table-bordered > thead > tr > td, +.table-bordered > tbody > tr > td, +.table-bordered > tfoot > tr > td { + border: 1px solid #1c1e22; +} +.table-bordered > thead > tr > th, +.table-bordered > thead > tr > td { + border-bottom-width: 2px; +} +.table-striped > tbody > tr:nth-of-type(odd) { + background-color: #353a41; +} +.table-hover > tbody > tr:hover { + background-color: #49515a; +} +table col[class*="col-"] { + position: static; + float: none; + display: table-column; +} +table td[class*="col-"], +table th[class*="col-"] { + position: static; + float: none; + display: table-cell; +} +.table > thead > tr > td.active, +.table > tbody > tr > td.active, +.table > tfoot > tr > td.active, +.table > thead > tr > th.active, +.table > tbody > tr > th.active, +.table > tfoot > tr > th.active, +.table > thead > tr.active > td, +.table > tbody > tr.active > td, +.table > tfoot > tr.active > td, +.table > thead > tr.active > th, +.table > tbody > tr.active > th, +.table > tfoot > tr.active > th { + background-color: #49515a; +} +.table-hover > tbody > tr > td.active:hover, +.table-hover > tbody > tr > th.active:hover, +.table-hover > tbody > tr.active:hover > td, +.table-hover > tbody > tr:hover > .active, +.table-hover > tbody > tr.active:hover > th { + background-color: #3e444c; +} +.table > thead > tr > td.success, +.table > tbody > tr > td.success, +.table > tfoot > tr > td.success, +.table > thead > tr > th.success, +.table > tbody > tr > th.success, +.table > tfoot > tr > th.success, +.table > thead > tr.success > td, +.table > tbody > tr.success > td, +.table > tfoot > tr.success > td, +.table > thead > tr.success > th, +.table > tbody > tr.success > th, +.table > tfoot > tr.success > th { + background-color: #62c462; +} +.table-hover > tbody > tr > td.success:hover, +.table-hover > tbody > tr > th.success:hover, +.table-hover > tbody > tr.success:hover > td, +.table-hover > tbody > tr:hover > .success, +.table-hover > tbody > tr.success:hover > th { + background-color: #4fbd4f; +} +.table > thead > tr > td.info, +.table > tbody > tr > td.info, +.table > tfoot > tr > td.info, +.table > thead > tr > th.info, +.table > tbody > tr > th.info, +.table > tfoot > tr > th.info, +.table > thead > tr.info > td, +.table > tbody > tr.info > td, +.table > tfoot > tr.info > td, +.table > thead > tr.info > th, +.table > tbody > tr.info > th, +.table > tfoot > tr.info > th { + background-color: #5bc0de; +} +.table-hover > tbody > tr > td.info:hover, +.table-hover > tbody > tr > th.info:hover, +.table-hover > tbody > tr.info:hover > td, +.table-hover > tbody > tr:hover > .info, +.table-hover > tbody > tr.info:hover > th { + background-color: #46b8da; +} +.table > thead > tr > td.warning, +.table > tbody > tr > td.warning, +.table > tfoot > tr > td.warning, +.table > thead > tr > th.warning, +.table > tbody > tr > th.warning, +.table > tfoot > tr > th.warning, +.table > thead > tr.warning > td, +.table > tbody > tr.warning > td, +.table > tfoot > tr.warning > td, +.table > thead > tr.warning > th, +.table > tbody > tr.warning > th, +.table > tfoot > tr.warning > th { + background-color: #f89406; +} +.table-hover > tbody > tr > td.warning:hover, +.table-hover > tbody > tr > th.warning:hover, +.table-hover > tbody > tr.warning:hover > td, +.table-hover > tbody > tr:hover > .warning, +.table-hover > tbody > tr.warning:hover > th { + background-color: #df8505; +} +.table > thead > tr > td.danger, +.table > tbody > tr > td.danger, +.table > tfoot > tr > td.danger, +.table > thead > tr > th.danger, +.table > tbody > tr > th.danger, +.table > tfoot > tr > th.danger, +.table > thead > tr.danger > td, +.table > tbody > tr.danger > td, +.table > tfoot > tr.danger > td, +.table > thead > tr.danger > th, +.table > tbody > tr.danger > th, +.table > tfoot > tr.danger > th { + background-color: #ee5f5b; +} +.table-hover > tbody > tr > td.danger:hover, +.table-hover > tbody > tr > th.danger:hover, +.table-hover > tbody > tr.danger:hover > td, +.table-hover > tbody > tr:hover > .danger, +.table-hover > tbody > tr.danger:hover > th { + background-color: #ec4844; +} +.table-responsive { + overflow-x: auto; + min-height: 0.01%; +} +@media screen and (max-width: 767px) { + .table-responsive { + width: 100%; + margin-bottom: 15px; + overflow-y: hidden; + -ms-overflow-style: -ms-autohiding-scrollbar; + border: 1px solid #1c1e22; + } + .table-responsive > .table { + margin-bottom: 0; + } + .table-responsive > .table > thead > tr > th, + .table-responsive > .table > tbody > tr > th, + .table-responsive > .table > tfoot > tr > th, + .table-responsive > .table > thead > tr > td, + .table-responsive > .table > tbody > tr > td, + .table-responsive > .table > tfoot > tr > td { + white-space: nowrap; + } + .table-responsive > .table-bordered { + border: 0; + } + .table-responsive > .table-bordered > thead > tr > th:first-child, + .table-responsive > .table-bordered > tbody > tr > th:first-child, + .table-responsive > .table-bordered > tfoot > tr > th:first-child, + .table-responsive > .table-bordered > thead > tr > td:first-child, + .table-responsive > .table-bordered > tbody > tr > td:first-child, + .table-responsive > .table-bordered > tfoot > tr > td:first-child { + border-left: 0; + } + .table-responsive > .table-bordered > thead > tr > th:last-child, + .table-responsive > .table-bordered > tbody > tr > th:last-child, + .table-responsive > .table-bordered > tfoot > tr > th:last-child, + .table-responsive > .table-bordered > thead > tr > td:last-child, + .table-responsive > .table-bordered > tbody > tr > td:last-child, + .table-responsive > .table-bordered > tfoot > tr > td:last-child { + border-right: 0; + } + .table-responsive > .table-bordered > tbody > tr:last-child > th, + .table-responsive > .table-bordered > tfoot > tr:last-child > th, + .table-responsive > .table-bordered > tbody > tr:last-child > td, + .table-responsive > .table-bordered > tfoot > tr:last-child > td { + border-bottom: 0; + } +} +fieldset { + padding: 0; + margin: 0; + border: 0; + min-width: 0; +} +legend { + display: block; + width: 100%; + padding: 0; + margin-bottom: 20px; + font-size: 21px; + line-height: inherit; + color: #c8c8c8; + border: 0; + border-bottom: 1px solid #1c1e22; +} +label { + display: inline-block; + max-width: 100%; + margin-bottom: 5px; + font-weight: bold; +} +input[type="search"] { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +input[type="radio"], +input[type="checkbox"] { + margin: 4px 0 0; + margin-top: 1px \9; + line-height: normal; +} +input[type="file"] { + display: block; +} +input[type="range"] { + display: block; + width: 100%; +} +select[multiple], +select[size] { + height: auto; +} +input[type="file"]:focus, +input[type="radio"]:focus, +input[type="checkbox"]:focus { + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} +output { + display: block; + padding-top: 9px; + font-size: 14px; + line-height: 1.42857143; + color: #272b30; +} +.form-control { + display: block; + width: 100%; + height: 38px; + padding: 8px 12px; + font-size: 14px; + line-height: 1.42857143; + color: #272b30; + background-color: #ffffff; + background-image: none; + border: 1px solid #000000; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + -webkit-transition: border-color ease-in-out .15s, -webkit-box-shadow ease-in-out .15s; + -o-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; + transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; +} +.form-control:focus { + border-color: #66afe9; + outline: 0; + -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, 0.6); + box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, 0.6); +} +.form-control::-moz-placeholder { + color: #7a8288; + opacity: 1; +} +.form-control:-ms-input-placeholder { + color: #7a8288; +} +.form-control::-webkit-input-placeholder { + color: #7a8288; +} +.form-control::-ms-expand { + border: 0; + background-color: transparent; +} +.form-control[disabled], +.form-control[readonly], +fieldset[disabled] .form-control { + background-color: #999999; + opacity: 1; +} +.form-control[disabled], +fieldset[disabled] .form-control { + cursor: not-allowed; +} +textarea.form-control { + height: auto; +} +input[type="search"] { + -webkit-appearance: none; +} +@media screen and (-webkit-min-device-pixel-ratio: 0) { + input[type="date"].form-control, + input[type="time"].form-control, + input[type="datetime-local"].form-control, + input[type="month"].form-control { + line-height: 38px; + } + input[type="date"].input-sm, + input[type="time"].input-sm, + input[type="datetime-local"].input-sm, + input[type="month"].input-sm, + .input-group-sm input[type="date"], + .input-group-sm input[type="time"], + .input-group-sm input[type="datetime-local"], + .input-group-sm input[type="month"] { + line-height: 30px; + } + input[type="date"].input-lg, + input[type="time"].input-lg, + input[type="datetime-local"].input-lg, + input[type="month"].input-lg, + .input-group-lg input[type="date"], + .input-group-lg input[type="time"], + .input-group-lg input[type="datetime-local"], + .input-group-lg input[type="month"] { + line-height: 54px; + } +} +.form-group { + margin-bottom: 15px; +} +.radio, +.checkbox { + position: relative; + display: block; + margin-top: 10px; + margin-bottom: 10px; +} +.radio label, +.checkbox label { + min-height: 20px; + padding-left: 20px; + margin-bottom: 0; + font-weight: normal; + cursor: pointer; +} +.radio input[type="radio"], +.radio-inline input[type="radio"], +.checkbox input[type="checkbox"], +.checkbox-inline input[type="checkbox"] { + position: absolute; + margin-left: -20px; + margin-top: 4px \9; +} +.radio + .radio, +.checkbox + .checkbox { + margin-top: -5px; +} +.radio-inline, +.checkbox-inline { + position: relative; + display: inline-block; + padding-left: 20px; + margin-bottom: 0; + vertical-align: middle; + font-weight: normal; + cursor: pointer; +} +.radio-inline + .radio-inline, +.checkbox-inline + .checkbox-inline { + margin-top: 0; + margin-left: 10px; +} +input[type="radio"][disabled], +input[type="checkbox"][disabled], +input[type="radio"].disabled, +input[type="checkbox"].disabled, +fieldset[disabled] input[type="radio"], +fieldset[disabled] input[type="checkbox"] { + cursor: not-allowed; +} +.radio-inline.disabled, +.checkbox-inline.disabled, +fieldset[disabled] .radio-inline, +fieldset[disabled] .checkbox-inline { + cursor: not-allowed; +} +.radio.disabled label, +.checkbox.disabled label, +fieldset[disabled] .radio label, +fieldset[disabled] .checkbox label { + cursor: not-allowed; +} +.form-control-static { + padding-top: 9px; + padding-bottom: 9px; + margin-bottom: 0; + min-height: 34px; +} +.form-control-static.input-lg, +.form-control-static.input-sm { + padding-left: 0; + padding-right: 0; +} +.input-sm { + height: 30px; + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} +select.input-sm { + height: 30px; + line-height: 30px; +} +textarea.input-sm, +select[multiple].input-sm { + height: auto; +} +.form-group-sm .form-control { + height: 30px; + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} +.form-group-sm select.form-control { + height: 30px; + line-height: 30px; +} +.form-group-sm textarea.form-control, +.form-group-sm select[multiple].form-control { + height: auto; +} +.form-group-sm .form-control-static { + height: 30px; + min-height: 32px; + padding: 6px 10px; + font-size: 12px; + line-height: 1.5; +} +.input-lg { + height: 54px; + padding: 14px 16px; + font-size: 18px; + line-height: 1.3333333; + border-radius: 6px; +} +select.input-lg { + height: 54px; + line-height: 54px; +} +textarea.input-lg, +select[multiple].input-lg { + height: auto; +} +.form-group-lg .form-control { + height: 54px; + padding: 14px 16px; + font-size: 18px; + line-height: 1.3333333; + border-radius: 6px; +} +.form-group-lg select.form-control { + height: 54px; + line-height: 54px; +} +.form-group-lg textarea.form-control, +.form-group-lg select[multiple].form-control { + height: auto; +} +.form-group-lg .form-control-static { + height: 54px; + min-height: 38px; + padding: 15px 16px; + font-size: 18px; + line-height: 1.3333333; +} +.has-feedback { + position: relative; +} +.has-feedback .form-control { + padding-right: 47.5px; +} +.form-control-feedback { + position: absolute; + top: 0; + right: 0; + z-index: 2; + display: block; + width: 38px; + height: 38px; + line-height: 38px; + text-align: center; + pointer-events: none; +} +.input-lg + .form-control-feedback, +.input-group-lg + .form-control-feedback, +.form-group-lg .form-control + .form-control-feedback { + width: 54px; + height: 54px; + line-height: 54px; +} +.input-sm + .form-control-feedback, +.input-group-sm + .form-control-feedback, +.form-group-sm .form-control + .form-control-feedback { + width: 30px; + height: 30px; + line-height: 30px; +} +.has-success .help-block, +.has-success .control-label, +.has-success .radio, +.has-success .checkbox, +.has-success .radio-inline, +.has-success .checkbox-inline, +.has-success.radio label, +.has-success.checkbox label, +.has-success.radio-inline label, +.has-success.checkbox-inline label { + color: #ffffff; +} +.has-success .form-control { + border-color: #ffffff; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); +} +.has-success .form-control:focus { + border-color: #e6e6e6; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ffffff; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ffffff; +} +.has-success .input-group-addon { + color: #ffffff; + border-color: #ffffff; + background-color: #62c462; +} +.has-success .form-control-feedback { + color: #ffffff; +} +.has-warning .help-block, +.has-warning .control-label, +.has-warning .radio, +.has-warning .checkbox, +.has-warning .radio-inline, +.has-warning .checkbox-inline, +.has-warning.radio label, +.has-warning.checkbox label, +.has-warning.radio-inline label, +.has-warning.checkbox-inline label { + color: #ffffff; +} +.has-warning .form-control { + border-color: #ffffff; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); +} +.has-warning .form-control:focus { + border-color: #e6e6e6; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ffffff; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ffffff; +} +.has-warning .input-group-addon { + color: #ffffff; + border-color: #ffffff; + background-color: #f89406; +} +.has-warning .form-control-feedback { + color: #ffffff; +} +.has-error .help-block, +.has-error .control-label, +.has-error .radio, +.has-error .checkbox, +.has-error .radio-inline, +.has-error .checkbox-inline, +.has-error.radio label, +.has-error.checkbox label, +.has-error.radio-inline label, +.has-error.checkbox-inline label { + color: #ffffff; +} +.has-error .form-control { + border-color: #ffffff; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); +} +.has-error .form-control:focus { + border-color: #e6e6e6; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ffffff; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ffffff; +} +.has-error .input-group-addon { + color: #ffffff; + border-color: #ffffff; + background-color: #ee5f5b; +} +.has-error .form-control-feedback { + color: #ffffff; +} +.has-feedback label ~ .form-control-feedback { + top: 25px; +} +.has-feedback label.sr-only ~ .form-control-feedback { + top: 0; +} +.help-block { + display: block; + margin-top: 5px; + margin-bottom: 10px; + color: #ffffff; +} +@media (min-width: 768px) { + .form-inline .form-group { + display: inline-block; + margin-bottom: 0; + vertical-align: middle; + } + .form-inline .form-control { + display: inline-block; + width: auto; + vertical-align: middle; + } + .form-inline .form-control-static { + display: inline-block; + } + .form-inline .input-group { + display: inline-table; + vertical-align: middle; + } + .form-inline .input-group .input-group-addon, + .form-inline .input-group .input-group-btn, + .form-inline .input-group .form-control { + width: auto; + } + .form-inline .input-group > .form-control { + width: 100%; + } + .form-inline .control-label { + margin-bottom: 0; + vertical-align: middle; + } + .form-inline .radio, + .form-inline .checkbox { + display: inline-block; + margin-top: 0; + margin-bottom: 0; + vertical-align: middle; + } + .form-inline .radio label, + .form-inline .checkbox label { + padding-left: 0; + } + .form-inline .radio input[type="radio"], + .form-inline .checkbox input[type="checkbox"] { + position: relative; + margin-left: 0; + } + .form-inline .has-feedback .form-control-feedback { + top: 0; + } +} +.form-horizontal .radio, +.form-horizontal .checkbox, +.form-horizontal .radio-inline, +.form-horizontal .checkbox-inline { + margin-top: 0; + margin-bottom: 0; + padding-top: 9px; +} +.form-horizontal .radio, +.form-horizontal .checkbox { + min-height: 29px; +} +.form-horizontal .form-group { + margin-left: -15px; + margin-right: -15px; +} +@media (min-width: 768px) { + .form-horizontal .control-label { + text-align: right; + margin-bottom: 0; + padding-top: 9px; + } +} +.form-horizontal .has-feedback .form-control-feedback { + right: 15px; +} +@media (min-width: 768px) { + .form-horizontal .form-group-lg .control-label { + padding-top: 15px; + font-size: 18px; + } +} +@media (min-width: 768px) { + .form-horizontal .form-group-sm .control-label { + padding-top: 6px; + font-size: 12px; + } +} +.btn { + display: inline-block; + margin-bottom: 0; + font-weight: normal; + text-align: center; + vertical-align: middle; + -ms-touch-action: manipulation; + touch-action: manipulation; + cursor: pointer; + background-image: none; + border: 0px solid transparent; + white-space: nowrap; + padding: 8px 12px; + font-size: 14px; + line-height: 1.42857143; + border-radius: 4px; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} +.btn:focus, +.btn:active:focus, +.btn.active:focus, +.btn.focus, +.btn:active.focus, +.btn.active.focus { + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} +.btn:hover, +.btn:focus, +.btn.focus { + color: #ffffff; + text-decoration: none; +} +.btn:active, +.btn.active { + outline: 0; + background-image: none; + -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); +} +.btn.disabled, +.btn[disabled], +fieldset[disabled] .btn { + cursor: not-allowed; + opacity: 0.65; + filter: alpha(opacity=65); + -webkit-box-shadow: none; + box-shadow: none; +} +a.btn.disabled, +fieldset[disabled] a.btn { + pointer-events: none; +} +.btn-default { + color: #ffffff; + background-color: #3a3f44; + border-color: #3a3f44; +} +.btn-default:focus, +.btn-default.focus { + color: #ffffff; + background-color: #232628; + border-color: #000000; +} +.btn-default:hover { + color: #ffffff; + background-color: #232628; + border-color: #1e2023; +} +.btn-default:active, +.btn-default.active, +.open > .dropdown-toggle.btn-default { + color: #ffffff; + background-color: #232628; + border-color: #1e2023; +} +.btn-default:active:hover, +.btn-default.active:hover, +.open > .dropdown-toggle.btn-default:hover, +.btn-default:active:focus, +.btn-default.active:focus, +.open > .dropdown-toggle.btn-default:focus, +.btn-default:active.focus, +.btn-default.active.focus, +.open > .dropdown-toggle.btn-default.focus { + color: #ffffff; + background-color: #121415; + border-color: #000000; +} +.btn-default:active, +.btn-default.active, +.open > .dropdown-toggle.btn-default { + background-image: none; +} +.btn-default.disabled:hover, +.btn-default[disabled]:hover, +fieldset[disabled] .btn-default:hover, +.btn-default.disabled:focus, +.btn-default[disabled]:focus, +fieldset[disabled] .btn-default:focus, +.btn-default.disabled.focus, +.btn-default[disabled].focus, +fieldset[disabled] .btn-default.focus { + background-color: #3a3f44; + border-color: #3a3f44; +} +.btn-default .badge { + color: #3a3f44; + background-color: #ffffff; +} +.btn-primary { + color: #ffffff; + background-color: #7a8288; + border-color: #7a8288; +} +.btn-primary:focus, +.btn-primary.focus { + color: #ffffff; + background-color: #62686d; + border-color: #3e4245; +} +.btn-primary:hover { + color: #ffffff; + background-color: #62686d; + border-color: #5d6368; +} +.btn-primary:active, +.btn-primary.active, +.open > .dropdown-toggle.btn-primary { + color: #ffffff; + background-color: #62686d; + border-color: #5d6368; +} +.btn-primary:active:hover, +.btn-primary.active:hover, +.open > .dropdown-toggle.btn-primary:hover, +.btn-primary:active:focus, +.btn-primary.active:focus, +.open > .dropdown-toggle.btn-primary:focus, +.btn-primary:active.focus, +.btn-primary.active.focus, +.open > .dropdown-toggle.btn-primary.focus { + color: #ffffff; + background-color: #51565a; + border-color: #3e4245; +} +.btn-primary:active, +.btn-primary.active, +.open > .dropdown-toggle.btn-primary { + background-image: none; +} +.btn-primary.disabled:hover, +.btn-primary[disabled]:hover, +fieldset[disabled] .btn-primary:hover, +.btn-primary.disabled:focus, +.btn-primary[disabled]:focus, +fieldset[disabled] .btn-primary:focus, +.btn-primary.disabled.focus, +.btn-primary[disabled].focus, +fieldset[disabled] .btn-primary.focus { + background-color: #7a8288; + border-color: #7a8288; +} +.btn-primary .badge { + color: #7a8288; + background-color: #ffffff; +} +.btn-success { + color: #ffffff; + background-color: #62c462; + border-color: #62c462; +} +.btn-success:focus, +.btn-success.focus { + color: #ffffff; + background-color: #42b142; + border-color: #2d792d; +} +.btn-success:hover { + color: #ffffff; + background-color: #42b142; + border-color: #40a940; +} +.btn-success:active, +.btn-success.active, +.open > .dropdown-toggle.btn-success { + color: #ffffff; + background-color: #42b142; + border-color: #40a940; +} +.btn-success:active:hover, +.btn-success.active:hover, +.open > .dropdown-toggle.btn-success:hover, +.btn-success:active:focus, +.btn-success.active:focus, +.open > .dropdown-toggle.btn-success:focus, +.btn-success:active.focus, +.btn-success.active.focus, +.open > .dropdown-toggle.btn-success.focus { + color: #ffffff; + background-color: #399739; + border-color: #2d792d; +} +.btn-success:active, +.btn-success.active, +.open > .dropdown-toggle.btn-success { + background-image: none; +} +.btn-success.disabled:hover, +.btn-success[disabled]:hover, +fieldset[disabled] .btn-success:hover, +.btn-success.disabled:focus, +.btn-success[disabled]:focus, +fieldset[disabled] .btn-success:focus, +.btn-success.disabled.focus, +.btn-success[disabled].focus, +fieldset[disabled] .btn-success.focus { + background-color: #62c462; + border-color: #62c462; +} +.btn-success .badge { + color: #62c462; + background-color: #ffffff; +} +.btn-info { + color: #ffffff; + background-color: #5bc0de; + border-color: #5bc0de; +} +.btn-info:focus, +.btn-info.focus { + color: #ffffff; + background-color: #31b0d5; + border-color: #1f7e9a; +} +.btn-info:hover { + color: #ffffff; + background-color: #31b0d5; + border-color: #2aabd2; +} +.btn-info:active, +.btn-info.active, +.open > .dropdown-toggle.btn-info { + color: #ffffff; + background-color: #31b0d5; + border-color: #2aabd2; +} +.btn-info:active:hover, +.btn-info.active:hover, +.open > .dropdown-toggle.btn-info:hover, +.btn-info:active:focus, +.btn-info.active:focus, +.open > .dropdown-toggle.btn-info:focus, +.btn-info:active.focus, +.btn-info.active.focus, +.open > .dropdown-toggle.btn-info.focus { + color: #ffffff; + background-color: #269abc; + border-color: #1f7e9a; +} +.btn-info:active, +.btn-info.active, +.open > .dropdown-toggle.btn-info { + background-image: none; +} +.btn-info.disabled:hover, +.btn-info[disabled]:hover, +fieldset[disabled] .btn-info:hover, +.btn-info.disabled:focus, +.btn-info[disabled]:focus, +fieldset[disabled] .btn-info:focus, +.btn-info.disabled.focus, +.btn-info[disabled].focus, +fieldset[disabled] .btn-info.focus { + background-color: #5bc0de; + border-color: #5bc0de; +} +.btn-info .badge { + color: #5bc0de; + background-color: #ffffff; +} +.btn-warning { + color: #ffffff; + background-color: #f89406; + border-color: #f89406; +} +.btn-warning:focus, +.btn-warning.focus { + color: #ffffff; + background-color: #c67605; + border-color: #7c4a03; +} +.btn-warning:hover { + color: #ffffff; + background-color: #c67605; + border-color: #bc7005; +} +.btn-warning:active, +.btn-warning.active, +.open > .dropdown-toggle.btn-warning { + color: #ffffff; + background-color: #c67605; + border-color: #bc7005; +} +.btn-warning:active:hover, +.btn-warning.active:hover, +.open > .dropdown-toggle.btn-warning:hover, +.btn-warning:active:focus, +.btn-warning.active:focus, +.open > .dropdown-toggle.btn-warning:focus, +.btn-warning:active.focus, +.btn-warning.active.focus, +.open > .dropdown-toggle.btn-warning.focus { + color: #ffffff; + background-color: #a36104; + border-color: #7c4a03; +} +.btn-warning:active, +.btn-warning.active, +.open > .dropdown-toggle.btn-warning { + background-image: none; +} +.btn-warning.disabled:hover, +.btn-warning[disabled]:hover, +fieldset[disabled] .btn-warning:hover, +.btn-warning.disabled:focus, +.btn-warning[disabled]:focus, +fieldset[disabled] .btn-warning:focus, +.btn-warning.disabled.focus, +.btn-warning[disabled].focus, +fieldset[disabled] .btn-warning.focus { + background-color: #f89406; + border-color: #f89406; +} +.btn-warning .badge { + color: #f89406; + background-color: #ffffff; +} +.btn-danger { + color: #ffffff; + background-color: #ee5f5b; + border-color: #ee5f5b; +} +.btn-danger:focus, +.btn-danger.focus { + color: #ffffff; + background-color: #e9322d; + border-color: #b71713; +} +.btn-danger:hover { + color: #ffffff; + background-color: #e9322d; + border-color: #e82924; +} +.btn-danger:active, +.btn-danger.active, +.open > .dropdown-toggle.btn-danger { + color: #ffffff; + background-color: #e9322d; + border-color: #e82924; +} +.btn-danger:active:hover, +.btn-danger.active:hover, +.open > .dropdown-toggle.btn-danger:hover, +.btn-danger:active:focus, +.btn-danger.active:focus, +.open > .dropdown-toggle.btn-danger:focus, +.btn-danger:active.focus, +.btn-danger.active.focus, +.open > .dropdown-toggle.btn-danger.focus { + color: #ffffff; + background-color: #dc1c17; + border-color: #b71713; +} +.btn-danger:active, +.btn-danger.active, +.open > .dropdown-toggle.btn-danger { + background-image: none; +} +.btn-danger.disabled:hover, +.btn-danger[disabled]:hover, +fieldset[disabled] .btn-danger:hover, +.btn-danger.disabled:focus, +.btn-danger[disabled]:focus, +fieldset[disabled] .btn-danger:focus, +.btn-danger.disabled.focus, +.btn-danger[disabled].focus, +fieldset[disabled] .btn-danger.focus { + background-color: #ee5f5b; + border-color: #ee5f5b; +} +.btn-danger .badge { + color: #ee5f5b; + background-color: #ffffff; +} +.btn-link { + color: #ffffff; + font-weight: normal; + border-radius: 0; +} +.btn-link, +.btn-link:active, +.btn-link.active, +.btn-link[disabled], +fieldset[disabled] .btn-link { + background-color: transparent; + -webkit-box-shadow: none; + box-shadow: none; +} +.btn-link, +.btn-link:hover, +.btn-link:focus, +.btn-link:active { + border-color: transparent; +} +.btn-link:hover, +.btn-link:focus { + color: #ffffff; + text-decoration: underline; + background-color: transparent; +} +.btn-link[disabled]:hover, +fieldset[disabled] .btn-link:hover, +.btn-link[disabled]:focus, +fieldset[disabled] .btn-link:focus { + color: #7a8288; + text-decoration: none; +} +.btn-lg, +.btn-group-lg > .btn { + padding: 14px 16px; + font-size: 18px; + line-height: 1.3333333; + border-radius: 6px; +} +.btn-sm, +.btn-group-sm > .btn { + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} +.btn-xs, +.btn-group-xs > .btn { + padding: 1px 5px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} +.btn-block { + display: block; + width: 100%; +} +.btn-block + .btn-block { + margin-top: 5px; +} +input[type="submit"].btn-block, +input[type="reset"].btn-block, +input[type="button"].btn-block { + width: 100%; +} +.fade { + opacity: 0; + -webkit-transition: opacity 0.15s linear; + -o-transition: opacity 0.15s linear; + transition: opacity 0.15s linear; +} +.fade.in { + opacity: 1; +} +.collapse { + display: none; +} +.collapse.in { + display: block; +} +tr.collapse.in { + display: table-row; +} +tbody.collapse.in { + display: table-row-group; +} +.collapsing { + position: relative; + height: 0; + overflow: hidden; + -webkit-transition-property: height, visibility; + -o-transition-property: height, visibility; + transition-property: height, visibility; + -webkit-transition-duration: 0.35s; + -o-transition-duration: 0.35s; + transition-duration: 0.35s; + -webkit-transition-timing-function: ease; + -o-transition-timing-function: ease; + transition-timing-function: ease; +} +.caret { + display: inline-block; + width: 0; + height: 0; + margin-left: 2px; + vertical-align: middle; + border-top: 4px dashed; + border-top: 4px solid \9; + border-right: 4px solid transparent; + border-left: 4px solid transparent; +} +.dropup, +.dropdown { + position: relative; +} +.dropdown-toggle:focus { + outline: 0; +} +.dropdown-menu { + position: absolute; + top: 100%; + left: 0; + z-index: 1000; + display: none; + float: left; + min-width: 160px; + padding: 5px 0; + margin: 2px 0 0; + list-style: none; + font-size: 14px; + text-align: left; + background-color: #3a3f44; + border: 1px solid #272b30; + border: 1px solid rgba(0, 0, 0, 0.15); + border-radius: 4px; + -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); + box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); + -webkit-background-clip: padding-box; + background-clip: padding-box; +} +.dropdown-menu.pull-right { + right: 0; + left: auto; +} +.dropdown-menu .divider { + height: 1px; + margin: 9px 0; + overflow: hidden; + background-color: #272b30; +} +.dropdown-menu > li > a { + display: block; + padding: 3px 20px; + clear: both; + font-weight: normal; + line-height: 1.42857143; + color: #c8c8c8; + white-space: nowrap; +} +.dropdown-menu > li > a:hover, +.dropdown-menu > li > a:focus { + text-decoration: none; + color: #ffffff; + background-color: #272b30; +} +.dropdown-menu > .active > a, +.dropdown-menu > .active > a:hover, +.dropdown-menu > .active > a:focus { + color: #ffffff; + text-decoration: none; + outline: 0; + background-color: #272b30; +} +.dropdown-menu > .disabled > a, +.dropdown-menu > .disabled > a:hover, +.dropdown-menu > .disabled > a:focus { + color: #7a8288; +} +.dropdown-menu > .disabled > a:hover, +.dropdown-menu > .disabled > a:focus { + text-decoration: none; + background-color: transparent; + background-image: none; + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); + cursor: not-allowed; +} +.open > .dropdown-menu { + display: block; +} +.open > a { + outline: 0; +} +.dropdown-menu-right { + left: auto; + right: 0; +} +.dropdown-menu-left { + left: 0; + right: auto; +} +.dropdown-header { + display: block; + padding: 3px 20px; + font-size: 12px; + line-height: 1.42857143; + color: #7a8288; + white-space: nowrap; +} +.dropdown-backdrop { + position: fixed; + left: 0; + right: 0; + bottom: 0; + top: 0; + z-index: 990; +} +.pull-right > .dropdown-menu { + right: 0; + left: auto; +} +.dropup .caret, +.navbar-fixed-bottom .dropdown .caret { + border-top: 0; + border-bottom: 4px dashed; + border-bottom: 4px solid \9; + content: ""; +} +.dropup .dropdown-menu, +.navbar-fixed-bottom .dropdown .dropdown-menu { + top: auto; + bottom: 100%; + margin-bottom: 2px; +} +@media (min-width: 768px) { + .navbar-right .dropdown-menu { + left: auto; + right: 0; + } + .navbar-right .dropdown-menu-left { + left: 0; + right: auto; + } +} +.btn-group, +.btn-group-vertical { + position: relative; + display: inline-block; + vertical-align: middle; +} +.btn-group > .btn, +.btn-group-vertical > .btn { + position: relative; + float: left; +} +.btn-group > .btn:hover, +.btn-group-vertical > .btn:hover, +.btn-group > .btn:focus, +.btn-group-vertical > .btn:focus, +.btn-group > .btn:active, +.btn-group-vertical > .btn:active, +.btn-group > .btn.active, +.btn-group-vertical > .btn.active { + z-index: 2; +} +.btn-group .btn + .btn, +.btn-group .btn + .btn-group, +.btn-group .btn-group + .btn, +.btn-group .btn-group + .btn-group { + margin-left: -1px; +} +.btn-toolbar { + margin-left: -5px; +} +.btn-toolbar .btn, +.btn-toolbar .btn-group, +.btn-toolbar .input-group { + float: left; +} +.btn-toolbar > .btn, +.btn-toolbar > .btn-group, +.btn-toolbar > .input-group { + margin-left: 5px; +} +.btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) { + border-radius: 0; +} +.btn-group > .btn:first-child { + margin-left: 0; +} +.btn-group > .btn:first-child:not(:last-child):not(.dropdown-toggle) { + border-bottom-right-radius: 0; + border-top-right-radius: 0; +} +.btn-group > .btn:last-child:not(:first-child), +.btn-group > .dropdown-toggle:not(:first-child) { + border-bottom-left-radius: 0; + border-top-left-radius: 0; +} +.btn-group > .btn-group { + float: left; +} +.btn-group > .btn-group:not(:first-child):not(:last-child) > .btn { + border-radius: 0; +} +.btn-group > .btn-group:first-child:not(:last-child) > .btn:last-child, +.btn-group > .btn-group:first-child:not(:last-child) > .dropdown-toggle { + border-bottom-right-radius: 0; + border-top-right-radius: 0; +} +.btn-group > .btn-group:last-child:not(:first-child) > .btn:first-child { + border-bottom-left-radius: 0; + border-top-left-radius: 0; +} +.btn-group .dropdown-toggle:active, +.btn-group.open .dropdown-toggle { + outline: 0; +} +.btn-group > .btn + .dropdown-toggle { + padding-left: 8px; + padding-right: 8px; +} +.btn-group > .btn-lg + .dropdown-toggle { + padding-left: 12px; + padding-right: 12px; +} +.btn-group.open .dropdown-toggle { + -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); +} +.btn-group.open .dropdown-toggle.btn-link { + -webkit-box-shadow: none; + box-shadow: none; +} +.btn .caret { + margin-left: 0; +} +.btn-lg .caret { + border-width: 5px 5px 0; + border-bottom-width: 0; +} +.dropup .btn-lg .caret { + border-width: 0 5px 5px; +} +.btn-group-vertical > .btn, +.btn-group-vertical > .btn-group, +.btn-group-vertical > .btn-group > .btn { + display: block; + float: none; + width: 100%; + max-width: 100%; +} +.btn-group-vertical > .btn-group > .btn { + float: none; +} +.btn-group-vertical > .btn + .btn, +.btn-group-vertical > .btn + .btn-group, +.btn-group-vertical > .btn-group + .btn, +.btn-group-vertical > .btn-group + .btn-group { + margin-top: -1px; + margin-left: 0; +} +.btn-group-vertical > .btn:not(:first-child):not(:last-child) { + border-radius: 0; +} +.btn-group-vertical > .btn:first-child:not(:last-child) { + border-top-right-radius: 4px; + border-top-left-radius: 4px; + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} +.btn-group-vertical > .btn:last-child:not(:first-child) { + border-top-right-radius: 0; + border-top-left-radius: 0; + border-bottom-right-radius: 4px; + border-bottom-left-radius: 4px; +} +.btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn { + border-radius: 0; +} +.btn-group-vertical > .btn-group:first-child:not(:last-child) > .btn:last-child, +.btn-group-vertical > .btn-group:first-child:not(:last-child) > .dropdown-toggle { + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} +.btn-group-vertical > .btn-group:last-child:not(:first-child) > .btn:first-child { + border-top-right-radius: 0; + border-top-left-radius: 0; +} +.btn-group-justified { + display: table; + width: 100%; + table-layout: fixed; + border-collapse: separate; +} +.btn-group-justified > .btn, +.btn-group-justified > .btn-group { + float: none; + display: table-cell; + width: 1%; +} +.btn-group-justified > .btn-group .btn { + width: 100%; +} +.btn-group-justified > .btn-group .dropdown-menu { + left: auto; +} +[data-toggle="buttons"] > .btn input[type="radio"], +[data-toggle="buttons"] > .btn-group > .btn input[type="radio"], +[data-toggle="buttons"] > .btn input[type="checkbox"], +[data-toggle="buttons"] > .btn-group > .btn input[type="checkbox"] { + position: absolute; + clip: rect(0, 0, 0, 0); + pointer-events: none; +} +.input-group { + position: relative; + display: table; + border-collapse: separate; +} +.input-group[class*="col-"] { + float: none; + padding-left: 0; + padding-right: 0; +} +.input-group .form-control { + position: relative; + z-index: 2; + float: left; + width: 100%; + margin-bottom: 0; +} +.input-group .form-control:focus { + z-index: 3; +} +.input-group-lg > .form-control, +.input-group-lg > .input-group-addon, +.input-group-lg > .input-group-btn > .btn { + height: 54px; + padding: 14px 16px; + font-size: 18px; + line-height: 1.3333333; + border-radius: 6px; +} +select.input-group-lg > .form-control, +select.input-group-lg > .input-group-addon, +select.input-group-lg > .input-group-btn > .btn { + height: 54px; + line-height: 54px; +} +textarea.input-group-lg > .form-control, +textarea.input-group-lg > .input-group-addon, +textarea.input-group-lg > .input-group-btn > .btn, +select[multiple].input-group-lg > .form-control, +select[multiple].input-group-lg > .input-group-addon, +select[multiple].input-group-lg > .input-group-btn > .btn { + height: auto; +} +.input-group-sm > .form-control, +.input-group-sm > .input-group-addon, +.input-group-sm > .input-group-btn > .btn { + height: 30px; + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} +select.input-group-sm > .form-control, +select.input-group-sm > .input-group-addon, +select.input-group-sm > .input-group-btn > .btn { + height: 30px; + line-height: 30px; +} +textarea.input-group-sm > .form-control, +textarea.input-group-sm > .input-group-addon, +textarea.input-group-sm > .input-group-btn > .btn, +select[multiple].input-group-sm > .form-control, +select[multiple].input-group-sm > .input-group-addon, +select[multiple].input-group-sm > .input-group-btn > .btn { + height: auto; +} +.input-group-addon, +.input-group-btn, +.input-group .form-control { + display: table-cell; +} +.input-group-addon:not(:first-child):not(:last-child), +.input-group-btn:not(:first-child):not(:last-child), +.input-group .form-control:not(:first-child):not(:last-child) { + border-radius: 0; +} +.input-group-addon, +.input-group-btn { + width: 1%; + white-space: nowrap; + vertical-align: middle; +} +.input-group-addon { + padding: 8px 12px; + font-size: 14px; + font-weight: normal; + line-height: 1; + color: #272b30; + text-align: center; + background-color: #3a3f44; + border: 1px solid rgba(0, 0, 0, 0.6); + border-radius: 4px; +} +.input-group-addon.input-sm { + padding: 5px 10px; + font-size: 12px; + border-radius: 3px; +} +.input-group-addon.input-lg { + padding: 14px 16px; + font-size: 18px; + border-radius: 6px; +} +.input-group-addon input[type="radio"], +.input-group-addon input[type="checkbox"] { + margin-top: 0; +} +.input-group .form-control:first-child, +.input-group-addon:first-child, +.input-group-btn:first-child > .btn, +.input-group-btn:first-child > .btn-group > .btn, +.input-group-btn:first-child > .dropdown-toggle, +.input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle), +.input-group-btn:last-child > .btn-group:not(:last-child) > .btn { + border-bottom-right-radius: 0; + border-top-right-radius: 0; +} +.input-group-addon:first-child { + border-right: 0; +} +.input-group .form-control:last-child, +.input-group-addon:last-child, +.input-group-btn:last-child > .btn, +.input-group-btn:last-child > .btn-group > .btn, +.input-group-btn:last-child > .dropdown-toggle, +.input-group-btn:first-child > .btn:not(:first-child), +.input-group-btn:first-child > .btn-group:not(:first-child) > .btn { + border-bottom-left-radius: 0; + border-top-left-radius: 0; +} +.input-group-addon:last-child { + border-left: 0; +} +.input-group-btn { + position: relative; + font-size: 0; + white-space: nowrap; +} +.input-group-btn > .btn { + position: relative; +} +.input-group-btn > .btn + .btn { + margin-left: -1px; +} +.input-group-btn > .btn:hover, +.input-group-btn > .btn:focus, +.input-group-btn > .btn:active { + z-index: 2; +} +.input-group-btn:first-child > .btn, +.input-group-btn:first-child > .btn-group { + margin-right: -1px; +} +.input-group-btn:last-child > .btn, +.input-group-btn:last-child > .btn-group { + z-index: 2; + margin-left: -1px; +} +.nav { + margin-bottom: 0; + padding-left: 0; + list-style: none; +} +.nav > li { + position: relative; + display: block; +} +.nav > li > a { + position: relative; + display: block; + padding: 10px 15px; +} +.nav > li > a:hover, +.nav > li > a:focus { + text-decoration: none; + background-color: #3e444c; +} +.nav > li.disabled > a { + color: #7a8288; +} +.nav > li.disabled > a:hover, +.nav > li.disabled > a:focus { + color: #7a8288; + text-decoration: none; + background-color: transparent; + cursor: not-allowed; +} +.nav .open > a, +.nav .open > a:hover, +.nav .open > a:focus { + background-color: #3e444c; + border-color: #ffffff; +} +.nav .nav-divider { + height: 1px; + margin: 9px 0; + overflow: hidden; + background-color: #e5e5e5; +} +.nav > li > a > img { + max-width: none; +} +.nav-tabs { + border-bottom: 1px solid #1c1e22; +} +.nav-tabs > li { + float: left; + margin-bottom: -1px; +} +.nav-tabs > li > a { + margin-right: 2px; + line-height: 1.42857143; + border: 1px solid transparent; + border-radius: 4px 4px 0 0; +} +.nav-tabs > li > a:hover { + border-color: #1c1e22 #1c1e22 #1c1e22; +} +.nav-tabs > li.active > a, +.nav-tabs > li.active > a:hover, +.nav-tabs > li.active > a:focus { + color: #ffffff; + background-color: #3e444c; + border: 1px solid #1c1e22; + border-bottom-color: transparent; + cursor: default; +} +.nav-tabs.nav-justified { + width: 100%; + border-bottom: 0; +} +.nav-tabs.nav-justified > li { + float: none; +} +.nav-tabs.nav-justified > li > a { + text-align: center; + margin-bottom: 5px; +} +.nav-tabs.nav-justified > .dropdown .dropdown-menu { + top: auto; + left: auto; +} +@media (min-width: 768px) { + .nav-tabs.nav-justified > li { + display: table-cell; + width: 1%; + } + .nav-tabs.nav-justified > li > a { + margin-bottom: 0; + } +} +.nav-tabs.nav-justified > li > a { + margin-right: 0; + border-radius: 4px; +} +.nav-tabs.nav-justified > .active > a, +.nav-tabs.nav-justified > .active > a:hover, +.nav-tabs.nav-justified > .active > a:focus { + border: 1px solid #1c1e22; +} +@media (min-width: 768px) { + .nav-tabs.nav-justified > li > a { + border-bottom: 1px solid #1c1e22; + border-radius: 4px 4px 0 0; + } + .nav-tabs.nav-justified > .active > a, + .nav-tabs.nav-justified > .active > a:hover, + .nav-tabs.nav-justified > .active > a:focus { + border-bottom-color: #272b30; + } +} +.nav-pills > li { + float: left; +} +.nav-pills > li > a { + border-radius: 4px; +} +.nav-pills > li + li { + margin-left: 2px; +} +.nav-pills > li.active > a, +.nav-pills > li.active > a:hover, +.nav-pills > li.active > a:focus { + color: #ffffff; + background-color: transparent; +} +.nav-stacked > li { + float: none; +} +.nav-stacked > li + li { + margin-top: 2px; + margin-left: 0; +} +.nav-justified { + width: 100%; +} +.nav-justified > li { + float: none; +} +.nav-justified > li > a { + text-align: center; + margin-bottom: 5px; +} +.nav-justified > .dropdown .dropdown-menu { + top: auto; + left: auto; +} +@media (min-width: 768px) { + .nav-justified > li { + display: table-cell; + width: 1%; + } + .nav-justified > li > a { + margin-bottom: 0; + } +} +.nav-tabs-justified { + border-bottom: 0; +} +.nav-tabs-justified > li > a { + margin-right: 0; + border-radius: 4px; +} +.nav-tabs-justified > .active > a, +.nav-tabs-justified > .active > a:hover, +.nav-tabs-justified > .active > a:focus { + border: 1px solid #1c1e22; +} +@media (min-width: 768px) { + .nav-tabs-justified > li > a { + border-bottom: 1px solid #1c1e22; + border-radius: 4px 4px 0 0; + } + .nav-tabs-justified > .active > a, + .nav-tabs-justified > .active > a:hover, + .nav-tabs-justified > .active > a:focus { + border-bottom-color: #272b30; + } +} +.tab-content > .tab-pane { + display: none; +} +.tab-content > .active { + display: block; +} +.nav-tabs .dropdown-menu { + margin-top: -1px; + border-top-right-radius: 0; + border-top-left-radius: 0; +} +.navbar { + position: relative; + min-height: 50px; + margin-bottom: 20px; + border: 1px solid transparent; +} +@media (min-width: 768px) { + .navbar { + border-radius: 4px; + } +} +@media (min-width: 768px) { + .navbar-header { + float: left; + } +} +.navbar-collapse { + overflow-x: visible; + padding-right: 15px; + padding-left: 15px; + border-top: 1px solid transparent; + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1); + -webkit-overflow-scrolling: touch; +} +.navbar-collapse.in { + overflow-y: auto; +} +@media (min-width: 768px) { + .navbar-collapse { + width: auto; + border-top: 0; + -webkit-box-shadow: none; + box-shadow: none; + } + .navbar-collapse.collapse { + display: block !important; + height: auto !important; + padding-bottom: 0; + overflow: visible !important; + } + .navbar-collapse.in { + overflow-y: visible; + } + .navbar-fixed-top .navbar-collapse, + .navbar-static-top .navbar-collapse, + .navbar-fixed-bottom .navbar-collapse { + padding-left: 0; + padding-right: 0; + } +} +.navbar-fixed-top .navbar-collapse, +.navbar-fixed-bottom .navbar-collapse { + max-height: 340px; +} +@media (max-device-width: 480px) and (orientation: landscape) { + .navbar-fixed-top .navbar-collapse, + .navbar-fixed-bottom .navbar-collapse { + max-height: 200px; + } +} +.container > .navbar-header, +.container-fluid > .navbar-header, +.container > .navbar-collapse, +.container-fluid > .navbar-collapse { + margin-right: -15px; + margin-left: -15px; +} +@media (min-width: 768px) { + .container > .navbar-header, + .container-fluid > .navbar-header, + .container > .navbar-collapse, + .container-fluid > .navbar-collapse { + margin-right: 0; + margin-left: 0; + } +} +.navbar-static-top { + z-index: 1000; + border-width: 0 0 1px; +} +@media (min-width: 768px) { + .navbar-static-top { + border-radius: 0; + } +} +.navbar-fixed-top, +.navbar-fixed-bottom { + position: fixed; + right: 0; + left: 0; + z-index: 1030; +} +@media (min-width: 768px) { + .navbar-fixed-top, + .navbar-fixed-bottom { + border-radius: 0; + } +} +.navbar-fixed-top { + top: 0; + border-width: 0 0 1px; +} +.navbar-fixed-bottom { + bottom: 0; + margin-bottom: 0; + border-width: 1px 0 0; +} +.navbar-brand { + float: left; + padding: 15px 15px; + font-size: 18px; + line-height: 20px; + height: 50px; +} +.navbar-brand:hover, +.navbar-brand:focus { + text-decoration: none; +} +.navbar-brand > img { + display: block; +} +@media (min-width: 768px) { + .navbar > .container .navbar-brand, + .navbar > .container-fluid .navbar-brand { + margin-left: -15px; + } +} +.navbar-toggle { + position: relative; + float: right; + margin-right: 15px; + padding: 9px 10px; + margin-top: 8px; + margin-bottom: 8px; + background-color: transparent; + background-image: none; + border: 1px solid transparent; + border-radius: 4px; +} +.navbar-toggle:focus { + outline: 0; +} +.navbar-toggle .icon-bar { + display: block; + width: 22px; + height: 2px; + border-radius: 1px; +} +.navbar-toggle .icon-bar + .icon-bar { + margin-top: 4px; +} +@media (min-width: 768px) { + .navbar-toggle { + display: none; + } +} +.navbar-nav { + margin: 7.5px -15px; +} +.navbar-nav > li > a { + padding-top: 10px; + padding-bottom: 10px; + line-height: 20px; +} +@media (max-width: 767px) { + .navbar-nav .open .dropdown-menu { + position: static; + float: none; + width: auto; + margin-top: 0; + background-color: transparent; + border: 0; + -webkit-box-shadow: none; + box-shadow: none; + } + .navbar-nav .open .dropdown-menu > li > a, + .navbar-nav .open .dropdown-menu .dropdown-header { + padding: 5px 15px 5px 25px; + } + .navbar-nav .open .dropdown-menu > li > a { + line-height: 20px; + } + .navbar-nav .open .dropdown-menu > li > a:hover, + .navbar-nav .open .dropdown-menu > li > a:focus { + background-image: none; + } +} +@media (min-width: 768px) { + .navbar-nav { + float: left; + margin: 0; + } + .navbar-nav > li { + float: left; + } + .navbar-nav > li > a { + padding-top: 15px; + padding-bottom: 15px; + } +} +.navbar-form { + margin-left: -15px; + margin-right: -15px; + padding: 10px 15px; + border-top: 1px solid transparent; + border-bottom: 1px solid transparent; + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); + margin-top: 6px; + margin-bottom: 6px; +} +@media (min-width: 768px) { + .navbar-form .form-group { + display: inline-block; + margin-bottom: 0; + vertical-align: middle; + } + .navbar-form .form-control { + display: inline-block; + width: auto; + vertical-align: middle; + } + .navbar-form .form-control-static { + display: inline-block; + } + .navbar-form .input-group { + display: inline-table; + vertical-align: middle; + } + .navbar-form .input-group .input-group-addon, + .navbar-form .input-group .input-group-btn, + .navbar-form .input-group .form-control { + width: auto; + } + .navbar-form .input-group > .form-control { + width: 100%; + } + .navbar-form .control-label { + margin-bottom: 0; + vertical-align: middle; + } + .navbar-form .radio, + .navbar-form .checkbox { + display: inline-block; + margin-top: 0; + margin-bottom: 0; + vertical-align: middle; + } + .navbar-form .radio label, + .navbar-form .checkbox label { + padding-left: 0; + } + .navbar-form .radio input[type="radio"], + .navbar-form .checkbox input[type="checkbox"] { + position: relative; + margin-left: 0; + } + .navbar-form .has-feedback .form-control-feedback { + top: 0; + } +} +@media (max-width: 767px) { + .navbar-form .form-group { + margin-bottom: 5px; + } + .navbar-form .form-group:last-child { + margin-bottom: 0; + } +} +@media (min-width: 768px) { + .navbar-form { + width: auto; + border: 0; + margin-left: 0; + margin-right: 0; + padding-top: 0; + padding-bottom: 0; + -webkit-box-shadow: none; + box-shadow: none; + } +} +.navbar-nav > li > .dropdown-menu { + margin-top: 0; + border-top-right-radius: 0; + border-top-left-radius: 0; +} +.navbar-fixed-bottom .navbar-nav > li > .dropdown-menu { + margin-bottom: 0; + border-top-right-radius: 4px; + border-top-left-radius: 4px; + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} +.navbar-btn { + margin-top: 6px; + margin-bottom: 6px; +} +.navbar-btn.btn-sm { + margin-top: 10px; + margin-bottom: 10px; +} +.navbar-btn.btn-xs { + margin-top: 14px; + margin-bottom: 14px; +} +.navbar-text { + margin-top: 15px; + margin-bottom: 15px; +} +@media (min-width: 768px) { + .navbar-text { + float: left; + margin-left: 15px; + margin-right: 15px; + } +} +@media (min-width: 768px) { + .navbar-left { + float: left !important; + } + .navbar-right { + float: right !important; + margin-right: -15px; + } + .navbar-right ~ .navbar-right { + margin-right: 0; + } +} +.navbar-default { + background-color: #3a3f44; + border-color: #2b2e32; +} +.navbar-default .navbar-brand { + color: #c8c8c8; +} +.navbar-default .navbar-brand:hover, +.navbar-default .navbar-brand:focus { + color: #ffffff; + background-color: none; +} +.navbar-default .navbar-text { + color: #c8c8c8; +} +.navbar-default .navbar-nav > li > a { + color: #c8c8c8; +} +.navbar-default .navbar-nav > li > a:hover, +.navbar-default .navbar-nav > li > a:focus { + color: #ffffff; + background-color: #272b2e; +} +.navbar-default .navbar-nav > .active > a, +.navbar-default .navbar-nav > .active > a:hover, +.navbar-default .navbar-nav > .active > a:focus { + color: #ffffff; + background-color: #272b2e; +} +.navbar-default .navbar-nav > .disabled > a, +.navbar-default .navbar-nav > .disabled > a:hover, +.navbar-default .navbar-nav > .disabled > a:focus { + color: #cccccc; + background-color: transparent; +} +.navbar-default .navbar-toggle { + border-color: #272b2e; +} +.navbar-default .navbar-toggle:hover, +.navbar-default .navbar-toggle:focus { + background-color: #272b2e; +} +.navbar-default .navbar-toggle .icon-bar { + background-color: #c8c8c8; +} +.navbar-default .navbar-collapse, +.navbar-default .navbar-form { + border-color: #2b2e32; +} +.navbar-default .navbar-nav > .open > a, +.navbar-default .navbar-nav > .open > a:hover, +.navbar-default .navbar-nav > .open > a:focus { + background-color: #272b2e; + color: #ffffff; +} +@media (max-width: 767px) { + .navbar-default .navbar-nav .open .dropdown-menu > li > a { + color: #c8c8c8; + } + .navbar-default .navbar-nav .open .dropdown-menu > li > a:hover, + .navbar-default .navbar-nav .open .dropdown-menu > li > a:focus { + color: #ffffff; + background-color: #272b2e; + } + .navbar-default .navbar-nav .open .dropdown-menu > .active > a, + .navbar-default .navbar-nav .open .dropdown-menu > .active > a:hover, + .navbar-default .navbar-nav .open .dropdown-menu > .active > a:focus { + color: #ffffff; + background-color: #272b2e; + } + .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a, + .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:hover, + .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:focus { + color: #cccccc; + background-color: transparent; + } +} +.navbar-default .navbar-link { + color: #c8c8c8; +} +.navbar-default .navbar-link:hover { + color: #ffffff; +} +.navbar-default .btn-link { + color: #c8c8c8; +} +.navbar-default .btn-link:hover, +.navbar-default .btn-link:focus { + color: #ffffff; +} +.navbar-default .btn-link[disabled]:hover, +fieldset[disabled] .navbar-default .btn-link:hover, +.navbar-default .btn-link[disabled]:focus, +fieldset[disabled] .navbar-default .btn-link:focus { + color: #cccccc; +} +.navbar-inverse { + background-color: #7a8288; + border-color: #62686d; +} +.navbar-inverse .navbar-brand { + color: #cccccc; +} +.navbar-inverse .navbar-brand:hover, +.navbar-inverse .navbar-brand:focus { + color: #ffffff; + background-color: none; +} +.navbar-inverse .navbar-text { + color: #cccccc; +} +.navbar-inverse .navbar-nav > li > a { + color: #cccccc; +} +.navbar-inverse .navbar-nav > li > a:hover, +.navbar-inverse .navbar-nav > li > a:focus { + color: #ffffff; + background-color: #5d6368; +} +.navbar-inverse .navbar-nav > .active > a, +.navbar-inverse .navbar-nav > .active > a:hover, +.navbar-inverse .navbar-nav > .active > a:focus { + color: #ffffff; + background-color: #5d6368; +} +.navbar-inverse .navbar-nav > .disabled > a, +.navbar-inverse .navbar-nav > .disabled > a:hover, +.navbar-inverse .navbar-nav > .disabled > a:focus { + color: #cccccc; + background-color: transparent; +} +.navbar-inverse .navbar-toggle { + border-color: #5d6368; +} +.navbar-inverse .navbar-toggle:hover, +.navbar-inverse .navbar-toggle:focus { + background-color: #5d6368; +} +.navbar-inverse .navbar-toggle .icon-bar { + background-color: #ffffff; +} +.navbar-inverse .navbar-collapse, +.navbar-inverse .navbar-form { + border-color: #697075; +} +.navbar-inverse .navbar-nav > .open > a, +.navbar-inverse .navbar-nav > .open > a:hover, +.navbar-inverse .navbar-nav > .open > a:focus { + background-color: #5d6368; + color: #ffffff; +} +@media (max-width: 767px) { + .navbar-inverse .navbar-nav .open .dropdown-menu > .dropdown-header { + border-color: #62686d; + } + .navbar-inverse .navbar-nav .open .dropdown-menu .divider { + background-color: #62686d; + } + .navbar-inverse .navbar-nav .open .dropdown-menu > li > a { + color: #cccccc; + } + .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:hover, + .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:focus { + color: #ffffff; + background-color: #5d6368; + } + .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a, + .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:hover, + .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:focus { + color: #ffffff; + background-color: #5d6368; + } + .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a, + .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:hover, + .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:focus { + color: #cccccc; + background-color: transparent; + } +} +.navbar-inverse .navbar-link { + color: #cccccc; +} +.navbar-inverse .navbar-link:hover { + color: #ffffff; +} +.navbar-inverse .btn-link { + color: #cccccc; +} +.navbar-inverse .btn-link:hover, +.navbar-inverse .btn-link:focus { + color: #ffffff; +} +.navbar-inverse .btn-link[disabled]:hover, +fieldset[disabled] .navbar-inverse .btn-link:hover, +.navbar-inverse .btn-link[disabled]:focus, +fieldset[disabled] .navbar-inverse .btn-link:focus { + color: #cccccc; +} +.breadcrumb { + padding: 8px 15px; + margin-bottom: 20px; + list-style: none; + background-color: transparent; + border-radius: 4px; +} +.breadcrumb > li { + display: inline-block; +} +.breadcrumb > li + li:before { + content: "/\00a0"; + padding: 0 5px; + color: #cccccc; +} +.breadcrumb > .active { + color: #7a8288; +} +.pagination { + display: inline-block; + padding-left: 0; + margin: 20px 0; + border-radius: 4px; +} +.pagination > li { + display: inline; +} +.pagination > li > a, +.pagination > li > span { + position: relative; + float: left; + padding: 8px 12px; + line-height: 1.42857143; + text-decoration: none; + color: #ffffff; + background-color: #3a3f44; + border: 1px solid rgba(0, 0, 0, 0.6); + margin-left: -1px; +} +.pagination > li:first-child > a, +.pagination > li:first-child > span { + margin-left: 0; + border-bottom-left-radius: 4px; + border-top-left-radius: 4px; +} +.pagination > li:last-child > a, +.pagination > li:last-child > span { + border-bottom-right-radius: 4px; + border-top-right-radius: 4px; +} +.pagination > li > a:hover, +.pagination > li > span:hover, +.pagination > li > a:focus, +.pagination > li > span:focus { + z-index: 2; + color: #ffffff; + background-color: transparent; + border-color: rgba(0, 0, 0, 0.6); +} +.pagination > .active > a, +.pagination > .active > span, +.pagination > .active > a:hover, +.pagination > .active > span:hover, +.pagination > .active > a:focus, +.pagination > .active > span:focus { + z-index: 3; + color: #ffffff; + background-color: #232628; + border-color: rgba(0, 0, 0, 0.6); + cursor: default; +} +.pagination > .disabled > span, +.pagination > .disabled > span:hover, +.pagination > .disabled > span:focus, +.pagination > .disabled > a, +.pagination > .disabled > a:hover, +.pagination > .disabled > a:focus { + color: #7a8288; + background-color: #ffffff; + border-color: rgba(0, 0, 0, 0.6); + cursor: not-allowed; +} +.pagination-lg > li > a, +.pagination-lg > li > span { + padding: 14px 16px; + font-size: 18px; + line-height: 1.3333333; +} +.pagination-lg > li:first-child > a, +.pagination-lg > li:first-child > span { + border-bottom-left-radius: 6px; + border-top-left-radius: 6px; +} +.pagination-lg > li:last-child > a, +.pagination-lg > li:last-child > span { + border-bottom-right-radius: 6px; + border-top-right-radius: 6px; +} +.pagination-sm > li > a, +.pagination-sm > li > span { + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; +} +.pagination-sm > li:first-child > a, +.pagination-sm > li:first-child > span { + border-bottom-left-radius: 3px; + border-top-left-radius: 3px; +} +.pagination-sm > li:last-child > a, +.pagination-sm > li:last-child > span { + border-bottom-right-radius: 3px; + border-top-right-radius: 3px; +} +.pager { + padding-left: 0; + margin: 20px 0; + list-style: none; + text-align: center; +} +.pager li { + display: inline; +} +.pager li > a, +.pager li > span { + display: inline-block; + padding: 5px 14px; + background-color: #3a3f44; + border: 1px solid rgba(0, 0, 0, 0.6); + border-radius: 15px; +} +.pager li > a:hover, +.pager li > a:focus { + text-decoration: none; + background-color: transparent; +} +.pager .next > a, +.pager .next > span { + float: right; +} +.pager .previous > a, +.pager .previous > span { + float: left; +} +.pager .disabled > a, +.pager .disabled > a:hover, +.pager .disabled > a:focus, +.pager .disabled > span { + color: #7a8288; + background-color: #3a3f44; + cursor: not-allowed; +} +.label { + display: inline; + padding: .2em .6em .3em; + font-size: 75%; + font-weight: bold; + line-height: 1; + color: #ffffff; + text-align: center; + white-space: nowrap; + vertical-align: baseline; + border-radius: .25em; +} +a.label:hover, +a.label:focus { + color: #ffffff; + text-decoration: none; + cursor: pointer; +} +.label:empty { + display: none; +} +.btn .label { + position: relative; + top: -1px; +} +.label-default { + background-color: #3a3f44; +} +.label-default[href]:hover, +.label-default[href]:focus { + background-color: #232628; +} +.label-primary { + background-color: #7a8288; +} +.label-primary[href]:hover, +.label-primary[href]:focus { + background-color: #62686d; +} +.label-success { + background-color: #62c462; +} +.label-success[href]:hover, +.label-success[href]:focus { + background-color: #42b142; +} +.label-info { + background-color: #5bc0de; +} +.label-info[href]:hover, +.label-info[href]:focus { + background-color: #31b0d5; +} +.label-warning { + background-color: #f89406; +} +.label-warning[href]:hover, +.label-warning[href]:focus { + background-color: #c67605; +} +.label-danger { + background-color: #ee5f5b; +} +.label-danger[href]:hover, +.label-danger[href]:focus { + background-color: #e9322d; +} +.badge { + display: inline-block; + min-width: 10px; + padding: 3px 7px; + font-size: 12px; + font-weight: bold; + color: #ffffff; + line-height: 1; + vertical-align: middle; + white-space: nowrap; + text-align: center; + background-color: #7a8288; + border-radius: 10px; +} +.badge:empty { + display: none; +} +.btn .badge { + position: relative; + top: -1px; +} +.btn-xs .badge, +.btn-group-xs > .btn .badge { + top: 0; + padding: 1px 5px; +} +a.badge:hover, +a.badge:focus { + color: #ffffff; + text-decoration: none; + cursor: pointer; +} +.list-group-item.active > .badge, +.nav-pills > .active > a > .badge { + color: #ffffff; + background-color: #7a8288; +} +.list-group-item > .badge { + float: right; +} +.list-group-item > .badge + .badge { + margin-right: 5px; +} +.nav-pills > li > a > .badge { + margin-left: 3px; +} +.jumbotron { + padding-top: 30px; + padding-bottom: 30px; + margin-bottom: 30px; + color: inherit; + background-color: #1c1e22; +} +.jumbotron h1, +.jumbotron .h1 { + color: inherit; +} +.jumbotron p { + margin-bottom: 15px; + font-size: 21px; + font-weight: 200; +} +.jumbotron > hr { + border-top-color: #050506; +} +.container .jumbotron, +.container-fluid .jumbotron { + border-radius: 6px; + padding-left: 15px; + padding-right: 15px; +} +.jumbotron .container { + max-width: 100%; +} +@media screen and (min-width: 768px) { + .jumbotron { + padding-top: 48px; + padding-bottom: 48px; + } + .container .jumbotron, + .container-fluid .jumbotron { + padding-left: 60px; + padding-right: 60px; + } + .jumbotron h1, + .jumbotron .h1 { + font-size: 63px; + } +} +.thumbnail { + display: block; + padding: 4px; + margin-bottom: 20px; + line-height: 1.42857143; + background-color: #1c1e22; + border: 1px solid #0c0d0e; + border-radius: 4px; + -webkit-transition: border 0.2s ease-in-out; + -o-transition: border 0.2s ease-in-out; + transition: border 0.2s ease-in-out; +} +.thumbnail > img, +.thumbnail a > img { + margin-left: auto; + margin-right: auto; +} +a.thumbnail:hover, +a.thumbnail:focus, +a.thumbnail.active { + border-color: #ffffff; +} +.thumbnail .caption { + padding: 9px; + color: #c8c8c8; +} +.alert { + padding: 15px; + margin-bottom: 20px; + border: 1px solid transparent; + border-radius: 4px; +} +.alert h4 { + margin-top: 0; + color: inherit; +} +.alert .alert-link { + font-weight: bold; +} +.alert > p, +.alert > ul { + margin-bottom: 0; +} +.alert > p + p { + margin-top: 5px; +} +.alert-dismissable, +.alert-dismissible { + padding-right: 35px; +} +.alert-dismissable .close, +.alert-dismissible .close { + position: relative; + top: -2px; + right: -21px; + color: inherit; +} +.alert-success { + background-color: #62c462; + border-color: #62bd4f; + color: #ffffff; +} +.alert-success hr { + border-top-color: #55b142; +} +.alert-success .alert-link { + color: #e6e6e6; +} +.alert-info { + background-color: #5bc0de; + border-color: #3dced8; + color: #ffffff; +} +.alert-info hr { + border-top-color: #2ac7d2; +} +.alert-info .alert-link { + color: #e6e6e6; +} +.alert-warning { + background-color: #f89406; + border-color: #e96506; + color: #ffffff; +} +.alert-warning hr { + border-top-color: #d05a05; +} +.alert-warning .alert-link { + color: #e6e6e6; +} +.alert-danger { + background-color: #ee5f5b; + border-color: #ed4d63; + color: #ffffff; +} +.alert-danger hr { + border-top-color: #ea364f; +} +.alert-danger .alert-link { + color: #e6e6e6; +} +@-webkit-keyframes progress-bar-stripes { + from { + background-position: 40px 0; + } + to { + background-position: 0 0; + } +} +@-o-keyframes progress-bar-stripes { + from { + background-position: 40px 0; + } + to { + background-position: 0 0; + } +} +@keyframes progress-bar-stripes { + from { + background-position: 40px 0; + } + to { + background-position: 0 0; + } +} +.progress { + overflow: hidden; + height: 20px; + margin-bottom: 20px; + background-color: #1c1e22; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); + box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); +} +.progress-bar { + float: left; + width: 0%; + height: 100%; + font-size: 12px; + line-height: 20px; + color: #ffffff; + text-align: center; + background-color: #7a8288; + -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); + box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); + -webkit-transition: width 0.6s ease; + -o-transition: width 0.6s ease; + transition: width 0.6s ease; +} +.progress-striped .progress-bar, +.progress-bar-striped { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + -webkit-background-size: 40px 40px; + background-size: 40px 40px; +} +.progress.active .progress-bar, +.progress-bar.active { + -webkit-animation: progress-bar-stripes 2s linear infinite; + -o-animation: progress-bar-stripes 2s linear infinite; + animation: progress-bar-stripes 2s linear infinite; +} +.progress-bar-success { + background-color: #62c462; +} +.progress-striped .progress-bar-success { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); +} +.progress-bar-info { + background-color: #5bc0de; +} +.progress-striped .progress-bar-info { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); +} +.progress-bar-warning { + background-color: #f89406; +} +.progress-striped .progress-bar-warning { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); +} +.progress-bar-danger { + background-color: #ee5f5b; +} +.progress-striped .progress-bar-danger { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); +} +.media { + margin-top: 15px; +} +.media:first-child { + margin-top: 0; +} +.media, +.media-body { + zoom: 1; + overflow: hidden; +} +.media-body { + width: 10000px; +} +.media-object { + display: block; +} +.media-object.img-thumbnail { + max-width: none; +} +.media-right, +.media > .pull-right { + padding-left: 10px; +} +.media-left, +.media > .pull-left { + padding-right: 10px; +} +.media-left, +.media-right, +.media-body { + display: table-cell; + vertical-align: top; +} +.media-middle { + vertical-align: middle; +} +.media-bottom { + vertical-align: bottom; +} +.media-heading { + margin-top: 0; + margin-bottom: 5px; +} +.media-list { + padding-left: 0; + list-style: none; +} +.list-group { + margin-bottom: 20px; + padding-left: 0; +} +.list-group-item { + position: relative; + display: block; + padding: 10px 15px; + margin-bottom: -1px; + background-color: #32383e; + border: 1px solid rgba(0, 0, 0, 0.6); +} +.list-group-item:first-child { + border-top-right-radius: 4px; + border-top-left-radius: 4px; +} +.list-group-item:last-child { + margin-bottom: 0; + border-bottom-right-radius: 4px; + border-bottom-left-radius: 4px; +} +a.list-group-item, +button.list-group-item { + color: #c8c8c8; +} +a.list-group-item .list-group-item-heading, +button.list-group-item .list-group-item-heading { + color: #ffffff; +} +a.list-group-item:hover, +button.list-group-item:hover, +a.list-group-item:focus, +button.list-group-item:focus { + text-decoration: none; + color: #c8c8c8; + background-color: #3e444c; +} +button.list-group-item { + width: 100%; + text-align: left; +} +.list-group-item.disabled, +.list-group-item.disabled:hover, +.list-group-item.disabled:focus { + background-color: #999999; + color: #7a8288; + cursor: not-allowed; +} +.list-group-item.disabled .list-group-item-heading, +.list-group-item.disabled:hover .list-group-item-heading, +.list-group-item.disabled:focus .list-group-item-heading { + color: inherit; +} +.list-group-item.disabled .list-group-item-text, +.list-group-item.disabled:hover .list-group-item-text, +.list-group-item.disabled:focus .list-group-item-text { + color: #7a8288; +} +.list-group-item.active, +.list-group-item.active:hover, +.list-group-item.active:focus { + z-index: 2; + color: #ffffff; + background-color: #3e444c; + border-color: rgba(0, 0, 0, 0.6); +} +.list-group-item.active .list-group-item-heading, +.list-group-item.active:hover .list-group-item-heading, +.list-group-item.active:focus .list-group-item-heading, +.list-group-item.active .list-group-item-heading > small, +.list-group-item.active:hover .list-group-item-heading > small, +.list-group-item.active:focus .list-group-item-heading > small, +.list-group-item.active .list-group-item-heading > .small, +.list-group-item.active:hover .list-group-item-heading > .small, +.list-group-item.active:focus .list-group-item-heading > .small { + color: inherit; +} +.list-group-item.active .list-group-item-text, +.list-group-item.active:hover .list-group-item-text, +.list-group-item.active:focus .list-group-item-text { + color: #a2aab4; +} +.list-group-item-success { + color: #ffffff; + background-color: #62c462; +} +a.list-group-item-success, +button.list-group-item-success { + color: #ffffff; +} +a.list-group-item-success .list-group-item-heading, +button.list-group-item-success .list-group-item-heading { + color: inherit; +} +a.list-group-item-success:hover, +button.list-group-item-success:hover, +a.list-group-item-success:focus, +button.list-group-item-success:focus { + color: #ffffff; + background-color: #4fbd4f; +} +a.list-group-item-success.active, +button.list-group-item-success.active, +a.list-group-item-success.active:hover, +button.list-group-item-success.active:hover, +a.list-group-item-success.active:focus, +button.list-group-item-success.active:focus { + color: #fff; + background-color: #ffffff; + border-color: #ffffff; +} +.list-group-item-info { + color: #ffffff; + background-color: #5bc0de; +} +a.list-group-item-info, +button.list-group-item-info { + color: #ffffff; +} +a.list-group-item-info .list-group-item-heading, +button.list-group-item-info .list-group-item-heading { + color: inherit; +} +a.list-group-item-info:hover, +button.list-group-item-info:hover, +a.list-group-item-info:focus, +button.list-group-item-info:focus { + color: #ffffff; + background-color: #46b8da; +} +a.list-group-item-info.active, +button.list-group-item-info.active, +a.list-group-item-info.active:hover, +button.list-group-item-info.active:hover, +a.list-group-item-info.active:focus, +button.list-group-item-info.active:focus { + color: #fff; + background-color: #ffffff; + border-color: #ffffff; +} +.list-group-item-warning { + color: #ffffff; + background-color: #f89406; +} +a.list-group-item-warning, +button.list-group-item-warning { + color: #ffffff; +} +a.list-group-item-warning .list-group-item-heading, +button.list-group-item-warning .list-group-item-heading { + color: inherit; +} +a.list-group-item-warning:hover, +button.list-group-item-warning:hover, +a.list-group-item-warning:focus, +button.list-group-item-warning:focus { + color: #ffffff; + background-color: #df8505; +} +a.list-group-item-warning.active, +button.list-group-item-warning.active, +a.list-group-item-warning.active:hover, +button.list-group-item-warning.active:hover, +a.list-group-item-warning.active:focus, +button.list-group-item-warning.active:focus { + color: #fff; + background-color: #ffffff; + border-color: #ffffff; +} +.list-group-item-danger { + color: #ffffff; + background-color: #ee5f5b; +} +a.list-group-item-danger, +button.list-group-item-danger { + color: #ffffff; +} +a.list-group-item-danger .list-group-item-heading, +button.list-group-item-danger .list-group-item-heading { + color: inherit; +} +a.list-group-item-danger:hover, +button.list-group-item-danger:hover, +a.list-group-item-danger:focus, +button.list-group-item-danger:focus { + color: #ffffff; + background-color: #ec4844; +} +a.list-group-item-danger.active, +button.list-group-item-danger.active, +a.list-group-item-danger.active:hover, +button.list-group-item-danger.active:hover, +a.list-group-item-danger.active:focus, +button.list-group-item-danger.active:focus { + color: #fff; + background-color: #ffffff; + border-color: #ffffff; +} +.list-group-item-heading { + margin-top: 0; + margin-bottom: 5px; +} +.list-group-item-text { + margin-bottom: 0; + line-height: 1.3; +} +.panel { + margin-bottom: 20px; + background-color: #2e3338; + border: 1px solid transparent; + border-radius: 4px; + -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05); + box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05); +} +.panel-body { + padding: 15px; +} +.panel-heading { + padding: 10px 15px; + border-bottom: 1px solid transparent; + border-top-right-radius: 3px; + border-top-left-radius: 3px; +} +.panel-heading > .dropdown .dropdown-toggle { + color: inherit; +} +.panel-title { + margin-top: 0; + margin-bottom: 0; + font-size: 16px; + color: inherit; +} +.panel-title > a, +.panel-title > small, +.panel-title > .small, +.panel-title > small > a, +.panel-title > .small > a { + color: inherit; +} +.panel-footer { + padding: 10px 15px; + background-color: #3e444c; + border-top: 1px solid rgba(0, 0, 0, 0.6); + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px; +} +.panel > .list-group, +.panel > .panel-collapse > .list-group { + margin-bottom: 0; +} +.panel > .list-group .list-group-item, +.panel > .panel-collapse > .list-group .list-group-item { + border-width: 1px 0; + border-radius: 0; +} +.panel > .list-group:first-child .list-group-item:first-child, +.panel > .panel-collapse > .list-group:first-child .list-group-item:first-child { + border-top: 0; + border-top-right-radius: 3px; + border-top-left-radius: 3px; +} +.panel > .list-group:last-child .list-group-item:last-child, +.panel > .panel-collapse > .list-group:last-child .list-group-item:last-child { + border-bottom: 0; + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px; +} +.panel > .panel-heading + .panel-collapse > .list-group .list-group-item:first-child { + border-top-right-radius: 0; + border-top-left-radius: 0; +} +.panel-heading + .list-group .list-group-item:first-child { + border-top-width: 0; +} +.list-group + .panel-footer { + border-top-width: 0; +} +.panel > .table, +.panel > .table-responsive > .table, +.panel > .panel-collapse > .table { + margin-bottom: 0; +} +.panel > .table caption, +.panel > .table-responsive > .table caption, +.panel > .panel-collapse > .table caption { + padding-left: 15px; + padding-right: 15px; +} +.panel > .table:first-child, +.panel > .table-responsive:first-child > .table:first-child { + border-top-right-radius: 3px; + border-top-left-radius: 3px; +} +.panel > .table:first-child > thead:first-child > tr:first-child, +.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child, +.panel > .table:first-child > tbody:first-child > tr:first-child, +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child { + border-top-left-radius: 3px; + border-top-right-radius: 3px; +} +.panel > .table:first-child > thead:first-child > tr:first-child td:first-child, +.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:first-child, +.panel > .table:first-child > tbody:first-child > tr:first-child td:first-child, +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:first-child, +.panel > .table:first-child > thead:first-child > tr:first-child th:first-child, +.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:first-child, +.panel > .table:first-child > tbody:first-child > tr:first-child th:first-child, +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:first-child { + border-top-left-radius: 3px; +} +.panel > .table:first-child > thead:first-child > tr:first-child td:last-child, +.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:last-child, +.panel > .table:first-child > tbody:first-child > tr:first-child td:last-child, +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:last-child, +.panel > .table:first-child > thead:first-child > tr:first-child th:last-child, +.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:last-child, +.panel > .table:first-child > tbody:first-child > tr:first-child th:last-child, +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:last-child { + border-top-right-radius: 3px; +} +.panel > .table:last-child, +.panel > .table-responsive:last-child > .table:last-child { + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px; +} +.panel > .table:last-child > tbody:last-child > tr:last-child, +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child, +.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child { + border-bottom-left-radius: 3px; + border-bottom-right-radius: 3px; +} +.panel > .table:last-child > tbody:last-child > tr:last-child td:first-child, +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:first-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child td:first-child, +.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:first-child, +.panel > .table:last-child > tbody:last-child > tr:last-child th:first-child, +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:first-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child th:first-child, +.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:first-child { + border-bottom-left-radius: 3px; +} +.panel > .table:last-child > tbody:last-child > tr:last-child td:last-child, +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:last-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child td:last-child, +.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:last-child, +.panel > .table:last-child > tbody:last-child > tr:last-child th:last-child, +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:last-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child th:last-child, +.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:last-child { + border-bottom-right-radius: 3px; +} +.panel > .panel-body + .table, +.panel > .panel-body + .table-responsive, +.panel > .table + .panel-body, +.panel > .table-responsive + .panel-body { + border-top: 1px solid #1c1e22; +} +.panel > .table > tbody:first-child > tr:first-child th, +.panel > .table > tbody:first-child > tr:first-child td { + border-top: 0; +} +.panel > .table-bordered, +.panel > .table-responsive > .table-bordered { + border: 0; +} +.panel > .table-bordered > thead > tr > th:first-child, +.panel > .table-responsive > .table-bordered > thead > tr > th:first-child, +.panel > .table-bordered > tbody > tr > th:first-child, +.panel > .table-responsive > .table-bordered > tbody > tr > th:first-child, +.panel > .table-bordered > tfoot > tr > th:first-child, +.panel > .table-responsive > .table-bordered > tfoot > tr > th:first-child, +.panel > .table-bordered > thead > tr > td:first-child, +.panel > .table-responsive > .table-bordered > thead > tr > td:first-child, +.panel > .table-bordered > tbody > tr > td:first-child, +.panel > .table-responsive > .table-bordered > tbody > tr > td:first-child, +.panel > .table-bordered > tfoot > tr > td:first-child, +.panel > .table-responsive > .table-bordered > tfoot > tr > td:first-child { + border-left: 0; +} +.panel > .table-bordered > thead > tr > th:last-child, +.panel > .table-responsive > .table-bordered > thead > tr > th:last-child, +.panel > .table-bordered > tbody > tr > th:last-child, +.panel > .table-responsive > .table-bordered > tbody > tr > th:last-child, +.panel > .table-bordered > tfoot > tr > th:last-child, +.panel > .table-responsive > .table-bordered > tfoot > tr > th:last-child, +.panel > .table-bordered > thead > tr > td:last-child, +.panel > .table-responsive > .table-bordered > thead > tr > td:last-child, +.panel > .table-bordered > tbody > tr > td:last-child, +.panel > .table-responsive > .table-bordered > tbody > tr > td:last-child, +.panel > .table-bordered > tfoot > tr > td:last-child, +.panel > .table-responsive > .table-bordered > tfoot > tr > td:last-child { + border-right: 0; +} +.panel > .table-bordered > thead > tr:first-child > td, +.panel > .table-responsive > .table-bordered > thead > tr:first-child > td, +.panel > .table-bordered > tbody > tr:first-child > td, +.panel > .table-responsive > .table-bordered > tbody > tr:first-child > td, +.panel > .table-bordered > thead > tr:first-child > th, +.panel > .table-responsive > .table-bordered > thead > tr:first-child > th, +.panel > .table-bordered > tbody > tr:first-child > th, +.panel > .table-responsive > .table-bordered > tbody > tr:first-child > th { + border-bottom: 0; +} +.panel > .table-bordered > tbody > tr:last-child > td, +.panel > .table-responsive > .table-bordered > tbody > tr:last-child > td, +.panel > .table-bordered > tfoot > tr:last-child > td, +.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > td, +.panel > .table-bordered > tbody > tr:last-child > th, +.panel > .table-responsive > .table-bordered > tbody > tr:last-child > th, +.panel > .table-bordered > tfoot > tr:last-child > th, +.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > th { + border-bottom: 0; +} +.panel > .table-responsive { + border: 0; + margin-bottom: 0; +} +.panel-group { + margin-bottom: 20px; +} +.panel-group .panel { + margin-bottom: 0; + border-radius: 4px; +} +.panel-group .panel + .panel { + margin-top: 5px; +} +.panel-group .panel-heading { + border-bottom: 0; +} +.panel-group .panel-heading + .panel-collapse > .panel-body, +.panel-group .panel-heading + .panel-collapse > .list-group { + border-top: 1px solid rgba(0, 0, 0, 0.6); +} +.panel-group .panel-footer { + border-top: 0; +} +.panel-group .panel-footer + .panel-collapse .panel-body { + border-bottom: 1px solid rgba(0, 0, 0, 0.6); +} +.panel-default { + border-color: rgba(0, 0, 0, 0.6); +} +.panel-default > .panel-heading { + color: #c8c8c8; + background-color: #3e444c; + border-color: rgba(0, 0, 0, 0.6); +} +.panel-default > .panel-heading + .panel-collapse > .panel-body { + border-top-color: rgba(0, 0, 0, 0.6); +} +.panel-default > .panel-heading .badge { + color: #3e444c; + background-color: #c8c8c8; +} +.panel-default > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: rgba(0, 0, 0, 0.6); +} +.panel-primary { + border-color: rgba(0, 0, 0, 0.6); +} +.panel-primary > .panel-heading { + color: #ffffff; + background-color: #7a8288; + border-color: rgba(0, 0, 0, 0.6); +} +.panel-primary > .panel-heading + .panel-collapse > .panel-body { + border-top-color: rgba(0, 0, 0, 0.6); +} +.panel-primary > .panel-heading .badge { + color: #7a8288; + background-color: #ffffff; +} +.panel-primary > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: rgba(0, 0, 0, 0.6); +} +.panel-success { + border-color: rgba(0, 0, 0, 0.6); +} +.panel-success > .panel-heading { + color: #ffffff; + background-color: #62c462; + border-color: rgba(0, 0, 0, 0.6); +} +.panel-success > .panel-heading + .panel-collapse > .panel-body { + border-top-color: rgba(0, 0, 0, 0.6); +} +.panel-success > .panel-heading .badge { + color: #62c462; + background-color: #ffffff; +} +.panel-success > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: rgba(0, 0, 0, 0.6); +} +.panel-info { + border-color: rgba(0, 0, 0, 0.6); +} +.panel-info > .panel-heading { + color: #ffffff; + background-color: #5bc0de; + border-color: rgba(0, 0, 0, 0.6); +} +.panel-info > .panel-heading + .panel-collapse > .panel-body { + border-top-color: rgba(0, 0, 0, 0.6); +} +.panel-info > .panel-heading .badge { + color: #5bc0de; + background-color: #ffffff; +} +.panel-info > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: rgba(0, 0, 0, 0.6); +} +.panel-warning { + border-color: rgba(0, 0, 0, 0.6); +} +.panel-warning > .panel-heading { + color: #ffffff; + background-color: #f89406; + border-color: rgba(0, 0, 0, 0.6); +} +.panel-warning > .panel-heading + .panel-collapse > .panel-body { + border-top-color: rgba(0, 0, 0, 0.6); +} +.panel-warning > .panel-heading .badge { + color: #f89406; + background-color: #ffffff; +} +.panel-warning > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: rgba(0, 0, 0, 0.6); +} +.panel-danger { + border-color: rgba(0, 0, 0, 0.6); +} +.panel-danger > .panel-heading { + color: #ffffff; + background-color: #ee5f5b; + border-color: rgba(0, 0, 0, 0.6); +} +.panel-danger > .panel-heading + .panel-collapse > .panel-body { + border-top-color: rgba(0, 0, 0, 0.6); +} +.panel-danger > .panel-heading .badge { + color: #ee5f5b; + background-color: #ffffff; +} +.panel-danger > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: rgba(0, 0, 0, 0.6); +} +.embed-responsive { + position: relative; + display: block; + height: 0; + padding: 0; + overflow: hidden; +} +.embed-responsive .embed-responsive-item, +.embed-responsive iframe, +.embed-responsive embed, +.embed-responsive object, +.embed-responsive video { + position: absolute; + top: 0; + left: 0; + bottom: 0; + height: 100%; + width: 100%; + border: 0; +} +.embed-responsive-16by9 { + padding-bottom: 56.25%; +} +.embed-responsive-4by3 { + padding-bottom: 75%; +} +.well { + min-height: 20px; + padding: 19px; + margin-bottom: 20px; + background-color: #1c1e22; + border: 1px solid #0c0d0e; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); +} +.well blockquote { + border-color: #ddd; + border-color: rgba(0, 0, 0, 0.15); +} +.well-lg { + padding: 24px; + border-radius: 6px; +} +.well-sm { + padding: 9px; + border-radius: 3px; +} +.close { + float: right; + font-size: 21px; + font-weight: bold; + line-height: 1; + color: #000000; + text-shadow: 0 1px 0 #ffffff; + opacity: 0.2; + filter: alpha(opacity=20); +} +.close:hover, +.close:focus { + color: #000000; + text-decoration: none; + cursor: pointer; + opacity: 0.5; + filter: alpha(opacity=50); +} +button.close { + padding: 0; + cursor: pointer; + background: transparent; + border: 0; + -webkit-appearance: none; +} +.modal-open { + overflow: hidden; +} +.modal { + display: none; + overflow: hidden; + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 1050; + -webkit-overflow-scrolling: touch; + outline: 0; +} +.modal.fade .modal-dialog { + -webkit-transform: translate(0, -25%); + -ms-transform: translate(0, -25%); + -o-transform: translate(0, -25%); + transform: translate(0, -25%); + -webkit-transition: -webkit-transform 0.3s ease-out; + -o-transition: -o-transform 0.3s ease-out; + transition: transform 0.3s ease-out; +} +.modal.in .modal-dialog { + -webkit-transform: translate(0, 0); + -ms-transform: translate(0, 0); + -o-transform: translate(0, 0); + transform: translate(0, 0); +} +.modal-open .modal { + overflow-x: hidden; + overflow-y: auto; +} +.modal-dialog { + position: relative; + width: auto; + margin: 10px; +} +.modal-content { + position: relative; + background-color: #2e3338; + border: 1px solid #999999; + border: 1px solid rgba(0, 0, 0, 0.2); + border-radius: 6px; + -webkit-box-shadow: 0 3px 9px rgba(0, 0, 0, 0.5); + box-shadow: 0 3px 9px rgba(0, 0, 0, 0.5); + -webkit-background-clip: padding-box; + background-clip: padding-box; + outline: 0; +} +.modal-backdrop { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 1040; + background-color: #000000; +} +.modal-backdrop.fade { + opacity: 0; + filter: alpha(opacity=0); +} +.modal-backdrop.in { + opacity: 0.5; + filter: alpha(opacity=50); +} +.modal-header { + padding: 15px; + border-bottom: 1px solid #1c1e22; +} +.modal-header .close { + margin-top: -2px; +} +.modal-title { + margin: 0; + line-height: 1.42857143; +} +.modal-body { + position: relative; + padding: 20px; +} +.modal-footer { + padding: 20px; + text-align: right; + border-top: 1px solid #1c1e22; +} +.modal-footer .btn + .btn { + margin-left: 5px; + margin-bottom: 0; +} +.modal-footer .btn-group .btn + .btn { + margin-left: -1px; +} +.modal-footer .btn-block + .btn-block { + margin-left: 0; +} +.modal-scrollbar-measure { + position: absolute; + top: -9999px; + width: 50px; + height: 50px; + overflow: scroll; +} +@media (min-width: 768px) { + .modal-dialog { + width: 600px; + margin: 30px auto; + } + .modal-content { + -webkit-box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5); + box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5); + } + .modal-sm { + width: 300px; + } +} +@media (min-width: 992px) { + .modal-lg { + width: 900px; + } +} +.tooltip { + position: absolute; + z-index: 1070; + display: block; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-style: normal; + font-weight: normal; + letter-spacing: normal; + line-break: auto; + line-height: 1.42857143; + text-align: left; + text-align: start; + text-decoration: none; + text-shadow: none; + text-transform: none; + white-space: normal; + word-break: normal; + word-spacing: normal; + word-wrap: normal; + font-size: 12px; + opacity: 0; + filter: alpha(opacity=0); +} +.tooltip.in { + opacity: 0.9; + filter: alpha(opacity=90); +} +.tooltip.top { + margin-top: -3px; + padding: 5px 0; +} +.tooltip.right { + margin-left: 3px; + padding: 0 5px; +} +.tooltip.bottom { + margin-top: 3px; + padding: 5px 0; +} +.tooltip.left { + margin-left: -3px; + padding: 0 5px; +} +.tooltip-inner { + max-width: 200px; + padding: 3px 8px; + color: #ffffff; + text-align: center; + background-color: #000000; + border-radius: 4px; +} +.tooltip-arrow { + position: absolute; + width: 0; + height: 0; + border-color: transparent; + border-style: solid; +} +.tooltip.top .tooltip-arrow { + bottom: 0; + left: 50%; + margin-left: -5px; + border-width: 5px 5px 0; + border-top-color: #000000; +} +.tooltip.top-left .tooltip-arrow { + bottom: 0; + right: 5px; + margin-bottom: -5px; + border-width: 5px 5px 0; + border-top-color: #000000; +} +.tooltip.top-right .tooltip-arrow { + bottom: 0; + left: 5px; + margin-bottom: -5px; + border-width: 5px 5px 0; + border-top-color: #000000; +} +.tooltip.right .tooltip-arrow { + top: 50%; + left: 0; + margin-top: -5px; + border-width: 5px 5px 5px 0; + border-right-color: #000000; +} +.tooltip.left .tooltip-arrow { + top: 50%; + right: 0; + margin-top: -5px; + border-width: 5px 0 5px 5px; + border-left-color: #000000; +} +.tooltip.bottom .tooltip-arrow { + top: 0; + left: 50%; + margin-left: -5px; + border-width: 0 5px 5px; + border-bottom-color: #000000; +} +.tooltip.bottom-left .tooltip-arrow { + top: 0; + right: 5px; + margin-top: -5px; + border-width: 0 5px 5px; + border-bottom-color: #000000; +} +.tooltip.bottom-right .tooltip-arrow { + top: 0; + left: 5px; + margin-top: -5px; + border-width: 0 5px 5px; + border-bottom-color: #000000; +} +.popover { + position: absolute; + top: 0; + left: 0; + z-index: 1060; + display: none; + max-width: 276px; + padding: 1px; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-style: normal; + font-weight: normal; + letter-spacing: normal; + line-break: auto; + line-height: 1.42857143; + text-align: left; + text-align: start; + text-decoration: none; + text-shadow: none; + text-transform: none; + white-space: normal; + word-break: normal; + word-spacing: normal; + word-wrap: normal; + font-size: 14px; + background-color: #2e3338; + -webkit-background-clip: padding-box; + background-clip: padding-box; + border: 1px solid #999999; + border: 1px solid rgba(0, 0, 0, 0.2); + border-radius: 6px; + -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); + box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); +} +.popover.top { + margin-top: -10px; +} +.popover.right { + margin-left: 10px; +} +.popover.bottom { + margin-top: 10px; +} +.popover.left { + margin-left: -10px; +} +.popover-title { + margin: 0; + padding: 8px 14px; + font-size: 14px; + background-color: #2e3338; + border-bottom: 1px solid #22262a; + border-radius: 5px 5px 0 0; +} +.popover-content { + padding: 9px 14px; +} +.popover > .arrow, +.popover > .arrow:after { + position: absolute; + display: block; + width: 0; + height: 0; + border-color: transparent; + border-style: solid; +} +.popover > .arrow { + border-width: 11px; +} +.popover > .arrow:after { + border-width: 10px; + content: ""; +} +.popover.top > .arrow { + left: 50%; + margin-left: -11px; + border-bottom-width: 0; + border-top-color: #666666; + border-top-color: rgba(0, 0, 0, 0.25); + bottom: -11px; +} +.popover.top > .arrow:after { + content: " "; + bottom: 1px; + margin-left: -10px; + border-bottom-width: 0; + border-top-color: #2e3338; +} +.popover.right > .arrow { + top: 50%; + left: -11px; + margin-top: -11px; + border-left-width: 0; + border-right-color: #666666; + border-right-color: rgba(0, 0, 0, 0.25); +} +.popover.right > .arrow:after { + content: " "; + left: 1px; + bottom: -10px; + border-left-width: 0; + border-right-color: #2e3338; +} +.popover.bottom > .arrow { + left: 50%; + margin-left: -11px; + border-top-width: 0; + border-bottom-color: #666666; + border-bottom-color: rgba(0, 0, 0, 0.25); + top: -11px; +} +.popover.bottom > .arrow:after { + content: " "; + top: 1px; + margin-left: -10px; + border-top-width: 0; + border-bottom-color: #2e3338; +} +.popover.left > .arrow { + top: 50%; + right: -11px; + margin-top: -11px; + border-right-width: 0; + border-left-color: #666666; + border-left-color: rgba(0, 0, 0, 0.25); +} +.popover.left > .arrow:after { + content: " "; + right: 1px; + border-right-width: 0; + border-left-color: #2e3338; + bottom: -10px; +} +.carousel { + position: relative; +} +.carousel-inner { + position: relative; + overflow: hidden; + width: 100%; +} +.carousel-inner > .item { + display: none; + position: relative; + -webkit-transition: 0.6s ease-in-out left; + -o-transition: 0.6s ease-in-out left; + transition: 0.6s ease-in-out left; +} +.carousel-inner > .item > img, +.carousel-inner > .item > a > img { + line-height: 1; +} +@media all and (transform-3d), (-webkit-transform-3d) { + .carousel-inner > .item { + -webkit-transition: -webkit-transform 0.6s ease-in-out; + -o-transition: -o-transform 0.6s ease-in-out; + transition: transform 0.6s ease-in-out; + -webkit-backface-visibility: hidden; + backface-visibility: hidden; + -webkit-perspective: 1000px; + perspective: 1000px; + } + .carousel-inner > .item.next, + .carousel-inner > .item.active.right { + -webkit-transform: translate3d(100%, 0, 0); + transform: translate3d(100%, 0, 0); + left: 0; + } + .carousel-inner > .item.prev, + .carousel-inner > .item.active.left { + -webkit-transform: translate3d(-100%, 0, 0); + transform: translate3d(-100%, 0, 0); + left: 0; + } + .carousel-inner > .item.next.left, + .carousel-inner > .item.prev.right, + .carousel-inner > .item.active { + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + left: 0; + } +} +.carousel-inner > .active, +.carousel-inner > .next, +.carousel-inner > .prev { + display: block; +} +.carousel-inner > .active { + left: 0; +} +.carousel-inner > .next, +.carousel-inner > .prev { + position: absolute; + top: 0; + width: 100%; +} +.carousel-inner > .next { + left: 100%; +} +.carousel-inner > .prev { + left: -100%; +} +.carousel-inner > .next.left, +.carousel-inner > .prev.right { + left: 0; +} +.carousel-inner > .active.left { + left: -100%; +} +.carousel-inner > .active.right { + left: 100%; +} +.carousel-control { + position: absolute; + top: 0; + left: 0; + bottom: 0; + width: 15%; + opacity: 0.5; + filter: alpha(opacity=50); + font-size: 20px; + color: #ffffff; + text-align: center; + text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6); + background-color: rgba(0, 0, 0, 0); +} +.carousel-control.left { + background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0.0001) 100%); + background-image: -o-linear-gradient(left, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0.0001) 100%); + background-image: -webkit-gradient(linear, left top, right top, from(rgba(0, 0, 0, 0.5)), to(rgba(0, 0, 0, 0.0001))); + background-image: linear-gradient(to right, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0.0001) 100%); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1); +} +.carousel-control.right { + left: auto; + right: 0; + background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, 0.0001) 0%, rgba(0, 0, 0, 0.5) 100%); + background-image: -o-linear-gradient(left, rgba(0, 0, 0, 0.0001) 0%, rgba(0, 0, 0, 0.5) 100%); + background-image: -webkit-gradient(linear, left top, right top, from(rgba(0, 0, 0, 0.0001)), to(rgba(0, 0, 0, 0.5))); + background-image: linear-gradient(to right, rgba(0, 0, 0, 0.0001) 0%, rgba(0, 0, 0, 0.5) 100%); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1); +} +.carousel-control:hover, +.carousel-control:focus { + outline: 0; + color: #ffffff; + text-decoration: none; + opacity: 0.9; + filter: alpha(opacity=90); +} +.carousel-control .icon-prev, +.carousel-control .icon-next, +.carousel-control .glyphicon-chevron-left, +.carousel-control .glyphicon-chevron-right { + position: absolute; + top: 50%; + margin-top: -10px; + z-index: 5; + display: inline-block; +} +.carousel-control .icon-prev, +.carousel-control .glyphicon-chevron-left { + left: 50%; + margin-left: -10px; +} +.carousel-control .icon-next, +.carousel-control .glyphicon-chevron-right { + right: 50%; + margin-right: -10px; +} +.carousel-control .icon-prev, +.carousel-control .icon-next { + width: 20px; + height: 20px; + line-height: 1; + font-family: serif; +} +.carousel-control .icon-prev:before { + content: '\2039'; +} +.carousel-control .icon-next:before { + content: '\203a'; +} +.carousel-indicators { + position: absolute; + bottom: 10px; + left: 50%; + z-index: 15; + width: 60%; + margin-left: -30%; + padding-left: 0; + list-style: none; + text-align: center; +} +.carousel-indicators li { + display: inline-block; + width: 10px; + height: 10px; + margin: 1px; + text-indent: -999px; + border: 1px solid #ffffff; + border-radius: 10px; + cursor: pointer; + background-color: #000 \9; + background-color: rgba(0, 0, 0, 0); +} +.carousel-indicators .active { + margin: 0; + width: 12px; + height: 12px; + background-color: #ffffff; +} +.carousel-caption { + position: absolute; + left: 15%; + right: 15%; + bottom: 20px; + z-index: 10; + padding-top: 20px; + padding-bottom: 20px; + color: #ffffff; + text-align: center; + text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6); +} +.carousel-caption .btn { + text-shadow: none; +} +@media screen and (min-width: 768px) { + .carousel-control .glyphicon-chevron-left, + .carousel-control .glyphicon-chevron-right, + .carousel-control .icon-prev, + .carousel-control .icon-next { + width: 30px; + height: 30px; + margin-top: -10px; + font-size: 30px; + } + .carousel-control .glyphicon-chevron-left, + .carousel-control .icon-prev { + margin-left: -10px; + } + .carousel-control .glyphicon-chevron-right, + .carousel-control .icon-next { + margin-right: -10px; + } + .carousel-caption { + left: 20%; + right: 20%; + padding-bottom: 30px; + } + .carousel-indicators { + bottom: 20px; + } +} +.clearfix:before, +.clearfix:after, +.dl-horizontal dd:before, +.dl-horizontal dd:after, +.container:before, +.container:after, +.container-fluid:before, +.container-fluid:after, +.row:before, +.row:after, +.form-horizontal .form-group:before, +.form-horizontal .form-group:after, +.btn-toolbar:before, +.btn-toolbar:after, +.btn-group-vertical > .btn-group:before, +.btn-group-vertical > .btn-group:after, +.nav:before, +.nav:after, +.navbar:before, +.navbar:after, +.navbar-header:before, +.navbar-header:after, +.navbar-collapse:before, +.navbar-collapse:after, +.pager:before, +.pager:after, +.panel-body:before, +.panel-body:after, +.modal-header:before, +.modal-header:after, +.modal-footer:before, +.modal-footer:after { + content: " "; + display: table; +} +.clearfix:after, +.dl-horizontal dd:after, +.container:after, +.container-fluid:after, +.row:after, +.form-horizontal .form-group:after, +.btn-toolbar:after, +.btn-group-vertical > .btn-group:after, +.nav:after, +.navbar:after, +.navbar-header:after, +.navbar-collapse:after, +.pager:after, +.panel-body:after, +.modal-header:after, +.modal-footer:after { + clear: both; +} +.center-block { + display: block; + margin-left: auto; + margin-right: auto; +} +.pull-right { + float: right !important; +} +.pull-left { + float: left !important; +} +.hide { + display: none !important; +} +.show { + display: block !important; +} +.invisible { + visibility: hidden; +} +.text-hide { + font: 0/0 a; + color: transparent; + text-shadow: none; + background-color: transparent; + border: 0; +} +.hidden { + display: none !important; +} +.affix { + position: fixed; +} +@-ms-viewport { + width: device-width; +} +.visible-xs, +.visible-sm, +.visible-md, +.visible-lg { + display: none !important; +} +.visible-xs-block, +.visible-xs-inline, +.visible-xs-inline-block, +.visible-sm-block, +.visible-sm-inline, +.visible-sm-inline-block, +.visible-md-block, +.visible-md-inline, +.visible-md-inline-block, +.visible-lg-block, +.visible-lg-inline, +.visible-lg-inline-block { + display: none !important; +} +@media (max-width: 767px) { + .visible-xs { + display: block !important; + } + table.visible-xs { + display: table !important; + } + tr.visible-xs { + display: table-row !important; + } + th.visible-xs, + td.visible-xs { + display: table-cell !important; + } +} +@media (max-width: 767px) { + .visible-xs-block { + display: block !important; + } +} +@media (max-width: 767px) { + .visible-xs-inline { + display: inline !important; + } +} +@media (max-width: 767px) { + .visible-xs-inline-block { + display: inline-block !important; + } +} +@media (min-width: 768px) and (max-width: 991px) { + .visible-sm { + display: block !important; + } + table.visible-sm { + display: table !important; + } + tr.visible-sm { + display: table-row !important; + } + th.visible-sm, + td.visible-sm { + display: table-cell !important; + } +} +@media (min-width: 768px) and (max-width: 991px) { + .visible-sm-block { + display: block !important; + } +} +@media (min-width: 768px) and (max-width: 991px) { + .visible-sm-inline { + display: inline !important; + } +} +@media (min-width: 768px) and (max-width: 991px) { + .visible-sm-inline-block { + display: inline-block !important; + } +} +@media (min-width: 992px) and (max-width: 1199px) { + .visible-md { + display: block !important; + } + table.visible-md { + display: table !important; + } + tr.visible-md { + display: table-row !important; + } + th.visible-md, + td.visible-md { + display: table-cell !important; + } +} +@media (min-width: 992px) and (max-width: 1199px) { + .visible-md-block { + display: block !important; + } +} +@media (min-width: 992px) and (max-width: 1199px) { + .visible-md-inline { + display: inline !important; + } +} +@media (min-width: 992px) and (max-width: 1199px) { + .visible-md-inline-block { + display: inline-block !important; + } +} +@media (min-width: 1200px) { + .visible-lg { + display: block !important; + } + table.visible-lg { + display: table !important; + } + tr.visible-lg { + display: table-row !important; + } + th.visible-lg, + td.visible-lg { + display: table-cell !important; + } +} +@media (min-width: 1200px) { + .visible-lg-block { + display: block !important; + } +} +@media (min-width: 1200px) { + .visible-lg-inline { + display: inline !important; + } +} +@media (min-width: 1200px) { + .visible-lg-inline-block { + display: inline-block !important; + } +} +@media (max-width: 767px) { + .hidden-xs { + display: none !important; + } +} +@media (min-width: 768px) and (max-width: 991px) { + .hidden-sm { + display: none !important; + } +} +@media (min-width: 992px) and (max-width: 1199px) { + .hidden-md { + display: none !important; + } +} +@media (min-width: 1200px) { + .hidden-lg { + display: none !important; + } +} +.visible-print { + display: none !important; +} +@media print { + .visible-print { + display: block !important; + } + table.visible-print { + display: table !important; + } + tr.visible-print { + display: table-row !important; + } + th.visible-print, + td.visible-print { + display: table-cell !important; + } +} +.visible-print-block { + display: none !important; +} +@media print { + .visible-print-block { + display: block !important; + } +} +.visible-print-inline { + display: none !important; +} +@media print { + .visible-print-inline { + display: inline !important; + } +} +.visible-print-inline-block { + display: none !important; +} +@media print { + .visible-print-inline-block { + display: inline-block !important; + } +} +@media print { + .hidden-print { + display: none !important; + } +} +.navbar-default, +.navbar-inverse { + border: 1px solid rgba(0, 0, 0, 0.6); + text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.3); +} +@media (min-width: 768px) { + .navbar-default .navbar-nav > li > a, + .navbar-inverse .navbar-nav > li > a { + border-right: 1px solid rgba(0, 0, 0, 0.2); + border-left: 1px solid rgba(255, 255, 255, 0.1); + } + .navbar-default .navbar-nav > li > a:hover, + .navbar-inverse .navbar-nav > li > a:hover { + border-left-color: transparent; + } + .navbar-default .nav .open > a, + .navbar-inverse .nav .open > a { + border-color: transparent; + } + .navbar-default .navbar-nav > li.active > a, + .navbar-inverse .navbar-nav > li.active > a { + border-left-color: transparent; + } + .navbar-default .navbar-form, + .navbar-inverse .navbar-form { + margin-left: 5px; + margin-right: 5px; + } +} +.navbar-default { + -webkit-filter: none; + filter: none; +} +.navbar-default .navbar-nav > li > a:hover { + -webkit-filter: none; + filter: none; +} +.navbar-inverse { + -webkit-filter: none; + filter: none; +} +.navbar-inverse .badge { + background-color: #5d6368; +} +.navbar-inverse .navbar-nav > li > a:hover { + -webkit-filter: none; + filter: none; +} +.btn, +.btn:hover { + border-color: rgba(0, 0, 0, 0.6); + text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.3); +} +.btn-default { + -webkit-filter: none; + filter: none; +} +.btn-default:hover { + -webkit-filter: none; + filter: none; +} +.btn-primary { + -webkit-filter: none; + filter: none; +} +.btn-primary:hover { + -webkit-filter: none; + filter: none; +} +.btn-success { + -webkit-filter: none; + filter: none; +} +.btn-success:hover { + -webkit-filter: none; + filter: none; +} +.btn-info { + -webkit-filter: none; + filter: none; +} +.btn-info:hover { + -webkit-filter: none; + filter: none; +} +.btn-warning { + -webkit-filter: none; + filter: none; +} +.btn-warning:hover { + -webkit-filter: none; + filter: none; +} +.btn-danger { + -webkit-filter: none; + filter: none; +} +.btn-danger:hover { + -webkit-filter: none; + filter: none; +} +.btn-link, +.btn-link:hover { + border-color: transparent; +} +h1, +h2, +h3, +h4, +h5, +h6 { + text-shadow: -1px -1px 0 rgba(0, 0, 0, 0.3); +} +.text-primary, +.text-primary:hover { + color: #7a8288; +} +.text-success, +.text-success:hover { + color: #62c462; +} +.text-danger, +.text-danger:hover { + color: #ee5f5b; +} +.text-warning, +.text-warning:hover { + color: #f89406; +} +.text-info, +.text-info:hover { + color: #5bc0de; +} +.table .success, +.table .warning, +.table .danger, +.table .info { + color: #fff; +} +.table-bordered tbody tr.success td, +.table-bordered tbody tr.warning td, +.table-bordered tbody tr.danger td, +.table-bordered tbody tr.success:hover td, +.table-bordered tbody tr.warning:hover td, +.table-bordered tbody tr.danger:hover td { + border-color: #1c1e22; +} +.table-responsive > .table { + background-color: #2e3338; +} +input, +textarea { + color: #272b30; +} +.has-warning .help-block, +.has-warning .control-label, +.has-warning .radio, +.has-warning .checkbox, +.has-warning .radio-inline, +.has-warning .checkbox-inline, +.has-warning.radio label, +.has-warning.checkbox label, +.has-warning.radio-inline label, +.has-warning.checkbox-inline label, +.has-warning .form-control-feedback { + color: #f89406; +} +.has-warning .form-control, +.has-warning .form-control:focus { + border-color: #f89406; +} +.has-warning .input-group-addon { + border-color: rgba(0, 0, 0, 0.6); +} +.has-error .help-block, +.has-error .control-label, +.has-error .radio, +.has-error .checkbox, +.has-error .radio-inline, +.has-error .checkbox-inline, +.has-error.radio label, +.has-error.checkbox label, +.has-error.radio-inline label, +.has-error.checkbox-inline label, +.has-error .form-control-feedback { + color: #ee5f5b; +} +.has-error .form-control, +.has-error .form-control:focus { + border-color: #ee5f5b; +} +.has-error .input-group-addon { + border-color: rgba(0, 0, 0, 0.6); +} +.has-success .help-block, +.has-success .control-label, +.has-success .radio, +.has-success .checkbox, +.has-success .radio-inline, +.has-success .checkbox-inline, +.has-success.radio label, +.has-success.checkbox label, +.has-success.radio-inline label, +.has-success.checkbox-inline label, +.has-success .form-control-feedback { + color: #62c462; +} +.has-success .form-control, +.has-success .form-control:focus { + border-color: #62c462; +} +.has-success .input-group-addon { + border-color: rgba(0, 0, 0, 0.6); +} +legend { + color: #fff; +} +.input-group-addon { + -webkit-filter: none; + filter: none; + text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.3); + color: #ffffff; +} +.nav .open > a, +.nav .open > a:hover, +.nav .open > a:focus { + border-color: rgba(0, 0, 0, 0.6); +} +.nav-pills > li > a { + -webkit-filter: none; + filter: none; + border: 1px solid rgba(0, 0, 0, 0.6); + text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.3); +} +.nav-pills > li > a:hover { + -webkit-filter: none; + filter: none; + border: 1px solid rgba(0, 0, 0, 0.6); +} +.nav-pills > li.active > a, +.nav-pills > li.active > a:hover { + background-color: none; + -webkit-filter: none; + filter: none; + border: 1px solid rgba(0, 0, 0, 0.6); +} +.nav-pills > li.disabled > a, +.nav-pills > li.disabled > a:hover { + -webkit-filter: none; + filter: none; +} +.pagination > li > a, +.pagination > li > span { + text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.3); + -webkit-filter: none; + filter: none; +} +.pagination > li > a:hover, +.pagination > li > span:hover { + -webkit-filter: none; + filter: none; +} +.pagination > li.active > a, +.pagination > li.active > span { + -webkit-filter: none; + filter: none; +} +.pagination > li.disabled > a, +.pagination > li.disabled > a:hover, +.pagination > li.disabled > span, +.pagination > li.disabled > span:hover { + -webkit-filter: none; + filter: none; +} +.pager > li > a { + -webkit-filter: none; + filter: none; + text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.3); +} +.pager > li > a:hover { + -webkit-filter: none; + filter: none; +} +.pager > li.disabled > a, +.pager > li.disabled > a:hover { + -webkit-filter: none; + filter: none; +} +.breadcrumb { + border: 1px solid rgba(0, 0, 0, 0.6); + text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.3); + -webkit-filter: none; + filter: none; +} +.alert .alert-link, +.alert a { + color: #fff; + text-decoration: underline; +} +.alert .close { + color: #000000; + text-decoration: none; +} +a.thumbnail:hover, +a.thumbnail:focus, +a.thumbnail.active { + border-color: #0c0d0e; +} +a.list-group-item.active, +a.list-group-item.active:hover, +a.list-group-item.active:focus { + border-color: rgba(0, 0, 0, 0.6); +} +a.list-group-item-success.active { + background-color: #62c462; +} +a.list-group-item-success.active:hover, +a.list-group-item-success.active:focus { + background-color: #4fbd4f; +} +a.list-group-item-warning.active { + background-color: #f89406; +} +a.list-group-item-warning.active:hover, +a.list-group-item-warning.active:focus { + background-color: #df8505; +} +a.list-group-item-danger.active { + background-color: #ee5f5b; +} +a.list-group-item-danger.active:hover, +a.list-group-item-danger.active:focus { + background-color: #ec4844; +} +.jumbotron { + border: 1px solid rgba(0, 0, 0, 0.6); +} +.panel-primary .panel-heading, +.panel-success .panel-heading, +.panel-danger .panel-heading, +.panel-warning .panel-heading, +.panel-info .panel-heading { + border-color: #000; +} diff --git a/web/css/bootstrap.slate.min.css b/web/css/bootstrap.slate.min.css deleted file mode 100644 index e474bb6ba..000000000 --- a/web/css/bootstrap.slate.min.css +++ /dev/null @@ -1,11 +0,0 @@ -/*! - * bootswatch v3.3.6 - * Homepage: http://bootswatch.com - * Copyright 2012-2016 Thomas Park - * Licensed under MIT - * Based on Bootstrap -*//*! - * Bootstrap v3.3.6 (http://getbootstrap.com) - * Copyright 2011-2015 Twitter, Inc. - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - *//*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:bold}dfn{font-style:italic}h1{font-size:2em;margin:0.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace, monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type="checkbox"],input[type="radio"]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0}input[type="number"]::-webkit-inner-spin-button,input[type="number"]::-webkit-outer-spin-button{height:auto}input[type="search"]{-webkit-appearance:textfield;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:0.35em 0.625em 0.75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:bold}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */@media print{*,*:before,*:after{background:transparent !important;color:#000 !important;-webkit-box-shadow:none !important;box-shadow:none !important;text-shadow:none !important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="#"]:after,a[href^="javascript:"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100% !important}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}.navbar{display:none}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000 !important}.label{border:1px solid #000}.table{border-collapse:collapse !important}.table td,.table th{background-color:#fff !important}.table-bordered th,.table-bordered td{border:1px solid #ddd !important}}@font-face{font-family:'Glyphicons Halflings';src:url('../fonts/glyphicons-halflings-regular.eot');src:url('../fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'),url('../fonts/glyphicons-halflings-regular.woff2') format('woff2'),url('../fonts/glyphicons-halflings-regular.woff') format('woff'),url('../fonts/glyphicons-halflings-regular.ttf') format('truetype'),url('../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular') format('svg')}.glyphicon{position:relative;top:1px;display:inline-block;font-family:'Glyphicons Halflings';font-style:normal;font-weight:normal;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.glyphicon-asterisk:before{content:"\002a"}.glyphicon-plus:before{content:"\002b"}.glyphicon-euro:before,.glyphicon-eur:before{content:"\20ac"}.glyphicon-minus:before{content:"\2212"}.glyphicon-cloud:before{content:"\2601"}.glyphicon-envelope:before{content:"\2709"}.glyphicon-pencil:before{content:"\270f"}.glyphicon-glass:before{content:"\e001"}.glyphicon-music:before{content:"\e002"}.glyphicon-search:before{content:"\e003"}.glyphicon-heart:before{content:"\e005"}.glyphicon-star:before{content:"\e006"}.glyphicon-star-empty:before{content:"\e007"}.glyphicon-user:before{content:"\e008"}.glyphicon-film:before{content:"\e009"}.glyphicon-th-large:before{content:"\e010"}.glyphicon-th:before{content:"\e011"}.glyphicon-th-list:before{content:"\e012"}.glyphicon-ok:before{content:"\e013"}.glyphicon-remove:before{content:"\e014"}.glyphicon-zoom-in:before{content:"\e015"}.glyphicon-zoom-out:before{content:"\e016"}.glyphicon-off:before{content:"\e017"}.glyphicon-signal:before{content:"\e018"}.glyphicon-cog:before{content:"\e019"}.glyphicon-trash:before{content:"\e020"}.glyphicon-home:before{content:"\e021"}.glyphicon-file:before{content:"\e022"}.glyphicon-time:before{content:"\e023"}.glyphicon-road:before{content:"\e024"}.glyphicon-download-alt:before{content:"\e025"}.glyphicon-download:before{content:"\e026"}.glyphicon-upload:before{content:"\e027"}.glyphicon-inbox:before{content:"\e028"}.glyphicon-play-circle:before{content:"\e029"}.glyphicon-repeat:before{content:"\e030"}.glyphicon-refresh:before{content:"\e031"}.glyphicon-list-alt:before{content:"\e032"}.glyphicon-lock:before{content:"\e033"}.glyphicon-flag:before{content:"\e034"}.glyphicon-headphones:before{content:"\e035"}.glyphicon-volume-off:before{content:"\e036"}.glyphicon-volume-down:before{content:"\e037"}.glyphicon-volume-up:before{content:"\e038"}.glyphicon-qrcode:before{content:"\e039"}.glyphicon-barcode:before{content:"\e040"}.glyphicon-tag:before{content:"\e041"}.glyphicon-tags:before{content:"\e042"}.glyphicon-book:before{content:"\e043"}.glyphicon-bookmark:before{content:"\e044"}.glyphicon-print:before{content:"\e045"}.glyphicon-camera:before{content:"\e046"}.glyphicon-font:before{content:"\e047"}.glyphicon-bold:before{content:"\e048"}.glyphicon-italic:before{content:"\e049"}.glyphicon-text-height:before{content:"\e050"}.glyphicon-text-width:before{content:"\e051"}.glyphicon-align-left:before{content:"\e052"}.glyphicon-align-center:before{content:"\e053"}.glyphicon-align-right:before{content:"\e054"}.glyphicon-align-justify:before{content:"\e055"}.glyphicon-list:before{content:"\e056"}.glyphicon-indent-left:before{content:"\e057"}.glyphicon-indent-right:before{content:"\e058"}.glyphicon-facetime-video:before{content:"\e059"}.glyphicon-picture:before{content:"\e060"}.glyphicon-map-marker:before{content:"\e062"}.glyphicon-adjust:before{content:"\e063"}.glyphicon-tint:before{content:"\e064"}.glyphicon-edit:before{content:"\e065"}.glyphicon-share:before{content:"\e066"}.glyphicon-check:before{content:"\e067"}.glyphicon-move:before{content:"\e068"}.glyphicon-step-backward:before{content:"\e069"}.glyphicon-fast-backward:before{content:"\e070"}.glyphicon-backward:before{content:"\e071"}.glyphicon-play:before{content:"\e072"}.glyphicon-pause:before{content:"\e073"}.glyphicon-stop:before{content:"\e074"}.glyphicon-forward:before{content:"\e075"}.glyphicon-fast-forward:before{content:"\e076"}.glyphicon-step-forward:before{content:"\e077"}.glyphicon-eject:before{content:"\e078"}.glyphicon-chevron-left:before{content:"\e079"}.glyphicon-chevron-right:before{content:"\e080"}.glyphicon-plus-sign:before{content:"\e081"}.glyphicon-minus-sign:before{content:"\e082"}.glyphicon-remove-sign:before{content:"\e083"}.glyphicon-ok-sign:before{content:"\e084"}.glyphicon-question-sign:before{content:"\e085"}.glyphicon-info-sign:before{content:"\e086"}.glyphicon-screenshot:before{content:"\e087"}.glyphicon-remove-circle:before{content:"\e088"}.glyphicon-ok-circle:before{content:"\e089"}.glyphicon-ban-circle:before{content:"\e090"}.glyphicon-arrow-left:before{content:"\e091"}.glyphicon-arrow-right:before{content:"\e092"}.glyphicon-arrow-up:before{content:"\e093"}.glyphicon-arrow-down:before{content:"\e094"}.glyphicon-share-alt:before{content:"\e095"}.glyphicon-resize-full:before{content:"\e096"}.glyphicon-resize-small:before{content:"\e097"}.glyphicon-exclamation-sign:before{content:"\e101"}.glyphicon-gift:before{content:"\e102"}.glyphicon-leaf:before{content:"\e103"}.glyphicon-fire:before{content:"\e104"}.glyphicon-eye-open:before{content:"\e105"}.glyphicon-eye-close:before{content:"\e106"}.glyphicon-warning-sign:before{content:"\e107"}.glyphicon-plane:before{content:"\e108"}.glyphicon-calendar:before{content:"\e109"}.glyphicon-random:before{content:"\e110"}.glyphicon-comment:before{content:"\e111"}.glyphicon-magnet:before{content:"\e112"}.glyphicon-chevron-up:before{content:"\e113"}.glyphicon-chevron-down:before{content:"\e114"}.glyphicon-retweet:before{content:"\e115"}.glyphicon-shopping-cart:before{content:"\e116"}.glyphicon-folder-close:before{content:"\e117"}.glyphicon-folder-open:before{content:"\e118"}.glyphicon-resize-vertical:before{content:"\e119"}.glyphicon-resize-horizontal:before{content:"\e120"}.glyphicon-hdd:before{content:"\e121"}.glyphicon-bullhorn:before{content:"\e122"}.glyphicon-bell:before{content:"\e123"}.glyphicon-certificate:before{content:"\e124"}.glyphicon-thumbs-up:before{content:"\e125"}.glyphicon-thumbs-down:before{content:"\e126"}.glyphicon-hand-right:before{content:"\e127"}.glyphicon-hand-left:before{content:"\e128"}.glyphicon-hand-up:before{content:"\e129"}.glyphicon-hand-down:before{content:"\e130"}.glyphicon-circle-arrow-right:before{content:"\e131"}.glyphicon-circle-arrow-left:before{content:"\e132"}.glyphicon-circle-arrow-up:before{content:"\e133"}.glyphicon-circle-arrow-down:before{content:"\e134"}.glyphicon-globe:before{content:"\e135"}.glyphicon-wrench:before{content:"\e136"}.glyphicon-tasks:before{content:"\e137"}.glyphicon-filter:before{content:"\e138"}.glyphicon-briefcase:before{content:"\e139"}.glyphicon-fullscreen:before{content:"\e140"}.glyphicon-dashboard:before{content:"\e141"}.glyphicon-paperclip:before{content:"\e142"}.glyphicon-heart-empty:before{content:"\e143"}.glyphicon-link:before{content:"\e144"}.glyphicon-phone:before{content:"\e145"}.glyphicon-pushpin:before{content:"\e146"}.glyphicon-usd:before{content:"\e148"}.glyphicon-gbp:before{content:"\e149"}.glyphicon-sort:before{content:"\e150"}.glyphicon-sort-by-alphabet:before{content:"\e151"}.glyphicon-sort-by-alphabet-alt:before{content:"\e152"}.glyphicon-sort-by-order:before{content:"\e153"}.glyphicon-sort-by-order-alt:before{content:"\e154"}.glyphicon-sort-by-attributes:before{content:"\e155"}.glyphicon-sort-by-attributes-alt:before{content:"\e156"}.glyphicon-unchecked:before{content:"\e157"}.glyphicon-expand:before{content:"\e158"}.glyphicon-collapse-down:before{content:"\e159"}.glyphicon-collapse-up:before{content:"\e160"}.glyphicon-log-in:before{content:"\e161"}.glyphicon-flash:before{content:"\e162"}.glyphicon-log-out:before{content:"\e163"}.glyphicon-new-window:before{content:"\e164"}.glyphicon-record:before{content:"\e165"}.glyphicon-save:before{content:"\e166"}.glyphicon-open:before{content:"\e167"}.glyphicon-saved:before{content:"\e168"}.glyphicon-import:before{content:"\e169"}.glyphicon-export:before{content:"\e170"}.glyphicon-send:before{content:"\e171"}.glyphicon-floppy-disk:before{content:"\e172"}.glyphicon-floppy-saved:before{content:"\e173"}.glyphicon-floppy-remove:before{content:"\e174"}.glyphicon-floppy-save:before{content:"\e175"}.glyphicon-floppy-open:before{content:"\e176"}.glyphicon-credit-card:before{content:"\e177"}.glyphicon-transfer:before{content:"\e178"}.glyphicon-cutlery:before{content:"\e179"}.glyphicon-header:before{content:"\e180"}.glyphicon-compressed:before{content:"\e181"}.glyphicon-earphone:before{content:"\e182"}.glyphicon-phone-alt:before{content:"\e183"}.glyphicon-tower:before{content:"\e184"}.glyphicon-stats:before{content:"\e185"}.glyphicon-sd-video:before{content:"\e186"}.glyphicon-hd-video:before{content:"\e187"}.glyphicon-subtitles:before{content:"\e188"}.glyphicon-sound-stereo:before{content:"\e189"}.glyphicon-sound-dolby:before{content:"\e190"}.glyphicon-sound-5-1:before{content:"\e191"}.glyphicon-sound-6-1:before{content:"\e192"}.glyphicon-sound-7-1:before{content:"\e193"}.glyphicon-copyright-mark:before{content:"\e194"}.glyphicon-registration-mark:before{content:"\e195"}.glyphicon-cloud-download:before{content:"\e197"}.glyphicon-cloud-upload:before{content:"\e198"}.glyphicon-tree-conifer:before{content:"\e199"}.glyphicon-tree-deciduous:before{content:"\e200"}.glyphicon-cd:before{content:"\e201"}.glyphicon-save-file:before{content:"\e202"}.glyphicon-open-file:before{content:"\e203"}.glyphicon-level-up:before{content:"\e204"}.glyphicon-copy:before{content:"\e205"}.glyphicon-paste:before{content:"\e206"}.glyphicon-alert:before{content:"\e209"}.glyphicon-equalizer:before{content:"\e210"}.glyphicon-king:before{content:"\e211"}.glyphicon-queen:before{content:"\e212"}.glyphicon-pawn:before{content:"\e213"}.glyphicon-bishop:before{content:"\e214"}.glyphicon-knight:before{content:"\e215"}.glyphicon-baby-formula:before{content:"\e216"}.glyphicon-tent:before{content:"\26fa"}.glyphicon-blackboard:before{content:"\e218"}.glyphicon-bed:before{content:"\e219"}.glyphicon-apple:before{content:"\f8ff"}.glyphicon-erase:before{content:"\e221"}.glyphicon-hourglass:before{content:"\231b"}.glyphicon-lamp:before{content:"\e223"}.glyphicon-duplicate:before{content:"\e224"}.glyphicon-piggy-bank:before{content:"\e225"}.glyphicon-scissors:before{content:"\e226"}.glyphicon-bitcoin:before{content:"\e227"}.glyphicon-btc:before{content:"\e227"}.glyphicon-xbt:before{content:"\e227"}.glyphicon-yen:before{content:"\00a5"}.glyphicon-jpy:before{content:"\00a5"}.glyphicon-ruble:before{content:"\20bd"}.glyphicon-rub:before{content:"\20bd"}.glyphicon-scale:before{content:"\e230"}.glyphicon-ice-lolly:before{content:"\e231"}.glyphicon-ice-lolly-tasted:before{content:"\e232"}.glyphicon-education:before{content:"\e233"}.glyphicon-option-horizontal:before{content:"\e234"}.glyphicon-option-vertical:before{content:"\e235"}.glyphicon-menu-hamburger:before{content:"\e236"}.glyphicon-modal-window:before{content:"\e237"}.glyphicon-oil:before{content:"\e238"}.glyphicon-grain:before{content:"\e239"}.glyphicon-sunglasses:before{content:"\e240"}.glyphicon-text-size:before{content:"\e241"}.glyphicon-text-color:before{content:"\e242"}.glyphicon-text-background:before{content:"\e243"}.glyphicon-object-align-top:before{content:"\e244"}.glyphicon-object-align-bottom:before{content:"\e245"}.glyphicon-object-align-horizontal:before{content:"\e246"}.glyphicon-object-align-left:before{content:"\e247"}.glyphicon-object-align-vertical:before{content:"\e248"}.glyphicon-object-align-right:before{content:"\e249"}.glyphicon-triangle-right:before{content:"\e250"}.glyphicon-triangle-left:before{content:"\e251"}.glyphicon-triangle-bottom:before{content:"\e252"}.glyphicon-triangle-top:before{content:"\e253"}.glyphicon-console:before{content:"\e254"}.glyphicon-superscript:before{content:"\e255"}.glyphicon-subscript:before{content:"\e256"}.glyphicon-menu-left:before{content:"\e257"}.glyphicon-menu-right:before{content:"\e258"}.glyphicon-menu-down:before{content:"\e259"}.glyphicon-menu-up:before{content:"\e260"}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}*:before,*:after{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#c8c8c8;background-color:#272b30}input,button,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#ffffff;text-decoration:none}a:hover,a:focus{color:#ffffff;text-decoration:underline}a:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.img-responsive,.thumbnail>img,.thumbnail a>img,.carousel-inner>.item>img,.carousel-inner>.item>a>img{display:block;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{padding:4px;line-height:1.42857143;background-color:#1c1e22;border:1px solid #0c0d0e;border-radius:4px;-webkit-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out;display:inline-block;max-width:100%;height:auto}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #1c1e22}.sr-only{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}[role="button"]{cursor:pointer}h1,h2,h3,h4,h5,h6,.h1,.h2,.h3,.h4,.h5,.h6{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-weight:500;line-height:1.1;color:inherit}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small,.h1 small,.h2 small,.h3 small,.h4 small,.h5 small,.h6 small,h1 .small,h2 .small,h3 .small,h4 .small,h5 .small,h6 .small,.h1 .small,.h2 .small,.h3 .small,.h4 .small,.h5 .small,.h6 .small{font-weight:normal;line-height:1;color:#7a8288}h1,.h1,h2,.h2,h3,.h3{margin-top:20px;margin-bottom:10px}h1 small,.h1 small,h2 small,.h2 small,h3 small,.h3 small,h1 .small,.h1 .small,h2 .small,.h2 .small,h3 .small,.h3 .small{font-size:65%}h4,.h4,h5,.h5,h6,.h6{margin-top:10px;margin-bottom:10px}h4 small,.h4 small,h5 small,.h5 small,h6 small,.h6 small,h4 .small,.h4 .small,h5 .small,.h5 .small,h6 .small,.h6 .small{font-size:75%}h1,.h1{font-size:36px}h2,.h2{font-size:30px}h3,.h3{font-size:24px}h4,.h4{font-size:18px}h5,.h5{font-size:14px}h6,.h6{font-size:12px}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:16px;font-weight:300;line-height:1.4}@media (min-width:768px){.lead{font-size:21px}}small,.small{font-size:85%}mark,.mark{background-color:#f89406;padding:.2em}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-muted{color:#7a8288}.text-primary{color:#7a8288}a.text-primary:hover,a.text-primary:focus{color:#62686d}.text-success{color:#ffffff}a.text-success:hover,a.text-success:focus{color:#e6e6e6}.text-info{color:#ffffff}a.text-info:hover,a.text-info:focus{color:#e6e6e6}.text-warning{color:#ffffff}a.text-warning:hover,a.text-warning:focus{color:#e6e6e6}.text-danger{color:#ffffff}a.text-danger:hover,a.text-danger:focus{color:#e6e6e6}.bg-primary{color:#fff;background-color:#7a8288}a.bg-primary:hover,a.bg-primary:focus{background-color:#62686d}.bg-success{background-color:#62c462}a.bg-success:hover,a.bg-success:focus{background-color:#42b142}.bg-info{background-color:#5bc0de}a.bg-info:hover,a.bg-info:focus{background-color:#31b0d5}.bg-warning{background-color:#f89406}a.bg-warning:hover,a.bg-warning:focus{background-color:#c67605}.bg-danger{background-color:#ee5f5b}a.bg-danger:hover,a.bg-danger:focus{background-color:#e9322d}.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #1c1e22}ul,ol{margin-top:0;margin-bottom:10px}ul ul,ol ul,ul ol,ol ol{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none;margin-left:-5px}.list-inline>li{display:inline-block;padding-left:5px;padding-right:5px}dl{margin-top:0;margin-bottom:20px}dt,dd{line-height:1.42857143}dt{font-weight:bold}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;clear:left;text-align:right;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[title],abbr[data-original-title]{cursor:help;border-bottom:1px dotted #7a8288}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10px 20px;margin:0 0 20px;font-size:17.5px;border-left:5px solid #7a8288}blockquote p:last-child,blockquote ul:last-child,blockquote ol:last-child{margin-bottom:0}blockquote footer,blockquote small,blockquote .small{display:block;font-size:80%;line-height:1.42857143;color:#7a8288}blockquote footer:before,blockquote small:before,blockquote .small:before{content:'\2014 \00A0'}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;border-right:5px solid #7a8288;border-left:0;text-align:right}.blockquote-reverse footer:before,blockquote.pull-right footer:before,.blockquote-reverse small:before,blockquote.pull-right small:before,.blockquote-reverse .small:before,blockquote.pull-right .small:before{content:''}.blockquote-reverse footer:after,blockquote.pull-right footer:after,.blockquote-reverse small:after,blockquote.pull-right small:after,.blockquote-reverse .small:after,blockquote.pull-right .small:after{content:'\00A0 \2014'}address{margin-bottom:20px;font-style:normal;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#bbb;background-color:#555;border-radius:4px}kbd{padding:2px 4px;font-size:90%;color:#ffffff;background-color:#333333;border-radius:3px;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,0.25);box-shadow:inset 0 -1px 0 rgba(0,0,0,0.25)}kbd kbd{padding:0;font-size:100%;font-weight:bold;-webkit-box-shadow:none;box-shadow:none}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:1.42857143;word-break:break-all;word-wrap:break-word;color:#3a3f44;background-color:#f5f5f5;border:1px solid #cccccc;border-radius:4px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{margin-right:auto;margin-left:auto;padding-left:15px;padding-right:15px}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{margin-right:auto;margin-left:auto;padding-left:15px;padding-right:15px}.row{margin-left:-15px;margin-right:-15px}.col-xs-1,.col-sm-1,.col-md-1,.col-lg-1,.col-xs-2,.col-sm-2,.col-md-2,.col-lg-2,.col-xs-3,.col-sm-3,.col-md-3,.col-lg-3,.col-xs-4,.col-sm-4,.col-md-4,.col-lg-4,.col-xs-5,.col-sm-5,.col-md-5,.col-lg-5,.col-xs-6,.col-sm-6,.col-md-6,.col-lg-6,.col-xs-7,.col-sm-7,.col-md-7,.col-lg-7,.col-xs-8,.col-sm-8,.col-md-8,.col-lg-8,.col-xs-9,.col-sm-9,.col-md-9,.col-lg-9,.col-xs-10,.col-sm-10,.col-md-10,.col-lg-10,.col-xs-11,.col-sm-11,.col-md-11,.col-lg-11,.col-xs-12,.col-sm-12,.col-md-12,.col-lg-12{position:relative;min-height:1px;padding-left:15px;padding-right:15px}.col-xs-1,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9,.col-xs-10,.col-xs-11,.col-xs-12{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:auto}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:auto}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0%}@media (min-width:768px){.col-sm-1,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-10,.col-sm-11,.col-sm-12{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:auto}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:auto}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0%}}@media (min-width:992px){.col-md-1,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-10,.col-md-11,.col-md-12{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:auto}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:auto}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0%}}@media (min-width:1200px){.col-lg-1,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-10,.col-lg-11,.col-lg-12{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:auto}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:auto}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0%}}table{background-color:#2e3338}caption{padding-top:8px;padding-bottom:8px;color:#7a8288;text-align:left}th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:20px}.table>thead>tr>th,.table>tbody>tr>th,.table>tfoot>tr>th,.table>thead>tr>td,.table>tbody>tr>td,.table>tfoot>tr>td{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #1c1e22}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #1c1e22}.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>th,.table>caption+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>td,.table>thead:first-child>tr:first-child>td{border-top:0}.table>tbody+tbody{border-top:2px solid #1c1e22}.table .table{background-color:#272b30}.table-condensed>thead>tr>th,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>tbody>tr>td,.table-condensed>tfoot>tr>td{padding:5px}.table-bordered{border:1px solid #1c1e22}.table-bordered>thead>tr>th,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>tbody>tr>td,.table-bordered>tfoot>tr>td{border:1px solid #1c1e22}.table-bordered>thead>tr>th,.table-bordered>thead>tr>td{border-bottom-width:2px}.table-striped>tbody>tr:nth-of-type(odd){background-color:#353a41}.table-hover>tbody>tr:hover{background-color:#49515a}table col[class*="col-"]{position:static;float:none;display:table-column}table td[class*="col-"],table th[class*="col-"]{position:static;float:none;display:table-cell}.table>thead>tr>td.active,.table>tbody>tr>td.active,.table>tfoot>tr>td.active,.table>thead>tr>th.active,.table>tbody>tr>th.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>tbody>tr.active>td,.table>tfoot>tr.active>td,.table>thead>tr.active>th,.table>tbody>tr.active>th,.table>tfoot>tr.active>th{background-color:#49515a}.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover,.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr:hover>.active,.table-hover>tbody>tr.active:hover>th{background-color:#3e444c}.table>thead>tr>td.success,.table>tbody>tr>td.success,.table>tfoot>tr>td.success,.table>thead>tr>th.success,.table>tbody>tr>th.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>tbody>tr.success>td,.table>tfoot>tr.success>td,.table>thead>tr.success>th,.table>tbody>tr.success>th,.table>tfoot>tr.success>th{background-color:#62c462}.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover,.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr:hover>.success,.table-hover>tbody>tr.success:hover>th{background-color:#4fbd4f}.table>thead>tr>td.info,.table>tbody>tr>td.info,.table>tfoot>tr>td.info,.table>thead>tr>th.info,.table>tbody>tr>th.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>tbody>tr.info>td,.table>tfoot>tr.info>td,.table>thead>tr.info>th,.table>tbody>tr.info>th,.table>tfoot>tr.info>th{background-color:#5bc0de}.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover,.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr:hover>.info,.table-hover>tbody>tr.info:hover>th{background-color:#46b8da}.table>thead>tr>td.warning,.table>tbody>tr>td.warning,.table>tfoot>tr>td.warning,.table>thead>tr>th.warning,.table>tbody>tr>th.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>tbody>tr.warning>td,.table>tfoot>tr.warning>td,.table>thead>tr.warning>th,.table>tbody>tr.warning>th,.table>tfoot>tr.warning>th{background-color:#f89406}.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover,.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr.warning:hover>th{background-color:#df8505}.table>thead>tr>td.danger,.table>tbody>tr>td.danger,.table>tfoot>tr>td.danger,.table>thead>tr>th.danger,.table>tbody>tr>th.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>tbody>tr.danger>td,.table>tfoot>tr.danger>td,.table>thead>tr.danger>th,.table>tbody>tr.danger>th,.table>tfoot>tr.danger>th{background-color:#ee5f5b}.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover,.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr.danger:hover>th{background-color:#ec4844}.table-responsive{overflow-x:auto;min-height:0.01%}@media screen and (max-width:767px){.table-responsive{width:100%;margin-bottom:15px;overflow-y:hidden;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #1c1e22}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>thead>tr>th,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tfoot>tr>td{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>thead>tr>th:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.table-responsive>.table-bordered>thead>tr>th:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>th,.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>td{border-bottom:0}}fieldset{padding:0;margin:0;border:0;min-width:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:inherit;color:#c8c8c8;border:0;border-bottom:1px solid #1c1e22}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:bold}input[type="search"]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type="radio"],input[type="checkbox"]{margin:4px 0 0;margin-top:1px \9;line-height:normal}input[type="file"]{display:block}input[type="range"]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:9px;font-size:14px;line-height:1.42857143;color:#272b30}.form-control{display:block;width:100%;height:38px;padding:8px 12px;font-size:14px;line-height:1.42857143;color:#272b30;background-color:#ffffff;background-image:none;border:1px solid #cccccc;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-webkit-transition:border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s;-o-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.form-control:focus{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(102,175,233,0.6);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(102,175,233,0.6)}.form-control::-moz-placeholder{color:#7a8288;opacity:1}.form-control:-ms-input-placeholder{color:#7a8288}.form-control::-webkit-input-placeholder{color:#7a8288}.form-control::-ms-expand{border:0;background-color:transparent}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{background-color:#999999;opacity:1}.form-control[disabled],fieldset[disabled] .form-control{cursor:not-allowed}textarea.form-control{height:auto}input[type="search"]{-webkit-appearance:none}@media screen and (-webkit-min-device-pixel-ratio:0){input[type="date"].form-control,input[type="time"].form-control,input[type="datetime-local"].form-control,input[type="month"].form-control{line-height:38px}input[type="date"].input-sm,input[type="time"].input-sm,input[type="datetime-local"].input-sm,input[type="month"].input-sm,.input-group-sm input[type="date"],.input-group-sm input[type="time"],.input-group-sm input[type="datetime-local"],.input-group-sm input[type="month"]{line-height:30px}input[type="date"].input-lg,input[type="time"].input-lg,input[type="datetime-local"].input-lg,input[type="month"].input-lg,.input-group-lg input[type="date"],.input-group-lg input[type="time"],.input-group-lg input[type="datetime-local"],.input-group-lg input[type="month"]{line-height:54px}}.form-group{margin-bottom:15px}.radio,.checkbox{position:relative;display:block;margin-top:10px;margin-bottom:10px}.radio label,.checkbox label{min-height:20px;padding-left:20px;margin-bottom:0;font-weight:normal;cursor:pointer}.radio input[type="radio"],.radio-inline input[type="radio"],.checkbox input[type="checkbox"],.checkbox-inline input[type="checkbox"]{position:absolute;margin-left:-20px;margin-top:4px \9}.radio+.radio,.checkbox+.checkbox{margin-top:-5px}.radio-inline,.checkbox-inline{position:relative;display:inline-block;padding-left:20px;margin-bottom:0;vertical-align:middle;font-weight:normal;cursor:pointer}.radio-inline+.radio-inline,.checkbox-inline+.checkbox-inline{margin-top:0;margin-left:10px}input[type="radio"][disabled],input[type="checkbox"][disabled],input[type="radio"].disabled,input[type="checkbox"].disabled,fieldset[disabled] input[type="radio"],fieldset[disabled] input[type="checkbox"]{cursor:not-allowed}.radio-inline.disabled,.checkbox-inline.disabled,fieldset[disabled] .radio-inline,fieldset[disabled] .checkbox-inline{cursor:not-allowed}.radio.disabled label,.checkbox.disabled label,fieldset[disabled] .radio label,fieldset[disabled] .checkbox label{cursor:not-allowed}.form-control-static{padding-top:9px;padding-bottom:9px;margin-bottom:0;min-height:34px}.form-control-static.input-lg,.form-control-static.input-sm{padding-left:0;padding-right:0}.input-sm{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-sm{height:30px;line-height:30px}textarea.input-sm,select[multiple].input-sm{height:auto}.form-group-sm .form-control{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.form-group-sm select.form-control{height:30px;line-height:30px}.form-group-sm textarea.form-control,.form-group-sm select[multiple].form-control{height:auto}.form-group-sm .form-control-static{height:30px;min-height:32px;padding:6px 10px;font-size:12px;line-height:1.5}.input-lg{height:54px;padding:14px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-lg{height:54px;line-height:54px}textarea.input-lg,select[multiple].input-lg{height:auto}.form-group-lg .form-control{height:54px;padding:14px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.form-group-lg select.form-control{height:54px;line-height:54px}.form-group-lg textarea.form-control,.form-group-lg select[multiple].form-control{height:auto}.form-group-lg .form-control-static{height:54px;min-height:38px;padding:15px 16px;font-size:18px;line-height:1.3333333}.has-feedback{position:relative}.has-feedback .form-control{padding-right:47.5px}.form-control-feedback{position:absolute;top:0;right:0;z-index:2;display:block;width:38px;height:38px;line-height:38px;text-align:center;pointer-events:none}.input-lg+.form-control-feedback,.input-group-lg+.form-control-feedback,.form-group-lg .form-control+.form-control-feedback{width:54px;height:54px;line-height:54px}.input-sm+.form-control-feedback,.input-group-sm+.form-control-feedback,.form-group-sm .form-control+.form-control-feedback{width:30px;height:30px;line-height:30px}.has-success .help-block,.has-success .control-label,.has-success .radio,.has-success .checkbox,.has-success .radio-inline,.has-success .checkbox-inline,.has-success.radio label,.has-success.checkbox label,.has-success.radio-inline label,.has-success.checkbox-inline label{color:#ffffff}.has-success .form-control{border-color:#ffffff;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-success .form-control:focus{border-color:#e6e6e6;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #fff;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #fff}.has-success .input-group-addon{color:#ffffff;border-color:#ffffff;background-color:#62c462}.has-success .form-control-feedback{color:#ffffff}.has-warning .help-block,.has-warning .control-label,.has-warning .radio,.has-warning .checkbox,.has-warning .radio-inline,.has-warning .checkbox-inline,.has-warning.radio label,.has-warning.checkbox label,.has-warning.radio-inline label,.has-warning.checkbox-inline label{color:#ffffff}.has-warning .form-control{border-color:#ffffff;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-warning .form-control:focus{border-color:#e6e6e6;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #fff;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #fff}.has-warning .input-group-addon{color:#ffffff;border-color:#ffffff;background-color:#f89406}.has-warning .form-control-feedback{color:#ffffff}.has-error .help-block,.has-error .control-label,.has-error .radio,.has-error .checkbox,.has-error .radio-inline,.has-error .checkbox-inline,.has-error.radio label,.has-error.checkbox label,.has-error.radio-inline label,.has-error.checkbox-inline label{color:#ffffff}.has-error .form-control{border-color:#ffffff;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-error .form-control:focus{border-color:#e6e6e6;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #fff;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #fff}.has-error .input-group-addon{color:#ffffff;border-color:#ffffff;background-color:#ee5f5b}.has-error .form-control-feedback{color:#ffffff}.has-feedback label~.form-control-feedback{top:25px}.has-feedback label.sr-only~.form-control-feedback{top:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#ffffff}@media (min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-static{display:inline-block}.form-inline .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn,.form-inline .input-group .form-control{width:auto}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .radio,.form-inline .checkbox{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.form-inline .radio label,.form-inline .checkbox label{padding-left:0}.form-inline .radio input[type="radio"],.form-inline .checkbox input[type="checkbox"]{position:relative;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .radio,.form-horizontal .checkbox,.form-horizontal .radio-inline,.form-horizontal .checkbox-inline{margin-top:0;margin-bottom:0;padding-top:9px}.form-horizontal .radio,.form-horizontal .checkbox{min-height:29px}.form-horizontal .form-group{margin-left:-15px;margin-right:-15px}@media (min-width:768px){.form-horizontal .control-label{text-align:right;margin-bottom:0;padding-top:9px}}.form-horizontal .has-feedback .form-control-feedback{right:15px}@media (min-width:768px){.form-horizontal .form-group-lg .control-label{padding-top:15px;font-size:18px}}@media (min-width:768px){.form-horizontal .form-group-sm .control-label{padding-top:6px;font-size:12px}}.btn{display:inline-block;margin-bottom:0;font-weight:normal;text-align:center;vertical-align:middle;-ms-touch-action:manipulation;touch-action:manipulation;cursor:pointer;background-image:none;border:1px solid transparent;white-space:nowrap;padding:6px 12px;font-size:14px;line-height:1.42857143;border-radius:4px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.btn:focus,.btn:active:focus,.btn.active:focus,.btn.focus,.btn:active.focus,.btn.active.focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn:hover,.btn:focus,.btn.focus{color:#ffffff;text-decoration:none}.btn:active,.btn.active{outline:0;background-image:none;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;opacity:0.65;filter:alpha(opacity=65);-webkit-box-shadow:none;box-shadow:none}a.btn.disabled,fieldset[disabled] a.btn{pointer-events:none}.btn-default{color:#ffffff;background-color:#3a3f44;border-color:#3a3f44}.btn-default:focus,.btn-default.focus{color:#ffffff;background-color:#232628;border-color:#000000}.btn-default:hover{color:#ffffff;background-color:#232628;border-color:#1e2023}.btn-default:active,.btn-default.active,.open>.dropdown-toggle.btn-default{color:#ffffff;background-color:#232628;border-color:#1e2023}.btn-default:active:hover,.btn-default.active:hover,.open>.dropdown-toggle.btn-default:hover,.btn-default:active:focus,.btn-default.active:focus,.open>.dropdown-toggle.btn-default:focus,.btn-default:active.focus,.btn-default.active.focus,.open>.dropdown-toggle.btn-default.focus{color:#ffffff;background-color:#121415;border-color:#000000}.btn-default:active,.btn-default.active,.open>.dropdown-toggle.btn-default{background-image:none}.btn-default.disabled:hover,.btn-default[disabled]:hover,fieldset[disabled] .btn-default:hover,.btn-default.disabled:focus,.btn-default[disabled]:focus,fieldset[disabled] .btn-default:focus,.btn-default.disabled.focus,.btn-default[disabled].focus,fieldset[disabled] .btn-default.focus{background-color:#3a3f44;border-color:#3a3f44}.btn-default .badge{color:#3a3f44;background-color:#ffffff}.btn-primary{color:#ffffff;background-color:#7a8288;border-color:#7a8288}.btn-primary:focus,.btn-primary.focus{color:#ffffff;background-color:#62686d;border-color:#3e4245}.btn-primary:hover{color:#ffffff;background-color:#62686d;border-color:#5d6368}.btn-primary:active,.btn-primary.active,.open>.dropdown-toggle.btn-primary{color:#ffffff;background-color:#62686d;border-color:#5d6368}.btn-primary:active:hover,.btn-primary.active:hover,.open>.dropdown-toggle.btn-primary:hover,.btn-primary:active:focus,.btn-primary.active:focus,.open>.dropdown-toggle.btn-primary:focus,.btn-primary:active.focus,.btn-primary.active.focus,.open>.dropdown-toggle.btn-primary.focus{color:#ffffff;background-color:#51565a;border-color:#3e4245}.btn-primary:active,.btn-primary.active,.open>.dropdown-toggle.btn-primary{background-image:none}.btn-primary.disabled:hover,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary:hover,.btn-primary.disabled:focus,.btn-primary[disabled]:focus,fieldset[disabled] .btn-primary:focus,.btn-primary.disabled.focus,.btn-primary[disabled].focus,fieldset[disabled] .btn-primary.focus{background-color:#7a8288;border-color:#7a8288}.btn-primary .badge{color:#7a8288;background-color:#ffffff}.btn-success{color:#ffffff;background-color:#62c462;border-color:#62c462}.btn-success:focus,.btn-success.focus{color:#ffffff;background-color:#42b142;border-color:#2d792d}.btn-success:hover{color:#ffffff;background-color:#42b142;border-color:#40a940}.btn-success:active,.btn-success.active,.open>.dropdown-toggle.btn-success{color:#ffffff;background-color:#42b142;border-color:#40a940}.btn-success:active:hover,.btn-success.active:hover,.open>.dropdown-toggle.btn-success:hover,.btn-success:active:focus,.btn-success.active:focus,.open>.dropdown-toggle.btn-success:focus,.btn-success:active.focus,.btn-success.active.focus,.open>.dropdown-toggle.btn-success.focus{color:#ffffff;background-color:#399739;border-color:#2d792d}.btn-success:active,.btn-success.active,.open>.dropdown-toggle.btn-success{background-image:none}.btn-success.disabled:hover,.btn-success[disabled]:hover,fieldset[disabled] .btn-success:hover,.btn-success.disabled:focus,.btn-success[disabled]:focus,fieldset[disabled] .btn-success:focus,.btn-success.disabled.focus,.btn-success[disabled].focus,fieldset[disabled] .btn-success.focus{background-color:#62c462;border-color:#62c462}.btn-success .badge{color:#62c462;background-color:#ffffff}.btn-info{color:#ffffff;background-color:#5bc0de;border-color:#5bc0de}.btn-info:focus,.btn-info.focus{color:#ffffff;background-color:#31b0d5;border-color:#1f7e9a}.btn-info:hover{color:#ffffff;background-color:#31b0d5;border-color:#2aabd2}.btn-info:active,.btn-info.active,.open>.dropdown-toggle.btn-info{color:#ffffff;background-color:#31b0d5;border-color:#2aabd2}.btn-info:active:hover,.btn-info.active:hover,.open>.dropdown-toggle.btn-info:hover,.btn-info:active:focus,.btn-info.active:focus,.open>.dropdown-toggle.btn-info:focus,.btn-info:active.focus,.btn-info.active.focus,.open>.dropdown-toggle.btn-info.focus{color:#ffffff;background-color:#269abc;border-color:#1f7e9a}.btn-info:active,.btn-info.active,.open>.dropdown-toggle.btn-info{background-image:none}.btn-info.disabled:hover,.btn-info[disabled]:hover,fieldset[disabled] .btn-info:hover,.btn-info.disabled:focus,.btn-info[disabled]:focus,fieldset[disabled] .btn-info:focus,.btn-info.disabled.focus,.btn-info[disabled].focus,fieldset[disabled] .btn-info.focus{background-color:#5bc0de;border-color:#5bc0de}.btn-info .badge{color:#5bc0de;background-color:#ffffff}.btn-warning{color:#ffffff;background-color:#f89406;border-color:#f89406}.btn-warning:focus,.btn-warning.focus{color:#ffffff;background-color:#c67605;border-color:#7c4a03}.btn-warning:hover{color:#ffffff;background-color:#c67605;border-color:#bc7005}.btn-warning:active,.btn-warning.active,.open>.dropdown-toggle.btn-warning{color:#ffffff;background-color:#c67605;border-color:#bc7005}.btn-warning:active:hover,.btn-warning.active:hover,.open>.dropdown-toggle.btn-warning:hover,.btn-warning:active:focus,.btn-warning.active:focus,.open>.dropdown-toggle.btn-warning:focus,.btn-warning:active.focus,.btn-warning.active.focus,.open>.dropdown-toggle.btn-warning.focus{color:#ffffff;background-color:#a36104;border-color:#7c4a03}.btn-warning:active,.btn-warning.active,.open>.dropdown-toggle.btn-warning{background-image:none}.btn-warning.disabled:hover,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning:hover,.btn-warning.disabled:focus,.btn-warning[disabled]:focus,fieldset[disabled] .btn-warning:focus,.btn-warning.disabled.focus,.btn-warning[disabled].focus,fieldset[disabled] .btn-warning.focus{background-color:#f89406;border-color:#f89406}.btn-warning .badge{color:#f89406;background-color:#ffffff}.btn-danger{color:#ffffff;background-color:#ee5f5b;border-color:#ee5f5b}.btn-danger:focus,.btn-danger.focus{color:#ffffff;background-color:#e9322d;border-color:#b71713}.btn-danger:hover{color:#ffffff;background-color:#e9322d;border-color:#e82924}.btn-danger:active,.btn-danger.active,.open>.dropdown-toggle.btn-danger{color:#ffffff;background-color:#e9322d;border-color:#e82924}.btn-danger:active:hover,.btn-danger.active:hover,.open>.dropdown-toggle.btn-danger:hover,.btn-danger:active:focus,.btn-danger.active:focus,.open>.dropdown-toggle.btn-danger:focus,.btn-danger:active.focus,.btn-danger.active.focus,.open>.dropdown-toggle.btn-danger.focus{color:#ffffff;background-color:#dc1c17;border-color:#b71713}.btn-danger:active,.btn-danger.active,.open>.dropdown-toggle.btn-danger{background-image:none}.btn-danger.disabled:hover,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger:hover,.btn-danger.disabled:focus,.btn-danger[disabled]:focus,fieldset[disabled] .btn-danger:focus,.btn-danger.disabled.focus,.btn-danger[disabled].focus,fieldset[disabled] .btn-danger.focus{background-color:#ee5f5b;border-color:#ee5f5b}.btn-danger .badge{color:#ee5f5b;background-color:#ffffff}.btn-link{color:#ffffff;font-weight:normal;border-radius:0}.btn-link,.btn-link:active,.btn-link.active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.btn-link,.btn-link:hover,.btn-link:focus,.btn-link:active{border-color:transparent}.btn-link:hover,.btn-link:focus{color:#ffffff;text-decoration:underline;background-color:transparent}.btn-link[disabled]:hover,fieldset[disabled] .btn-link:hover,.btn-link[disabled]:focus,fieldset[disabled] .btn-link:focus{color:#7a8288;text-decoration:none}.btn-lg,.btn-group-lg>.btn{padding:14px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.btn-sm,.btn-group-sm>.btn{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.btn-xs,.btn-group-xs>.btn{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type="submit"].btn-block,input[type="reset"].btn-block,input[type="button"].btn-block{width:100%}.fade{opacity:0;-webkit-transition:opacity 0.15s linear;-o-transition:opacity 0.15s linear;transition:opacity 0.15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}tr.collapse.in{display:table-row}tbody.collapse.in{display:table-row-group}.collapsing{position:relative;height:0;overflow:hidden;-webkit-transition-property:height, visibility;-o-transition-property:height, visibility;transition-property:height, visibility;-webkit-transition-duration:0.35s;-o-transition-duration:0.35s;transition-duration:0.35s;-webkit-transition-timing-function:ease;-o-transition-timing-function:ease;transition-timing-function:ease}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px dashed;border-top:4px solid \9;border-right:4px solid transparent;border-left:4px solid transparent}.dropup,.dropdown{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;list-style:none;font-size:14px;text-align:left;background-color:#3a3f44;border:1px solid #272b30;border:1px solid rgba(0,0,0,0.15);border-radius:4px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,0.175);box-shadow:0 6px 12px rgba(0,0,0,0.175);-webkit-background-clip:padding-box;background-clip:padding-box}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9px 0;overflow:hidden;background-color:#272b30}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:normal;line-height:1.42857143;color:#c8c8c8;white-space:nowrap}.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus{text-decoration:none;color:#ffffff;background-color:#272b30}.dropdown-menu>.active>a,.dropdown-menu>.active>a:hover,.dropdown-menu>.active>a:focus{color:#ffffff;text-decoration:none;outline:0;background-color:#272b30}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{color:#7a8288}.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{text-decoration:none;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);cursor:not-allowed}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{left:auto;right:0}.dropdown-menu-left{left:0;right:auto}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.42857143;color:#7a8288;white-space:nowrap}.dropdown-backdrop{position:fixed;left:0;right:0;bottom:0;top:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{border-top:0;border-bottom:4px dashed;border-bottom:4px solid \9;content:""}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:2px}@media (min-width:768px){.navbar-right .dropdown-menu{left:auto;right:0}.navbar-right .dropdown-menu-left{left:0;right:auto}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group>.btn,.btn-group-vertical>.btn{position:relative;float:left}.btn-group>.btn:hover,.btn-group-vertical>.btn:hover,.btn-group>.btn:focus,.btn-group-vertical>.btn:focus,.btn-group>.btn:active,.btn-group-vertical>.btn:active,.btn-group>.btn.active,.btn-group-vertical>.btn.active{z-index:2}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar .btn,.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-bottom-right-radius:0;border-top-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-bottom-left-radius:0;border-top-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-top-right-radius:0}.btn-group>.btn-group:last-child:not(:first-child)>.btn:first-child{border-bottom-left-radius:0;border-top-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-left:8px;padding-right:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-left:12px;padding-right:12px}.btn-group.open .dropdown-toggle{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn-group.open .dropdown-toggle.btn-link{-webkit-box-shadow:none;box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-right-radius:4px;border-top-left-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-top-right-radius:0;border-top-left-radius:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-right-radius:0;border-top-left-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{float:none;display:table-cell;width:1%}.btn-group-justified>.btn-group .btn{width:100%}.btn-group-justified>.btn-group .dropdown-menu{left:auto}[data-toggle="buttons"]>.btn input[type="radio"],[data-toggle="buttons"]>.btn-group>.btn input[type="radio"],[data-toggle="buttons"]>.btn input[type="checkbox"],[data-toggle="buttons"]>.btn-group>.btn input[type="checkbox"]{position:absolute;clip:rect(0, 0, 0, 0);pointer-events:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*="col-"]{float:none;padding-left:0;padding-right:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group .form-control:focus{z-index:3}.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:54px;padding:14px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:54px;line-height:54px}textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.input-group-lg>.input-group-btn>.btn,select[multiple].input-group-lg>.form-control,select[multiple].input-group-lg>.input-group-addon,select[multiple].input-group-lg>.input-group-btn>.btn{height:auto}.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-group-sm>.form-control,select.input-group-sm>.input-group-addon,select.input-group-sm>.input-group-btn>.btn{height:30px;line-height:30px}textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn,select[multiple].input-group-sm>.form-control,select[multiple].input-group-sm>.input-group-addon,select[multiple].input-group-sm>.input-group-btn>.btn{height:auto}.input-group-addon,.input-group-btn,.input-group .form-control{display:table-cell}.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child),.input-group .form-control:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:8px 12px;font-size:14px;font-weight:normal;line-height:1;color:#272b30;text-align:center;background-color:#999999;border:1px solid #cccccc;border-radius:4px}.input-group-addon.input-sm{padding:5px 10px;font-size:12px;border-radius:3px}.input-group-addon.input-lg{padding:14px 16px;font-size:18px;border-radius:6px}.input-group-addon input[type="radio"],.input-group-addon input[type="checkbox"]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle),.input-group-btn:last-child>.btn-group:not(:last-child)>.btn{border-bottom-right-radius:0;border-top-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:first-child>.btn-group:not(:first-child)>.btn{border-bottom-left-radius:0;border-top-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:hover,.input-group-btn>.btn:focus,.input-group-btn>.btn:active{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{z-index:2;margin-left:-1px}.nav{margin-bottom:0;padding-left:0;list-style:none}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:hover,.nav>li>a:focus{text-decoration:none;background-color:#3e444c}.nav>li.disabled>a{color:#7a8288}.nav>li.disabled>a:hover,.nav>li.disabled>a:focus{color:#7a8288;text-decoration:none;background-color:transparent;cursor:not-allowed}.nav .open>a,.nav .open>a:hover,.nav .open>a:focus{background-color:#3e444c;border-color:#ffffff}.nav .nav-divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #1c1e22}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#1c1e22 #1c1e22 #1c1e22}.nav-tabs>li.active>a,.nav-tabs>li.active>a:hover,.nav-tabs>li.active>a:focus{color:#ffffff;background-color:#3e444c;border:1px solid #1c1e22;border-bottom-color:transparent;cursor:default}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{text-align:center;margin-bottom:5px}.nav-tabs.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:focus{border:1px solid #1c1e22}@media (min-width:768px){.nav-tabs.nav-justified>li>a{border-bottom:1px solid #1c1e22;border-radius:4px 4px 0 0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:focus{border-bottom-color:#272b30}}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:4px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:hover,.nav-pills>li.active>a:focus{color:#ffffff;background-color:transparent}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{float:none}.nav-justified>li>a{text-align:center;margin-bottom:5px}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus{border:1px solid #1c1e22}@media (min-width:768px){.nav-tabs-justified>li>a{border-bottom:1px solid #1c1e22;border-radius:4px 4px 0 0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus{border-bottom-color:#272b30}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-right-radius:0;border-top-left-radius:0}.navbar{position:relative;min-height:50px;margin-bottom:20px;border:1px solid transparent}@media (min-width:768px){.navbar{border-radius:4px}}@media (min-width:768px){.navbar-header{float:left}}.navbar-collapse{overflow-x:visible;padding-right:15px;padding-left:15px;border-top:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1);box-shadow:inset 0 1px 0 rgba(255,255,255,0.1);-webkit-overflow-scrolling:touch}.navbar-collapse.in{overflow-y:auto}@media (min-width:768px){.navbar-collapse{width:auto;border-top:0;-webkit-box-shadow:none;box-shadow:none}.navbar-collapse.collapse{display:block !important;height:auto !important;padding-bottom:0;overflow:visible !important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{padding-left:0;padding-right:0}}.navbar-fixed-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{max-height:340px}@media (max-device-width:480px) and (orientation:landscape){.navbar-fixed-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{max-height:200px}}.container>.navbar-header,.container-fluid>.navbar-header,.container>.navbar-collapse,.container-fluid>.navbar-collapse{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.container>.navbar-header,.container-fluid>.navbar-header,.container>.navbar-collapse,.container-fluid>.navbar-collapse{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media (min-width:768px){.navbar-static-top{border-radius:0}}.navbar-fixed-top,.navbar-fixed-bottom{position:fixed;right:0;left:0;z-index:1030}@media (min-width:768px){.navbar-fixed-top,.navbar-fixed-bottom{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.navbar-brand{float:left;padding:15px 15px;font-size:18px;line-height:20px;height:50px}.navbar-brand:hover,.navbar-brand:focus{text-decoration:none}.navbar-brand>img{display:block}@media (min-width:768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;margin-right:15px;padding:9px 10px;margin-top:8px;margin-bottom:8px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:4px}.navbar-toggle:focus{outline:0}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width:768px){.navbar-toggle{display:none}}.navbar-nav{margin:7.5px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:20px}@media (max-width:767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;-webkit-box-shadow:none;box-shadow:none}.navbar-nav .open .dropdown-menu>li>a,.navbar-nav .open .dropdown-menu .dropdown-header{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:20px}.navbar-nav .open .dropdown-menu>li>a:hover,.navbar-nav .open .dropdown-menu>li>a:focus{background-image:none}}@media (min-width:768px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:15px;padding-bottom:15px}}.navbar-form{margin-left:-15px;margin-right:-15px;padding:10px 15px;border-top:1px solid transparent;border-bottom:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);margin-top:6px;margin-bottom:6px}@media (min-width:768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.navbar-form .form-control-static{display:inline-block}.navbar-form .input-group{display:inline-table;vertical-align:middle}.navbar-form .input-group .input-group-addon,.navbar-form .input-group .input-group-btn,.navbar-form .input-group .form-control{width:auto}.navbar-form .input-group>.form-control{width:100%}.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.navbar-form .radio,.navbar-form .checkbox{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.navbar-form .radio label,.navbar-form .checkbox label{padding-left:0}.navbar-form .radio input[type="radio"],.navbar-form .checkbox input[type="checkbox"]{position:relative;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width:767px){.navbar-form .form-group{margin-bottom:5px}.navbar-form .form-group:last-child{margin-bottom:0}}@media (min-width:768px){.navbar-form{width:auto;border:0;margin-left:0;margin-right:0;padding-top:0;padding-bottom:0;-webkit-box-shadow:none;box-shadow:none}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-right-radius:0;border-top-left-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{margin-bottom:0;border-top-right-radius:4px;border-top-left-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:6px;margin-bottom:6px}.navbar-btn.btn-sm{margin-top:10px;margin-bottom:10px}.navbar-btn.btn-xs{margin-top:14px;margin-bottom:14px}.navbar-text{margin-top:15px;margin-bottom:15px}@media (min-width:768px){.navbar-text{float:left;margin-left:15px;margin-right:15px}}@media (min-width:768px){.navbar-left{float:left !important}.navbar-right{float:right !important;margin-right:-15px}.navbar-right~.navbar-right{margin-right:0}}.navbar-default{background-color:#3a3f44;border-color:#2b2e32}.navbar-default .navbar-brand{color:#c8c8c8}.navbar-default .navbar-brand:hover,.navbar-default .navbar-brand:focus{color:#ffffff;background-color:none}.navbar-default .navbar-text{color:#c8c8c8}.navbar-default .navbar-nav>li>a{color:#c8c8c8}.navbar-default .navbar-nav>li>a:hover,.navbar-default .navbar-nav>li>a:focus{color:#ffffff;background-color:#272b2e}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:hover,.navbar-default .navbar-nav>.active>a:focus{color:#ffffff;background-color:#272b2e}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:hover,.navbar-default .navbar-nav>.disabled>a:focus{color:#cccccc;background-color:transparent}.navbar-default .navbar-toggle{border-color:#272b2e}.navbar-default .navbar-toggle:hover,.navbar-default .navbar-toggle:focus{background-color:#272b2e}.navbar-default .navbar-toggle .icon-bar{background-color:#c8c8c8}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:#2b2e32}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:hover,.navbar-default .navbar-nav>.open>a:focus{background-color:#272b2e;color:#ffffff}@media (max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#c8c8c8}.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus{color:#ffffff;background-color:#272b2e}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus{color:#ffffff;background-color:#272b2e}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#cccccc;background-color:transparent}}.navbar-default .navbar-link{color:#c8c8c8}.navbar-default .navbar-link:hover{color:#ffffff}.navbar-default .btn-link{color:#c8c8c8}.navbar-default .btn-link:hover,.navbar-default .btn-link:focus{color:#ffffff}.navbar-default .btn-link[disabled]:hover,fieldset[disabled] .navbar-default .btn-link:hover,.navbar-default .btn-link[disabled]:focus,fieldset[disabled] .navbar-default .btn-link:focus{color:#cccccc}.navbar-inverse{background-color:#7a8288;border-color:#62686d}.navbar-inverse .navbar-brand{color:#cccccc}.navbar-inverse .navbar-brand:hover,.navbar-inverse .navbar-brand:focus{color:#ffffff;background-color:none}.navbar-inverse .navbar-text{color:#cccccc}.navbar-inverse .navbar-nav>li>a{color:#cccccc}.navbar-inverse .navbar-nav>li>a:hover,.navbar-inverse .navbar-nav>li>a:focus{color:#ffffff;background-color:#5d6368}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:hover,.navbar-inverse .navbar-nav>.active>a:focus{color:#ffffff;background-color:#5d6368}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:hover,.navbar-inverse .navbar-nav>.disabled>a:focus{color:#cccccc;background-color:transparent}.navbar-inverse .navbar-toggle{border-color:#5d6368}.navbar-inverse .navbar-toggle:hover,.navbar-inverse .navbar-toggle:focus{background-color:#5d6368}.navbar-inverse .navbar-toggle .icon-bar{background-color:#ffffff}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#697075}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:hover,.navbar-inverse .navbar-nav>.open>a:focus{background-color:#5d6368;color:#ffffff}@media (max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#62686d}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#62686d}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#cccccc}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus{color:#ffffff;background-color:#5d6368}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus{color:#ffffff;background-color:#5d6368}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#cccccc;background-color:transparent}}.navbar-inverse .navbar-link{color:#cccccc}.navbar-inverse .navbar-link:hover{color:#ffffff}.navbar-inverse .btn-link{color:#cccccc}.navbar-inverse .btn-link:hover,.navbar-inverse .btn-link:focus{color:#ffffff}.navbar-inverse .btn-link[disabled]:hover,fieldset[disabled] .navbar-inverse .btn-link:hover,.navbar-inverse .btn-link[disabled]:focus,fieldset[disabled] .navbar-inverse .btn-link:focus{color:#cccccc}.breadcrumb{padding:8px 15px;margin-bottom:20px;list-style:none;background-color:transparent;border-radius:4px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{content:"/\00a0";padding:0 5px;color:#cccccc}.breadcrumb>.active{color:#7a8288}.pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:8px 12px;line-height:1.42857143;text-decoration:none;color:#ffffff;background-color:#3a3f44;border:1px solid rgba(0,0,0,0.6);margin-left:-1px}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-bottom-left-radius:4px;border-top-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-bottom-right-radius:4px;border-top-right-radius:4px}.pagination>li>a:hover,.pagination>li>span:hover,.pagination>li>a:focus,.pagination>li>span:focus{z-index:2;color:#ffffff;background-color:transparent;border-color:rgba(0,0,0,0.6)}.pagination>.active>a,.pagination>.active>span,.pagination>.active>a:hover,.pagination>.active>span:hover,.pagination>.active>a:focus,.pagination>.active>span:focus{z-index:3;color:#ffffff;background-color:#232628;border-color:rgba(0,0,0,0.6);cursor:default}.pagination>.disabled>span,.pagination>.disabled>span:hover,.pagination>.disabled>span:focus,.pagination>.disabled>a,.pagination>.disabled>a:hover,.pagination>.disabled>a:focus{color:#7a8288;background-color:#ffffff;border-color:rgba(0,0,0,0.6);cursor:not-allowed}.pagination-lg>li>a,.pagination-lg>li>span{padding:14px 16px;font-size:18px;line-height:1.3333333}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-bottom-left-radius:6px;border-top-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-bottom-right-radius:6px;border-top-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:12px;line-height:1.5}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-bottom-left-radius:3px;border-top-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-bottom-right-radius:3px;border-top-right-radius:3px}.pager{padding-left:0;margin:20px 0;list-style:none;text-align:center}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#3a3f44;border:1px solid rgba(0,0,0,0.6);border-radius:15px}.pager li>a:hover,.pager li>a:focus{text-decoration:none;background-color:transparent}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:hover,.pager .disabled>a:focus,.pager .disabled>span{color:#7a8288;background-color:#3a3f44;cursor:not-allowed}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:bold;line-height:1;color:#ffffff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}a.label:hover,a.label:focus{color:#ffffff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#3a3f44}.label-default[href]:hover,.label-default[href]:focus{background-color:#232628}.label-primary{background-color:#7a8288}.label-primary[href]:hover,.label-primary[href]:focus{background-color:#62686d}.label-success{background-color:#62c462}.label-success[href]:hover,.label-success[href]:focus{background-color:#42b142}.label-info{background-color:#5bc0de}.label-info[href]:hover,.label-info[href]:focus{background-color:#31b0d5}.label-warning{background-color:#f89406}.label-warning[href]:hover,.label-warning[href]:focus{background-color:#c67605}.label-danger{background-color:#ee5f5b}.label-danger[href]:hover,.label-danger[href]:focus{background-color:#e9322d}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:bold;color:#ffffff;line-height:1;vertical-align:middle;white-space:nowrap;text-align:center;background-color:#7a8288;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.btn-xs .badge,.btn-group-xs>.btn .badge{top:0;padding:1px 5px}a.badge:hover,a.badge:focus{color:#ffffff;text-decoration:none;cursor:pointer}.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#ffffff;background-color:#7a8288}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}.nav-pills>li>a>.badge{margin-left:3px}.jumbotron{padding-top:30px;padding-bottom:30px;margin-bottom:30px;color:inherit;background-color:#1c1e22}.jumbotron h1,.jumbotron .h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:21px;font-weight:200}.jumbotron>hr{border-top-color:#050506}.container .jumbotron,.container-fluid .jumbotron{border-radius:6px;padding-left:15px;padding-right:15px}.jumbotron .container{max-width:100%}@media screen and (min-width:768px){.jumbotron{padding-top:48px;padding-bottom:48px}.container .jumbotron,.container-fluid .jumbotron{padding-left:60px;padding-right:60px}.jumbotron h1,.jumbotron .h1{font-size:63px}}.thumbnail{display:block;padding:4px;margin-bottom:20px;line-height:1.42857143;background-color:#1c1e22;border:1px solid #0c0d0e;border-radius:4px;-webkit-transition:border .2s ease-in-out;-o-transition:border .2s ease-in-out;transition:border .2s ease-in-out}.thumbnail>img,.thumbnail a>img{margin-left:auto;margin-right:auto}a.thumbnail:hover,a.thumbnail:focus,a.thumbnail.active{border-color:#ffffff}.thumbnail .caption{padding:9px;color:#c8c8c8}.alert{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:bold}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable,.alert-dismissible{padding-right:35px}.alert-dismissable .close,.alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{background-color:#62c462;border-color:#62bd4f;color:#ffffff}.alert-success hr{border-top-color:#55b142}.alert-success .alert-link{color:#e6e6e6}.alert-info{background-color:#5bc0de;border-color:#3dced8;color:#ffffff}.alert-info hr{border-top-color:#2ac7d2}.alert-info .alert-link{color:#e6e6e6}.alert-warning{background-color:#f89406;border-color:#e96506;color:#ffffff}.alert-warning hr{border-top-color:#d05a05}.alert-warning .alert-link{color:#e6e6e6}.alert-danger{background-color:#ee5f5b;border-color:#ed4d63;color:#ffffff}.alert-danger hr{border-top-color:#ea364f}.alert-danger .alert-link{color:#e6e6e6}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-o-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{overflow:hidden;height:20px;margin-bottom:20px;background-color:#1c1e22;border-radius:4px;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);box-shadow:inset 0 1px 2px rgba(0,0,0,0.1)}.progress-bar{float:left;width:0%;height:100%;font-size:12px;line-height:20px;color:#ffffff;text-align:center;background-color:#7a8288;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);-webkit-transition:width 0.6s ease;-o-transition:width 0.6s ease;transition:width 0.6s ease}.progress-striped .progress-bar,.progress-bar-striped{background-image:none;-webkit-background-size:40px 40px;background-size:40px 40px}.progress.active .progress-bar,.progress-bar.active{-webkit-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar-success{background-color:#62c462}.progress-striped .progress-bar-success{background-image:none;}.progress-bar-info{background-color:#5bc0de}.progress-striped .progress-bar-info{background-image:none;}.progress-bar-warning{background-color:#f89406}.progress-striped .progress-bar-warning{background-image:none;}.progress-bar-danger{background-color:#ee5f5b}.progress-striped .progress-bar-danger{background-image:none;}.media{margin-top:15px}.media:first-child{margin-top:0}.media,.media-body{zoom:1;overflow:hidden}.media-body{width:10000px}.media-object{display:block}.media-object.img-thumbnail{max-width:none}.media-right,.media>.pull-right{padding-left:10px}.media-left,.media>.pull-left{padding-right:10px}.media-left,.media-right,.media-body{display:table-cell;vertical-align:top}.media-middle{vertical-align:middle}.media-bottom{vertical-align:bottom}.media-heading{margin-top:0;margin-bottom:5px}.media-list{padding-left:0;list-style:none}.list-group{margin-bottom:20px;padding-left:0}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#32383e;border:1px solid rgba(0,0,0,0.6)}.list-group-item:first-child{border-top-right-radius:4px;border-top-left-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}a.list-group-item,button.list-group-item{color:#c8c8c8}a.list-group-item .list-group-item-heading,button.list-group-item .list-group-item-heading{color:#ffffff}a.list-group-item:hover,button.list-group-item:hover,a.list-group-item:focus,button.list-group-item:focus{text-decoration:none;color:#c8c8c8;background-color:#3e444c}button.list-group-item{width:100%;text-align:left}.list-group-item.disabled,.list-group-item.disabled:hover,.list-group-item.disabled:focus{background-color:#999999;color:#7a8288;cursor:not-allowed}.list-group-item.disabled .list-group-item-heading,.list-group-item.disabled:hover .list-group-item-heading,.list-group-item.disabled:focus .list-group-item-heading{color:inherit}.list-group-item.disabled .list-group-item-text,.list-group-item.disabled:hover .list-group-item-text,.list-group-item.disabled:focus .list-group-item-text{color:#7a8288}.list-group-item.active,.list-group-item.active:hover,.list-group-item.active:focus{z-index:2;color:#ffffff;background-color:#3e444c;border-color:rgba(0,0,0,0.6)}.list-group-item.active .list-group-item-heading,.list-group-item.active:hover .list-group-item-heading,.list-group-item.active:focus .list-group-item-heading,.list-group-item.active .list-group-item-heading>small,.list-group-item.active:hover .list-group-item-heading>small,.list-group-item.active:focus .list-group-item-heading>small,.list-group-item.active .list-group-item-heading>.small,.list-group-item.active:hover .list-group-item-heading>.small,.list-group-item.active:focus .list-group-item-heading>.small{color:inherit}.list-group-item.active .list-group-item-text,.list-group-item.active:hover .list-group-item-text,.list-group-item.active:focus .list-group-item-text{color:#a2aab4}.list-group-item-success{color:#ffffff;background-color:#62c462}a.list-group-item-success,button.list-group-item-success{color:#ffffff}a.list-group-item-success .list-group-item-heading,button.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:hover,button.list-group-item-success:hover,a.list-group-item-success:focus,button.list-group-item-success:focus{color:#ffffff;background-color:#4fbd4f}a.list-group-item-success.active,button.list-group-item-success.active,a.list-group-item-success.active:hover,button.list-group-item-success.active:hover,a.list-group-item-success.active:focus,button.list-group-item-success.active:focus{color:#fff;background-color:#ffffff;border-color:#ffffff}.list-group-item-info{color:#ffffff;background-color:#5bc0de}a.list-group-item-info,button.list-group-item-info{color:#ffffff}a.list-group-item-info .list-group-item-heading,button.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:hover,button.list-group-item-info:hover,a.list-group-item-info:focus,button.list-group-item-info:focus{color:#ffffff;background-color:#46b8da}a.list-group-item-info.active,button.list-group-item-info.active,a.list-group-item-info.active:hover,button.list-group-item-info.active:hover,a.list-group-item-info.active:focus,button.list-group-item-info.active:focus{color:#fff;background-color:#ffffff;border-color:#ffffff}.list-group-item-warning{color:#ffffff;background-color:#f89406}a.list-group-item-warning,button.list-group-item-warning{color:#ffffff}a.list-group-item-warning .list-group-item-heading,button.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:hover,button.list-group-item-warning:hover,a.list-group-item-warning:focus,button.list-group-item-warning:focus{color:#ffffff;background-color:#df8505}a.list-group-item-warning.active,button.list-group-item-warning.active,a.list-group-item-warning.active:hover,button.list-group-item-warning.active:hover,a.list-group-item-warning.active:focus,button.list-group-item-warning.active:focus{color:#fff;background-color:#ffffff;border-color:#ffffff}.list-group-item-danger{color:#ffffff;background-color:#ee5f5b}a.list-group-item-danger,button.list-group-item-danger{color:#ffffff}a.list-group-item-danger .list-group-item-heading,button.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:hover,button.list-group-item-danger:hover,a.list-group-item-danger:focus,button.list-group-item-danger:focus{color:#ffffff;background-color:#ec4844}a.list-group-item-danger.active,button.list-group-item-danger.active,a.list-group-item-danger.active:hover,button.list-group-item-danger.active:hover,a.list-group-item-danger.active:focus,button.list-group-item-danger.active:focus{color:#fff;background-color:#ffffff;border-color:#ffffff}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:20px;background-color:#2e3338;border:1px solid transparent;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,0.05);box-shadow:0 1px 1px rgba(0,0,0,0.05)}.panel-body{padding:15px}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-right-radius:3px;border-top-left-radius:3px}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:16px;color:inherit}.panel-title>a,.panel-title>small,.panel-title>.small,.panel-title>small>a,.panel-title>.small>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#3e444c;border-top:1px solid rgba(0,0,0,0.6);border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.list-group,.panel>.panel-collapse>.list-group{margin-bottom:0}.panel>.list-group .list-group-item,.panel>.panel-collapse>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child,.panel>.panel-collapse>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-right-radius:3px;border-top-left-radius:3px}.panel>.list-group:last-child .list-group-item:last-child,.panel>.panel-collapse>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.panel-heading+.panel-collapse>.list-group .list-group-item:first-child{border-top-right-radius:0;border-top-left-radius:0}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.list-group+.panel-footer{border-top-width:0}.panel>.table,.panel>.table-responsive>.table,.panel>.panel-collapse>.table{margin-bottom:0}.panel>.table caption,.panel>.table-responsive>.table caption,.panel>.panel-collapse>.table caption{padding-left:15px;padding-right:15px}.panel>.table:first-child,.panel>.table-responsive:first-child>.table:first-child{border-top-right-radius:3px;border-top-left-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child{border-top-left-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child{border-top-right-radius:3px}.panel>.table:last-child,.panel>.table-responsive:last-child>.table:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child{border-bottom-left-radius:3px;border-bottom-right-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:3px}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive,.panel>.table+.panel-body,.panel>.table-responsive+.panel-body{border-top:1px solid #1c1e22}.panel>.table>tbody:first-child>tr:first-child th,.panel>.table>tbody:first-child>tr:first-child td{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{border:0;margin-bottom:0}.panel-group{margin-bottom:20px}.panel-group .panel{margin-bottom:0;border-radius:4px}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse>.panel-body,.panel-group .panel-heading+.panel-collapse>.list-group{border-top:1px solid rgba(0,0,0,0.6)}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid rgba(0,0,0,0.6)}.panel-default{border-color:rgba(0,0,0,0.6)}.panel-default>.panel-heading{color:#c8c8c8;background-color:#3e444c;border-color:rgba(0,0,0,0.6)}.panel-default>.panel-heading+.panel-collapse>.panel-body{border-top-color:rgba(0,0,0,0.6)}.panel-default>.panel-heading .badge{color:#3e444c;background-color:#c8c8c8}.panel-default>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:rgba(0,0,0,0.6)}.panel-primary{border-color:rgba(0,0,0,0.6)}.panel-primary>.panel-heading{color:#ffffff;background-color:#7a8288;border-color:rgba(0,0,0,0.6)}.panel-primary>.panel-heading+.panel-collapse>.panel-body{border-top-color:rgba(0,0,0,0.6)}.panel-primary>.panel-heading .badge{color:#7a8288;background-color:#ffffff}.panel-primary>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:rgba(0,0,0,0.6)}.panel-success{border-color:rgba(0,0,0,0.6)}.panel-success>.panel-heading{color:#ffffff;background-color:#62c462;border-color:rgba(0,0,0,0.6)}.panel-success>.panel-heading+.panel-collapse>.panel-body{border-top-color:rgba(0,0,0,0.6)}.panel-success>.panel-heading .badge{color:#62c462;background-color:#ffffff}.panel-success>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:rgba(0,0,0,0.6)}.panel-info{border-color:rgba(0,0,0,0.6)}.panel-info>.panel-heading{color:#ffffff;background-color:#5bc0de;border-color:rgba(0,0,0,0.6)}.panel-info>.panel-heading+.panel-collapse>.panel-body{border-top-color:rgba(0,0,0,0.6)}.panel-info>.panel-heading .badge{color:#5bc0de;background-color:#ffffff}.panel-info>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:rgba(0,0,0,0.6)}.panel-warning{border-color:rgba(0,0,0,0.6)}.panel-warning>.panel-heading{color:#ffffff;background-color:#f89406;border-color:rgba(0,0,0,0.6)}.panel-warning>.panel-heading+.panel-collapse>.panel-body{border-top-color:rgba(0,0,0,0.6)}.panel-warning>.panel-heading .badge{color:#f89406;background-color:#ffffff}.panel-warning>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:rgba(0,0,0,0.6)}.panel-danger{border-color:rgba(0,0,0,0.6)}.panel-danger>.panel-heading{color:#ffffff;background-color:#ee5f5b;border-color:rgba(0,0,0,0.6)}.panel-danger>.panel-heading+.panel-collapse>.panel-body{border-top-color:rgba(0,0,0,0.6)}.panel-danger>.panel-heading .badge{color:#ee5f5b;background-color:#ffffff}.panel-danger>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:rgba(0,0,0,0.6)}.embed-responsive{position:relative;display:block;height:0;padding:0;overflow:hidden}.embed-responsive .embed-responsive-item,.embed-responsive iframe,.embed-responsive embed,.embed-responsive object,.embed-responsive video{position:absolute;top:0;left:0;bottom:0;height:100%;width:100%;border:0}.embed-responsive-16by9{padding-bottom:56.25%}.embed-responsive-4by3{padding-bottom:75%}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#1c1e22;border:1px solid #0c0d0e;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.05);box-shadow:inset 0 1px 1px rgba(0,0,0,0.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,0.15)}.well-lg{padding:24px;border-radius:6px}.well-sm{padding:9px;border-radius:3px}.close{float:right;font-size:21px;font-weight:bold;line-height:1;color:#000000;text-shadow:0 1px 0 #ffffff;opacity:0.2;filter:alpha(opacity=20)}.close:hover,.close:focus{color:#000000;text-decoration:none;cursor:pointer;opacity:0.5;filter:alpha(opacity=50)}button.close{padding:0;cursor:pointer;background:transparent;border:0;-webkit-appearance:none}.modal-open{overflow:hidden}.modal{display:none;overflow:hidden;position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transform:translate(0, -25%);-ms-transform:translate(0, -25%);-o-transform:translate(0, -25%);transform:translate(0, -25%);-webkit-transition:-webkit-transform .3s ease-out;-o-transition:-o-transform .3s ease-out;transition:transform .3s ease-out}.modal.in .modal-dialog{-webkit-transform:translate(0, 0);-ms-transform:translate(0, 0);-o-transform:translate(0, 0);transform:translate(0, 0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#2e3338;border:1px solid #999999;border:1px solid rgba(0,0,0,0.2);border-radius:6px;-webkit-box-shadow:0 3px 9px rgba(0,0,0,0.5);box-shadow:0 3px 9px rgba(0,0,0,0.5);-webkit-background-clip:padding-box;background-clip:padding-box;outline:0}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000000}.modal-backdrop.fade{opacity:0;filter:alpha(opacity=0)}.modal-backdrop.in{opacity:0.5;filter:alpha(opacity=50)}.modal-header{padding:15px;border-bottom:1px solid #1c1e22}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.42857143}.modal-body{position:relative;padding:20px}.modal-footer{padding:20px;text-align:right;border-top:1px solid #1c1e22}.modal-footer .btn+.btn{margin-left:5px;margin-bottom:0}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{-webkit-box-shadow:0 5px 15px rgba(0,0,0,0.5);box-shadow:0 5px 15px rgba(0,0,0,0.5)}.modal-sm{width:300px}}@media (min-width:992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:1070;display:block;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-style:normal;font-weight:normal;letter-spacing:normal;line-break:auto;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;white-space:normal;word-break:normal;word-spacing:normal;word-wrap:normal;font-size:12px;opacity:0;filter:alpha(opacity=0)}.tooltip.in{opacity:0.9;filter:alpha(opacity=90)}.tooltip.top{margin-top:-3px;padding:5px 0}.tooltip.right{margin-left:3px;padding:0 5px}.tooltip.bottom{margin-top:3px;padding:5px 0}.tooltip.left{margin-left:-3px;padding:0 5px}.tooltip-inner{max-width:200px;padding:3px 8px;color:#ffffff;text-align:center;background-color:#000000;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000000}.tooltip.top-left .tooltip-arrow{bottom:0;right:5px;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000000}.tooltip.top-right .tooltip-arrow{bottom:0;left:5px;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000000}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000000}.tooltip.bottom-left .tooltip-arrow{top:0;right:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000000}.tooltip.bottom-right .tooltip-arrow{top:0;left:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000000}.popover{position:absolute;top:0;left:0;z-index:1060;display:none;max-width:276px;padding:1px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-style:normal;font-weight:normal;letter-spacing:normal;line-break:auto;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;white-space:normal;word-break:normal;word-spacing:normal;word-wrap:normal;font-size:14px;background-color:#2e3338;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #999999;border:1px solid rgba(0,0,0,0.2);border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,0.2);box-shadow:0 5px 10px rgba(0,0,0,0.2)}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover-title{margin:0;padding:8px 14px;font-size:14px;background-color:#2e3338;border-bottom:1px solid #22262a;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.popover>.arrow,.popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover>.arrow{border-width:11px}.popover>.arrow:after{border-width:10px;content:""}.popover.top>.arrow{left:50%;margin-left:-11px;border-bottom-width:0;border-top-color:#666666;border-top-color:rgba(0,0,0,0.25);bottom:-11px}.popover.top>.arrow:after{content:" ";bottom:1px;margin-left:-10px;border-bottom-width:0;border-top-color:#2e3338}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-left-width:0;border-right-color:#666666;border-right-color:rgba(0,0,0,0.25)}.popover.right>.arrow:after{content:" ";left:1px;bottom:-10px;border-left-width:0;border-right-color:#2e3338}.popover.bottom>.arrow{left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#666666;border-bottom-color:rgba(0,0,0,0.25);top:-11px}.popover.bottom>.arrow:after{content:" ";top:1px;margin-left:-10px;border-top-width:0;border-bottom-color:#2e3338}.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#666666;border-left-color:rgba(0,0,0,0.25)}.popover.left>.arrow:after{content:" ";right:1px;border-right-width:0;border-left-color:#2e3338;bottom:-10px}.carousel{position:relative}.carousel-inner{position:relative;overflow:hidden;width:100%}.carousel-inner>.item{display:none;position:relative;-webkit-transition:.6s ease-in-out left;-o-transition:.6s ease-in-out left;transition:.6s ease-in-out left}.carousel-inner>.item>img,.carousel-inner>.item>a>img{line-height:1}@media all and (transform-3d),(-webkit-transform-3d){.carousel-inner>.item{-webkit-transition:-webkit-transform .6s ease-in-out;-o-transition:-o-transform .6s ease-in-out;transition:transform .6s ease-in-out;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-perspective:1000px;perspective:1000px}.carousel-inner>.item.next,.carousel-inner>.item.active.right{-webkit-transform:translate3d(100%, 0, 0);transform:translate3d(100%, 0, 0);left:0}.carousel-inner>.item.prev,.carousel-inner>.item.active.left{-webkit-transform:translate3d(-100%, 0, 0);transform:translate3d(-100%, 0, 0);left:0}.carousel-inner>.item.next.left,.carousel-inner>.item.prev.right,.carousel-inner>.item.active{-webkit-transform:translate3d(0, 0, 0);transform:translate3d(0, 0, 0);left:0}}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;left:0;bottom:0;width:15%;opacity:0.5;filter:alpha(opacity=50);font-size:20px;color:#ffffff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,0.6);background-color:rgba(0,0,0,0)}.carousel-control.left{background-image:none;background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1)}.carousel-control.right{left:auto;right:0;background-image:none;background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1)}.carousel-control:hover,.carousel-control:focus{outline:0;color:#ffffff;text-decoration:none;opacity:0.9;filter:alpha(opacity=90)}.carousel-control .icon-prev,.carousel-control .icon-next,.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right{position:absolute;top:50%;margin-top:-10px;z-index:5;display:inline-block}.carousel-control .icon-prev,.carousel-control .glyphicon-chevron-left{left:50%;margin-left:-10px}.carousel-control .icon-next,.carousel-control .glyphicon-chevron-right{right:50%;margin-right:-10px}.carousel-control .icon-prev,.carousel-control .icon-next{width:20px;height:20px;line-height:1;font-family:serif}.carousel-control .icon-prev:before{content:'\2039'}.carousel-control .icon-next:before{content:'\203a'}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;margin-left:-30%;padding-left:0;list-style:none;text-align:center}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;border:1px solid #ffffff;border-radius:10px;cursor:pointer;background-color:#000 \9;background-color:rgba(0,0,0,0)}.carousel-indicators .active{margin:0;width:12px;height:12px;background-color:#ffffff}.carousel-caption{position:absolute;left:15%;right:15%;bottom:20px;z-index:10;padding-top:20px;padding-bottom:20px;color:#ffffff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,0.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width:768px){.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-prev,.carousel-control .icon-next{width:30px;height:30px;margin-top:-10px;font-size:30px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{margin-right:-10px}.carousel-caption{left:20%;right:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.clearfix:before,.clearfix:after,.dl-horizontal dd:before,.dl-horizontal dd:after,.container:before,.container:after,.container-fluid:before,.container-fluid:after,.row:before,.row:after,.form-horizontal .form-group:before,.form-horizontal .form-group:after,.btn-toolbar:before,.btn-toolbar:after,.btn-group-vertical>.btn-group:before,.btn-group-vertical>.btn-group:after,.nav:before,.nav:after,.navbar:before,.navbar:after,.navbar-header:before,.navbar-header:after,.navbar-collapse:before,.navbar-collapse:after,.pager:before,.pager:after,.panel-body:before,.panel-body:after,.modal-header:before,.modal-header:after,.modal-footer:before,.modal-footer:after{content:" ";display:table}.clearfix:after,.dl-horizontal dd:after,.container:after,.container-fluid:after,.row:after,.form-horizontal .form-group:after,.btn-toolbar:after,.btn-group-vertical>.btn-group:after,.nav:after,.navbar:after,.navbar-header:after,.navbar-collapse:after,.pager:after,.panel-body:after,.modal-header:after,.modal-footer:after{clear:both}.center-block{display:block;margin-left:auto;margin-right:auto}.pull-right{float:right !important}.pull-left{float:left !important}.hide{display:none !important}.show{display:block !important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none !important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-xs,.visible-sm,.visible-md,.visible-lg{display:none !important}.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block{display:none !important}@media (max-width:767px){.visible-xs{display:block !important}table.visible-xs{display:table !important}tr.visible-xs{display:table-row !important}th.visible-xs,td.visible-xs{display:table-cell !important}}@media (max-width:767px){.visible-xs-block{display:block !important}}@media (max-width:767px){.visible-xs-inline{display:inline !important}}@media (max-width:767px){.visible-xs-inline-block{display:inline-block !important}}@media (min-width:768px) and (max-width:991px){.visible-sm{display:block !important}table.visible-sm{display:table !important}tr.visible-sm{display:table-row !important}th.visible-sm,td.visible-sm{display:table-cell !important}}@media (min-width:768px) and (max-width:991px){.visible-sm-block{display:block !important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline{display:inline !important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline-block{display:inline-block !important}}@media (min-width:992px) and (max-width:1199px){.visible-md{display:block !important}table.visible-md{display:table !important}tr.visible-md{display:table-row !important}th.visible-md,td.visible-md{display:table-cell !important}}@media (min-width:992px) and (max-width:1199px){.visible-md-block{display:block !important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline{display:inline !important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline-block{display:inline-block !important}}@media (min-width:1200px){.visible-lg{display:block !important}table.visible-lg{display:table !important}tr.visible-lg{display:table-row !important}th.visible-lg,td.visible-lg{display:table-cell !important}}@media (min-width:1200px){.visible-lg-block{display:block !important}}@media (min-width:1200px){.visible-lg-inline{display:inline !important}}@media (min-width:1200px){.visible-lg-inline-block{display:inline-block !important}}@media (max-width:767px){.hidden-xs{display:none !important}}@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none !important}}@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none !important}}@media (min-width:1200px){.hidden-lg{display:none !important}}.visible-print{display:none !important}@media print{.visible-print{display:block !important}table.visible-print{display:table !important}tr.visible-print{display:table-row !important}th.visible-print,td.visible-print{display:table-cell !important}}.visible-print-block{display:none !important}@media print{.visible-print-block{display:block !important}}.visible-print-inline{display:none !important}@media print{.visible-print-inline{display:inline !important}}.visible-print-inline-block{display:none !important}@media print{.visible-print-inline-block{display:inline-block !important}}@media print{.hidden-print{display:none !important}}.navbar{background-image:none;background-repeat:no-repeat;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff484e55', endColorstr='#ff313539', GradientType=0);-webkit-filter:none;filter:none;border:1px solid rgba(0,0,0,0.6);text-shadow:1px 1px 1px rgba(0,0,0,0.3)}.navbar .navbar-nav>li>a{border-right:1px solid rgba(0,0,0,0.2);border-left:1px solid rgba(255,255,255,0.1)}.navbar .navbar-nav>li>a:hover{background-image:none;background-repeat:no-repeat;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff020202', endColorstr='#ff141618', GradientType=0);-webkit-filter:none;filter:none;border-left-color:transparent}.navbar-inverse{background-image:none;background-repeat:no-repeat;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff8a9196', endColorstr='#ff70787d', GradientType=0);-webkit-filter:none;filter:none}.navbar-inverse .badge{background-color:#5d6368}.navbar-inverse .navbar-nav>li>a:hover{background-image:none;background-repeat:no-repeat;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff404448', endColorstr='#ff53595d', GradientType=0);-webkit-filter:none;filter:none}.navbar .nav .open>a{border-color:transparent}.navbar-nav>li.active>a{border-left-color:transparent}.navbar-form{margin-left:5px;margin-right:5px}.btn,.btn:hover{text-shadow:1px 1px 1px rgba(0,0,0,0.3)}.btn-default{background-image:none;background-repeat:no-repeat;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff484e55', endColorstr='#ff313539', GradientType=0);-webkit-filter:none;filter:none}.btn-default:hover{background-image:none;background-repeat:no-repeat;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff020202', endColorstr='#ff141618', GradientType=0);-webkit-filter:none;filter:none}.btn-primary{background-image:none;background-repeat:no-repeat;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff8a9196', endColorstr='#ff70787d', GradientType=0);-webkit-filter:none;filter:none}.btn-primary:hover{background-image:none;background-repeat:no-repeat;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff404448', endColorstr='#ff53595d', GradientType=0);-webkit-filter:none;filter:none}.btn-success{background-image:none;background-repeat:no-repeat;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff78cc78', endColorstr='#ff53be53', GradientType=0);-webkit-filter:none;filter:none}.btn-success:hover{background-image:none;background-repeat:no-repeat;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff2f7d2f', endColorstr='#ff3a9a3a', GradientType=0);-webkit-filter:none;filter:none}.btn-info{background-image:none;background-repeat:no-repeat;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff74cae3', endColorstr='#ff4ab9db', GradientType=0);-webkit-filter:none;filter:none}.btn-info:hover{background-image:none;background-repeat:no-repeat;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff20829f', endColorstr='#ff279dc1', GradientType=0);-webkit-filter:none;filter:none}.btn-warning{background-image:none;background-repeat:no-repeat;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffaa123', endColorstr='#ffe48806', GradientType=0);-webkit-filter:none;filter:none}.btn-warning:hover{background-image:none;background-repeat:no-repeat;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff804d03', endColorstr='#ffa86404', GradientType=0);-webkit-filter:none;filter:none}.btn-danger{background-image:none;background-repeat:no-repeat;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff17a77', endColorstr='#ffec4d49', GradientType=0);-webkit-filter:none;filter:none}.btn-danger:hover{background-image:none;background-repeat:no-repeat;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffbb1813', endColorstr='#ffe01d17', GradientType=0);-webkit-filter:none;filter:none}.btn-link,.btn-link:hover{border-color:transparent}h1,h2,h3,h4,h5,h6{text-shadow:-1px -1px 0 rgba(0,0,0,0.3)}.text-primary,.text-primary:hover{color:#7a8288}.text-success,.text-success:hover{color:#62c462}.text-danger,.text-danger:hover{color:#ee5f5b}.text-warning,.text-warning:hover{color:#f89406}.text-info,.text-info:hover{color:#5bc0de}.table .success,.table .warning,.table .danger,.table .info{color:#fff}.table-bordered tbody tr.success td,.table-bordered tbody tr.warning td,.table-bordered tbody tr.danger td,.table-bordered tbody tr.success:hover td,.table-bordered tbody tr.warning:hover td,.table-bordered tbody tr.danger:hover td{border-color:#1c1e22}.table-responsive>.table{background-color:#2e3338}input,textarea{color:#272b30}.has-warning .help-block,.has-warning .control-label,.has-warning .radio,.has-warning .checkbox,.has-warning .radio-inline,.has-warning .checkbox-inline,.has-warning.radio label,.has-warning.checkbox label,.has-warning.radio-inline label,.has-warning.checkbox-inline label,.has-warning .form-control-feedback{color:#f89406}.has-warning .form-control,.has-warning .form-control:focus{border-color:#f89406}.has-warning .input-group-addon{background-color:#272b30;border:none}.has-error .help-block,.has-error .control-label,.has-error .radio,.has-error .checkbox,.has-error .radio-inline,.has-error .checkbox-inline,.has-error.radio label,.has-error.checkbox label,.has-error.radio-inline label,.has-error.checkbox-inline label,.has-error .form-control-feedback{color:#ee5f5b}.has-error .form-control,.has-error .form-control:focus{border-color:#ee5f5b}.has-error .input-group-addon{background-color:#272b30;border:none}.has-success .help-block,.has-success .control-label,.has-success .radio,.has-success .checkbox,.has-success .radio-inline,.has-success .checkbox-inline,.has-success.radio label,.has-success.checkbox label,.has-success.radio-inline label,.has-success.checkbox-inline label,.has-success .form-control-feedback{color:#62c462}.has-success .form-control,.has-success .form-control:focus{border-color:#62c462}.has-success .input-group-addon{background-color:#272b30;border:none}legend{color:#fff}.input-group-addon{border-color:rgba(0,0,0,0.6);text-shadow:1px 1px 1px rgba(0,0,0,0.3);background-image:none;background-repeat:no-repeat;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff484e55', endColorstr='#ff313539', GradientType=0);-webkit-filter:none;filter:none;color:#ffffff}.nav .open>a,.nav .open>a:hover,.nav .open>a:focus{border-color:rgba(0,0,0,0.6)}.nav-pills>li>a{background-image:none;background-repeat:no-repeat;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff484e55', endColorstr='#ff313539', GradientType=0);-webkit-filter:none;filter:none;border:1px solid rgba(0,0,0,0.6);text-shadow:1px 1px 1px rgba(0,0,0,0.3)}.nav-pills>li>a:hover{background-image:none;background-repeat:no-repeat;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff020202', endColorstr='#ff141618', GradientType=0);-webkit-filter:none;filter:none;border:1px solid rgba(0,0,0,0.6)}.nav-pills>li.active>a,.nav-pills>li.active>a:hover{background-color:none;background-image:none;background-repeat:no-repeat;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff020202', endColorstr='#ff141618', GradientType=0);-webkit-filter:none;filter:none;border:1px solid rgba(0,0,0,0.6)}.nav-pills>li.disabled>a,.nav-pills>li.disabled>a:hover{background-image:none;background-repeat:no-repeat;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff484e55', endColorstr='#ff313539', GradientType=0);-webkit-filter:none;filter:none}.pagination>li>a,.pagination>li>span{text-shadow:1px 1px 1px rgba(0,0,0,0.3);background-image:none;background-repeat:no-repeat;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff484e55', endColorstr='#ff313539', GradientType=0);-webkit-filter:none;filter:none}.pagination>li>a:hover,.pagination>li>span:hover{background-image:none;background-repeat:no-repeat;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff020202', endColorstr='#ff141618', GradientType=0);-webkit-filter:none;filter:none}.pagination>li.active>a,.pagination>li.active>span{background-image:none;background-repeat:no-repeat;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff020202', endColorstr='#ff141618', GradientType=0);-webkit-filter:none;filter:none}.pagination>li.disabled>a,.pagination>li.disabled>a:hover,.pagination>li.disabled>span,.pagination>li.disabled>span:hover{background-color:transparent;background-image:none;background-repeat:no-repeat;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff484e55', endColorstr='#ff313539', GradientType=0);-webkit-filter:none;filter:none}.pager>li>a{background-image:none;background-repeat:no-repeat;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff484e55', endColorstr='#ff313539', GradientType=0);-webkit-filter:none;filter:none;text-shadow:1px 1px 1px rgba(0,0,0,0.3)}.pager>li>a:hover{background-image:none;background-repeat:no-repeat;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff020202', endColorstr='#ff141618', GradientType=0);-webkit-filter:none;filter:none}.pager>li.disabled>a,.pager>li.disabled>a:hover{background-color:transparent;background-image:none;background-repeat:no-repeat;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff484e55', endColorstr='#ff313539', GradientType=0);-webkit-filter:none;filter:none}.breadcrumb{border:1px solid rgba(0,0,0,0.6);text-shadow:1px 1px 1px rgba(0,0,0,0.3);background-image:none;background-repeat:no-repeat;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff484e55', endColorstr='#ff313539', GradientType=0);-webkit-filter:none;filter:none}.alert .alert-link,.alert a{color:#fff;text-decoration:underline}.alert .close{color:#000000;text-decoration:none}a.thumbnail:hover,a.thumbnail:focus,a.thumbnail.active{border-color:#0c0d0e}a.list-group-item.active,a.list-group-item.active:hover,a.list-group-item.active:focus{border-color:rgba(0,0,0,0.6)}a.list-group-item-success.active{background-color:#62c462}a.list-group-item-success.active:hover,a.list-group-item-success.active:focus{background-color:#4fbd4f}a.list-group-item-warning.active{background-color:#f89406}a.list-group-item-warning.active:hover,a.list-group-item-warning.active:focus{background-color:#df8505}a.list-group-item-danger.active{background-color:#ee5f5b}a.list-group-item-danger.active:hover,a.list-group-item-danger.active:focus{background-color:#ec4844}.jumbotron{border:1px solid rgba(0,0,0,0.6)}.panel-primary .panel-heading,.panel-success .panel-heading,.panel-danger .panel-heading,.panel-warning .panel-heading,.panel-info .panel-heading{border-color:#000}
\ No newline at end of file diff --git a/web/dashboard.css b/web/dashboard.css index ee806ba47..8eeaa8bec 100644 --- a/web/dashboard.css +++ b/web/dashboard.css @@ -1,347 +1,306 @@ html, body { - /*font-family: Calibri,"Segoe UI","Helvetica Neue",Helvetica,Arial,sans-serif;*/ - font-family: "Helvetica Neue",Helvetica,Arial,sans-serif; - font-style: normal; - font-variant: normal; + /*font-family: Calibri,"Segoe UI","Helvetica Neue",Helvetica,Arial,sans-serif;*/ + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-style: normal; + font-variant: normal; } .morelink { - color: #765d9c; - text-decoration: none; + color: #765d9c; + text-decoration: none; } + .morelink:hover { - color: #563d7c; - text-decoration: none; + color: #563d7c; + text-decoration: none; } + .morelink:focus { - color: #765d9c; - text-decoration: none; + color: #765d9c; + text-decoration: none; } .netdata-chart-alignment { - margin-left: 55px; + margin-left: 55px; } .netdata-chart-row { - width: 100%; - text-align: center; - display: flex; - display: -webkit-flex; - display: -moz-flex; - align-items: baseline; - -moz-align-items: baseline; - -webkit-align-items: baseline; - justify-content: center; - -webkit-justify-content: center; - -moz-justify-content: center; + width: 100%; + text-align: center; + display: flex; + display: -webkit-flex; + display: -moz-flex; + align-items: baseline; + -moz-align-items: baseline; + -webkit-align-items: baseline; + justify-content: center; + -webkit-justify-content: center; + -moz-justify-content: center; } .netdata-container { - display: inline-block; - overflow: hidden; + display: inline-block; + overflow: hidden; - /* required for child elements to have absolute position */ - position: relative; - - /* width and height is given per chart with data-width and data-height */ + /* required for child elements to have absolute position */ + position: relative; + + /* width and height is given per chart with data-width and data-height */ } .netdata-aspect { - position: relative; - width: 100%; - padding: 0px; - margin: 0px; + position: relative; + width: 100%; + padding: 0px; + margin: 0px; } .netdata-container-with-legend { - display: inline-block; - overflow: hidden; + display: inline-block; + overflow: hidden; + + /* fix minimum scrollbar issue in firefox */ + min-height: 99px; - /* fix minimum scrollbar issue in firefox */ - min-height: 99px; + /* required for child elements to have absolute position */ + position: relative; - /* required for child elements to have absolute position */ - position: relative; - - /* width and height is given per chart with data-width and data-height */ + /* width and height is given per chart with data-width and data-height */ } .netdata-legend-resize-handler { - display: block; - position: absolute; - bottom: 0px; - right: 0px; - height: 15px; - width: 30px; - background-color: White; - font-size: 12px; - vertical-align: middle; - line-height: 15px; - cursor: ns-resize; - color: #DDDDDD; - text-align: center; - overflow: hidden; - z-index: 20; - padding: 0px; - margin: 0px; + display: block; + position: absolute; + bottom: 0px; + right: 0px; + height: 15px; + width: 30px; + background-color: White; + font-size: 12px; + vertical-align: middle; + line-height: 15px; + cursor: ns-resize; + color: #DDDDDD; + text-align: center; + overflow: hidden; + z-index: 20; + padding: 0px; + margin: 0px; } .netdata-legend-toolbox { - display: block; - position: absolute; - bottom: 0px; - right: 30px; - height: 15px; - width: 110px; - background-color: White; - font-size: 12px; - vertical-align: middle; - line-height: 15px; - color: #DDDDDD; - text-align: center; - overflow: hidden; - z-index: 20; - padding: 0px; - margin: 0px; + display: block; + position: absolute; + bottom: 0px; + right: 30px; + height: 15px; + width: 110px; + background-color: White; + font-size: 12px; + vertical-align: middle; + line-height: 15px; + color: #DDDDDD; + text-align: center; + overflow: hidden; + z-index: 20; + padding: 0px; + margin: 0px; } .netdata-legend-toolbox-button { - display: inline-block; - position: relative; - height: 15px; - width: 18px; - background-color: White; - font-size: 12px; - vertical-align: middle; - line-height: 15px; - color: #CDCDCD; - text-align: center; - overflow: hidden; - z-index: 21; - padding: 0px; - margin: 0px; - cursor: pointer; + display: inline-block; + position: relative; + height: 15px; + width: 18px; + background-color: White; + font-size: 12px; + vertical-align: middle; + line-height: 15px; + color: #CDCDCD; + text-align: center; + overflow: hidden; + z-index: 21; + padding: 0px; + margin: 0px; + cursor: pointer; } .netdata-message { - display: inline-block; - text-align: left; - vertical-align: top; - font-weight: bold; - font-size: x-small; - width: 100%; - height: 100%; - overflow: hidden; - background: inherit; - z-index: 0; + display: inline-block; + text-align: left; + vertical-align: top; + font-weight: bold; + font-size: x-small; + width: 100%; + height: 100%; + overflow: hidden; + background: inherit; + z-index: 0; } .netdata-message.hidden { - display: none; + display: none; } .netdata-message.icon { - color: #F8F8F8; - text-align: center; - vertical-align: middle; + color: #F8F8F8; + text-align: center; + vertical-align: middle; } .netdata-chart-legend { - position: absolute; /* within .netdata-container */ - top: 0; - right: 0; - background-color: white; - overflow: hidden; - text-overflow: ellipsis; - line-height: 14px; - display: block; - width: 140px; /* --legend-width */ - height: calc(100% - 15px); /* 10px for the resize handler and 5px for the top margin */ - font-size: 10px; - margin-top: 5px; - text-align: left; - /* width and height is calculated (depends on the appearance of the legend) */ + position: absolute; /* within .netdata-container */ + top: 0; + right: 0; + overflow: hidden; + text-overflow: ellipsis; + line-height: 14px; + display: block; + width: 140px; /* --legend-width */ + height: calc(100% - 15px); /* 10px for the resize handler and 5px for the top margin */ + font-size: 10px; + margin-top: 5px; + text-align: left; + /* width and height is calculated (depends on the appearance of the legend) */ } .netdata-legend-title-date { - font-size: 10px; - font-weight: normal; - margin-top: 0px; + font-size: 10px; + font-weight: normal; + margin-top: 0px; } + .netdata-legend-title-time { - font-size: 11px; - font-weight: bold; - margin-top: 0px; + font-size: 11px; + font-weight: bold; + margin-top: 0px; } + .netdata-legend-title-units { - position: absolute; - right: 5px; - float: right; - font-size: 10px; - vertical-align: top; - font-weight: normal; - margin-top: 0px; + position: absolute; + right: 10px; + float: right; + font-size: 11px; + vertical-align: top; + font-weight: normal; + margin-top: 0px; } + .netdata-legend-series { - position: absolute; - width: 140px; /* --legend-width */ - height: calc(100% - 50px); - overflow: hidden; - text-overflow: ellipsis; - line-height: 14px; - display: block; - font-size: 10px; - margin-top: 0px; -} - -.netdata-legend-series > .netdata-legend-series-content { - position : absolute; - overflow : scroll; - overflow-x : hidden; - top : 0; - right : 0; - bottom : 0; - left : 0; - text-overflow: ellipsis; -} -.netdata-legend-series > .netdata-legend-series-content:focus { - outline: thin dotted; -} -.netdata-legend-series > .netdata-legend-series-content::-webkit-scrollbar { - display: block; /* was 'none', but chrome was hiding content with it */ -} -.has-scrollbar > .netdata-legend-series-content::-webkit-scrollbar { - display: block; -} -.netdata-legend-series > .netdata-legend-series-pane { -/* background : rgba(0,0,0,.25);*/ - background : #DDD; - position : absolute; - width : 4px; - right : 0; - top : 0; - bottom : 0; -/* visibility : hidden\9; */ /* Target only IE7 and IE8 with this hack */ -/* opacity : .01; */ -/* -webkit-transition : .2s; */ -/* -moz-transition : .2s; */ -/* -o-transition : .2s; */ -/* transition : .2s; */ - -moz-border-radius : 4px; - -webkit-border-radius : 4px; - border-radius : 4px; -} -.netdata-legend-series > .netdata-legend-series-pane > .netdata-legend-series-slider { - background : #444; -/* background : rgba(0,0,0,.5);*/ - position : relative; - margin : 0 1px; - -moz-border-radius : 2px; - -webkit-border-radius : 2px; - border-radius : 2px; -} -.netdata-legend-series:hover > .netdata-legend-series-pane, .netdata-legend-series-pane.active, .netdata-legend-series-pane.flashed { -/* visibility : visible\9; */ /* Target only IE7 and IE8 with this hack */ -/* opacity : 0.99;*/ + position: absolute; + width: 140px; /* legend-width */ + height: calc(100% - 50px); + overflow: hidden; + text-overflow: ellipsis; + line-height: 14.5px; /* line spacing at the legend */ + display: block; + font-size: 10px; + margin-top: 0px; } .netdata-legend-name-table-line { - display: inline-block; - width: 13px; - height: 4px; - border-width: 0px; - border-bottom-width: 2px; - border-bottom-style: solid; - border-bottom-color: white; + display: inline-block; + width: 13px; + height: 4px; + border-width: 0px; + border-bottom-width: 2px; + border-bottom-style: solid; + border-bottom-color: white; } + .netdata-legend-name-table-area { - display: inline-block; - width: 13px; - height: 5px; - border-width: 1px; - border-top-width: 1px; - border-top-style: solid; - border-top-color: inherit; + display: inline-block; + width: 13px; + height: 5px; + border-width: 1px; + border-top-width: 1px; + border-top-style: solid; + border-top-color: inherit; } + .netdata-legend-name-table-stacked { - display: inline-block; - width: 13px; - height: 5px; - border-width: 1px; - border-top-width: 1px; - border-top-style: solid; - border-top-color: inherit; + display: inline-block; + width: 13px; + height: 5px; + border-width: 1px; + border-top-width: 1px; + border-top-style: solid; + border-top-color: inherit; } + .netdata-legend-name-tr { } + .netdata-legend-name-td { } + .netdata-legend-name { - text-align: left; - font-size: 10px; - font-weight: bold; - vertical-align: bottom; - margin-top: 0px; - z-index: 9; - padding: 0px; - width: 80px !important; - max-width: 80px !important; - text-overflow: ellipsis; - overflow: hidden; - white-space: nowrap; - display: inline-block; - cursor: pointer; + text-align: left; + font-size: 11px; /* legend: dimension name size */ + font-weight: bold; + vertical-align: bottom; + margin-top: 0px; + z-index: 9; + padding: 0px; + width: 80px !important; + max-width: 80px !important; + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + display: inline-block; + cursor: pointer; } .netdata-legend-value { - /*margin-left: 14px;*/ - position: absolute; - right: 5px; - float: right; - text-align: right; - font-size: 10px; - font-weight: bold; - vertical-align: bottom; - background-color: White; - margin-top: 0px; - z-index: 10; - padding: 0px; - padding-left: 15px; - cursor: pointer; - /* -webkit-font-smoothing: none; */ + /*margin-left: 14px;*/ + position: absolute; + right: 10px; + float: right; + text-align: right; + font-size: 11px; /* legend: dimension value size */ + font-weight: bold; + vertical-align: bottom; + background-color: White; + margin-top: 0px; + z-index: 10; + padding: 0px; + padding-left: 15px; + cursor: pointer; + /* -webkit-font-smoothing: none; */ } + .netdata-legend-name.not-selected { - font-weight: normal; - opacity: 0.3; + font-weight: normal; + opacity: 0.3; } .netdata-chart { - position: absolute; /* within .netdata-container */ - top: 0; /* within .netdata-container */ - left: 0; /* within .netdata-container */ - display: inline-block; - overflow: hidden; - width: 100%; - height: 100%; - z-index: 5; + position: absolute; /* within .netdata-container */ + top: 0; /* within .netdata-container */ + left: 0; /* within .netdata-container */ + display: inline-block; + overflow: hidden; + width: 100%; + height: 100%; + z-index: 5; - /* width and height is calculated (depends on the appearance of the legend) */ + /* width and height is calculated (depends on the appearance of the legend) */ } .netdata-chart-with-legend-right { - position: absolute; /* within .netdata-container */ - top: 0; /* within .netdata-container */ - left: 0; /* within .netdata-container */ - display: block; - overflow: hidden; - margin-right: 140px; /* --legend-width */ - width: calc(100% - 140px); /* --legend-width */ - height: 100%; - z-index: 5; - flex-grow: 1; + position: absolute; /* within .netdata-container */ + top: 0; /* within .netdata-container */ + left: 0; /* within .netdata-container */ + display: block; + overflow: hidden; + margin-right: 140px; /* --legend-width */ + width: calc(100% - 140px); /* --legend-width */ + height: 100%; + z-index: 5; + flex-grow: 1; - /* width and height is calculated (depends on the appearance of the legend) */ + /* width and height is calculated (depends on the appearance of the legend) */ } .netdata-peity-chart { @@ -368,175 +327,340 @@ body { } .dygraph-label-rotate-left { - text-align: center; - /* See http://caniuse.com/#feat=transforms2d */ - transform: rotate(90deg); - -webkit-transform: rotate(90deg); - -moz-transform: rotate(90deg); - -o-transform: rotate(90deg); - -ms-transform: rotate(90deg); + text-align: center; + /* See http://caniuse.com/#feat=transforms2d */ + transform: rotate(90deg); + -webkit-transform: rotate(90deg); + -moz-transform: rotate(90deg); + -o-transform: rotate(90deg); + -ms-transform: rotate(90deg); } /* For y2-axis label */ .dygraph-label-rotate-right { - text-align: center; - /* See http://caniuse.com/#feat=transforms2d */ - transform: rotate(-90deg); - -webkit-transform: rotate(-90deg); - -moz-transform: rotate(-90deg); - -o-transform: rotate(-90deg); - -ms-transform: rotate(-90deg); + text-align: center; + /* See http://caniuse.com/#feat=transforms2d */ + transform: rotate(-90deg); + -webkit-transform: rotate(-90deg); + -moz-transform: rotate(-90deg); + -o-transform: rotate(-90deg); + -ms-transform: rotate(-90deg); } .dygraph-title { - text-indent: 56px; - text-align: left; - position: absolute; - left: 0px; - top: 4px; - font-size: 11px; - font-weight: bold; - text-overflow: ellipsis; - overflow: hidden; - white-space: nowrap; + text-indent: 56px; + text-align: left; + position: absolute; + left: 0px; + top: 4px; + font-size: 11px; + font-weight: bold; + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; } /* fix for sparkline tooltip under bootstrap */ .jqstooltip { - width: auto !important; - height: auto !important; + width: auto !important; + height: auto !important; } .easyPieChart { - position: relative; - text-align: center; + position: relative; + text-align: center; } .easyPieChart canvas { - position: absolute; - top: 0; - left: 0; + position: absolute; + top: 0; + left: 0; } .easyPieChartLabel { - display: inline-block; - position: absolute; - float: left; - left: 0; - width: 100%; - text-align: center; - color: #666; - font-weight: normal; - text-shadow: #BBB 0px 0px 1px; - /* -webkit-font-smoothing: none; */ + display: inline-block; + position: absolute; + float: left; + left: 0; + width: 100%; + text-align: center; + color: #666; + font-weight: normal; + text-shadow: #BBB 0px 0px 1px; + /* -webkit-font-smoothing: none; */ } .easyPieChartTitle { - display: inline-block; - position: absolute; - float: left; - left: 0; - width: 64%; - margin-left: 18%; - text-align: center; - color: #999999; - font-weight: normal; + display: inline-block; + position: absolute; + float: left; + left: 0; + width: 64%; + margin-left: 18%; + text-align: center; + color: #999999; + font-weight: normal; } .easyPieChartUnits { - display: inline-block; - position: absolute; - float: left; - left: 0; - width: 60%; - margin-left: 20%; - text-align: center; - color: #999999; - font-weight: normal; + display: inline-block; + position: absolute; + float: left; + left: 0; + width: 60%; + margin-left: 20%; + text-align: center; + color: #999999; + font-weight: normal; } .gaugeChart { - position: relative; - text-align: center; + position: relative; + text-align: center; } .gaugeChart canvas { - position: absolute; - top: 0; - left: 0; - z-index: 0; + position: absolute; + top: 0; + left: 0; + z-index: 0; } .gaugeChartLabel { - display: inline-block; - position: absolute; - float: left; - left: 0; - width: 100%; - text-align: center; - color: #FFFFFF; - font-weight: bold; - z-index: 1; - text-shadow: #777 0px 0px 1px; - /* text-shadow: #CCC 1px 1px 0px, #CCC -1px -1px 0px, #CCC 1px -1px 0px, #CCC -1px 1px 0px; */ - /* -webkit-text-stroke: 1px #777; */ - /* -webkit-font-smoothing: none; */ + display: inline-block; + position: absolute; + float: left; + left: 0; + width: 100%; + text-align: center; + color: #FFFFFF; + font-weight: bold; + z-index: 1; + text-shadow: #777 0px 0px 1px; + /* text-shadow: #CCC 1px 1px 0px, #CCC -1px -1px 0px, #CCC 1px -1px 0px, #CCC -1px 1px 0px; */ + /* -webkit-text-stroke: 1px #777; */ + /* -webkit-font-smoothing: none; */ } .gaugeChartTitle { - display: inline-block; - position: absolute; - float: left; - left: 0; - width: 100%; - text-align: center; - color: #999999; - font-weight: bold; + display: inline-block; + position: absolute; + float: left; + left: 0; + width: 100%; + text-align: center; + color: #999999; + font-weight: bold; } .gaugeChartUnits { - display: inline-block; - position: absolute; - float: left; - left: 0; - bottom: 0; - width: 100%; - text-align: left; - margin-left: 5%; - color: #999999; - font-weight: normal; + display: inline-block; + position: absolute; + float: left; + left: 0; + bottom: 0; + width: 100%; + text-align: left; + margin-left: 5%; + color: #999999; + font-weight: normal; } .gaugeChartMin { - display: inline-block; - position: absolute; - float: left; - left: 0; - bottom: 10%; - width: 92%; - margin-left: 8%; - text-align: left; - color: #999999; - font-weight: normal; + display: inline-block; + position: absolute; + float: left; + left: 0; + bottom: 10%; + width: 92%; + margin-left: 8%; + text-align: left; + color: #999999; + font-weight: normal; } .gaugeChartMax { - display: inline-block; - position: absolute; - float: left; - left: 0; - bottom: 10%; - width: 95%; - margin-right: 5%; - text-align: right; - color: #999999; - font-weight: normal; + display: inline-block; + position: absolute; + float: left; + left: 0; + bottom: 10%; + width: 95%; + margin-right: 5%; + text-align: right; + color: #999999; + font-weight: normal; } .popover-title { - font-weight: bold; - font-size: 12px; + font-weight: bold; + font-size: 12px; } + .popover-content { - font-size: 11px; + font-size: 11px; +} + +/* ---------------------------------------------------------------------------- + perfect-scrollbar settings + */ + +.ps-container { + -ms-touch-action: auto; + touch-action: auto; + overflow: hidden !important; + -ms-overflow-style: none; +} + +@supports (-ms-overflow-style: none) { + .ps-container { + overflow: auto !important; + } +} + +@media screen and (-ms-high-contrast: active), (-ms-high-contrast: none) { + .ps-container { + overflow: auto !important; + } +} + +.ps-container.ps-active-x > .ps-scrollbar-x-rail, +.ps-container.ps-active-y > .ps-scrollbar-y-rail { + display: block; + background-color: transparent; +} + +.ps-container.ps-in-scrolling.ps-x > .ps-scrollbar-x-rail { + background-color: transparent; /* background color when dragged away */ + opacity: 0.9; +} + +.ps-container.ps-in-scrolling.ps-x > .ps-scrollbar-x-rail > .ps-scrollbar-x { + background-color: #aaa; /* scrollbar color when dragged away */ + height: 5px; +} + +.ps-container.ps-in-scrolling.ps-y > .ps-scrollbar-y-rail { + background-color: transparent; /* background color when dragged away */ + opacity: 0.9; +} + +.ps-container.ps-in-scrolling.ps-y > .ps-scrollbar-y-rail > .ps-scrollbar-y { + background-color: #aaa; /* scrollbar color when dragged away */ + width: 5px; +} + +.ps-container > .ps-scrollbar-x-rail { + display: none; + position: absolute; + /* please don't change 'position' */ + opacity: 0.2; /* the opacity when not on hover of the content */ + -webkit-transition: background-color .2s linear, opacity .2s linear; + -o-transition: background-color .2s linear, opacity .2s linear; + -moz-transition: background-color .2s linear, opacity .2s linear; + transition: background-color .2s linear, opacity .2s linear; + bottom: 0px; + /* there must be 'bottom' for ps-scrollbar-x-rail */ + height: 15px; +} + +.ps-container > .ps-scrollbar-x-rail > .ps-scrollbar-x { + position: absolute; + /* please don't change 'position' */ + background-color: #666; /* #aaa; the color on content hover */ + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; + -webkit-transition: background-color .2s linear, height .2s linear, width .2s ease-in-out, -webkit-border-radius .2s ease-in-out; + transition: background-color .2s linear, height .2s linear, width .2s ease-in-out, -webkit-border-radius .2s ease-in-out; + -o-transition: background-color .2s linear, height .2s linear, width .2s ease-in-out, border-radius .2s ease-in-out; + -moz-transition: background-color .2s linear, height .2s linear, width .2s ease-in-out, border-radius .2s ease-in-out, -moz-border-radius .2s ease-in-out; + transition: background-color .2s linear, height .2s linear, width .2s ease-in-out, border-radius .2s ease-in-out; + transition: background-color .2s linear, height .2s linear, width .2s ease-in-out, border-radius .2s ease-in-out, -webkit-border-radius .2s ease-in-out, -moz-border-radius .2s ease-in-out; + bottom: 2px; + /* there must be 'bottom' for ps-scrollbar-x */ + height: 5px; /* the width of the scrollbar */ +} + +.ps-container > .ps-scrollbar-x-rail:hover > .ps-scrollbar-x, .ps-container > .ps-scrollbar-x-rail:active > .ps-scrollbar-x { + height: 4px; +} + +.ps-container > .ps-scrollbar-y-rail { + display: none; + position: absolute; + /* please don't change 'position' */ + opacity: 0.2; /* the opacity when not on hover of the content */ + -webkit-transition: background-color .2s linear, opacity .2s linear; + -o-transition: background-color .2s linear, opacity .2s linear; + -moz-transition: background-color .2s linear, opacity .2s linear; + transition: background-color .2s linear, opacity .2s linear; + right: 0; + /* there must be 'right' for ps-scrollbar-y-rail */ + width: 15px; +} + +.ps-container > .ps-scrollbar-y-rail > .ps-scrollbar-y { + position: absolute; + /* please don't change 'position' */ + background-color: #666; /* #aaa; the color on content hover */ + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; + -webkit-transition: background-color .2s linear, height .2s linear, width .2s ease-in-out, -webkit-border-radius .2s ease-in-out; + transition: background-color .2s linear, height .2s linear, width .2s ease-in-out, -webkit-border-radius .2s ease-in-out; + -o-transition: background-color .2s linear, height .2s linear, width .2s ease-in-out, border-radius .2s ease-in-out; + -moz-transition: background-color .2s linear, height .2s linear, width .2s ease-in-out, border-radius .2s ease-in-out, -moz-border-radius .2s ease-in-out; + transition: background-color .2s linear, height .2s linear, width .2s ease-in-out, border-radius .2s ease-in-out; + transition: background-color .2s linear, height .2s linear, width .2s ease-in-out, border-radius .2s ease-in-out, -webkit-border-radius .2s ease-in-out, -moz-border-radius .2s ease-in-out; + right: 2px; + /* there must be 'right' for ps-scrollbar-y */ + width: 5px; /* the width of the scrollbar */ +} + +.ps-container > .ps-scrollbar-y-rail:hover > .ps-scrollbar-y, .ps-container > .ps-scrollbar-y-rail:active > .ps-scrollbar-y { + width: 5px; +} + +.ps-container:hover.ps-in-scrolling.ps-x > .ps-scrollbar-x-rail { + background-color: transparent; /* background color when dragged */ + opacity: 0.9; +} + +.ps-container:hover.ps-in-scrolling.ps-x > .ps-scrollbar-x-rail > .ps-scrollbar-x { + background-color: #bbb; /* scrollbar color when dragged */ + height: 5px; +} + +.ps-container:hover.ps-in-scrolling.ps-y > .ps-scrollbar-y-rail { + background-color: transparent; /* background color when dragged */ + opacity: 0.9; +} + +.ps-container:hover.ps-in-scrolling.ps-y > .ps-scrollbar-y-rail > .ps-scrollbar-y { + background-color: #bbb; /* scrollbar color when dragged */ + width: 5px; +} + +.ps-container:hover > .ps-scrollbar-x-rail, +.ps-container:hover > .ps-scrollbar-y-rail { + opacity: 0.6; +} + +.ps-container:hover > .ps-scrollbar-x-rail:hover { + background-color: transparent; /* the background color on hover of the scrollbar */ + opacity: 0.9; +} + +.ps-container:hover > .ps-scrollbar-x-rail:hover > .ps-scrollbar-x { + background-color: #999; /* scrollbar color on hover */ +} + +.ps-container:hover > .ps-scrollbar-y-rail:hover { + background-color: transparent; /* the background color on hover of the scrollbar */ + opacity: 0.9; +} + +.ps-container:hover > .ps-scrollbar-y-rail:hover > .ps-scrollbar-y { + background-color: #999; /* scrollbar color on hover */ } diff --git a/web/dashboard.html b/web/dashboard.html index 62f5d05af..342374c8d 100644 --- a/web/dashboard.html +++ b/web/dashboard.html @@ -13,7 +13,7 @@ <meta name="author" content="costa@tsaousis.gr"> <meta property="og:locale" content="en_US" /> - <meta property="og:image" content="https://my-netdata.io/images/seo-performance-512.png"/> + <meta property="og:image" content="https://cloud.githubusercontent.com/assets/2662304/19168687/f6a567be-8c19-11e6-8561-ce8d589e8346.gif"/> <meta property="og:url" content="http://my-netdata.io/"/> <meta property="og:type" content="website"/> <meta property="og:site_name" content="netdata"/> @@ -652,4 +652,4 @@ So, to avoid flashing the charts, we destroy and re-create the charts on each up <!-- <script> netdataServer = "http://box:19999"; </script> --> <!-- load the dashboard manager - it will do the rest --> -<script type="text/javascript" src="dashboard.js?v20161004-1"></script> +<script type="text/javascript" src="dashboard.js?v20170105-7"></script> diff --git a/web/dashboard.js b/web/dashboard.js index 4dd86391b..6fc294204 100644 --- a/web/dashboard.js +++ b/web/dashboard.js @@ -29,8 +29,10 @@ // netdata server already. // var netdataServer = "http://yourhost:19999"; // set your NetData server -//(function(window, document, undefined) { +// global namespace +var NETDATA = window.NETDATA || {}; +(function(window, document) { // ------------------------------------------------------------------------ // compatibility fixes @@ -53,9 +55,6 @@ }; } - // global namespace - var NETDATA = window.NETDATA || {}; - NETDATA.name2id = function(s) { return s .replace(/ /g, '_') @@ -125,12 +124,12 @@ NETDATA.themes = { white: { - bootstrap_css: NETDATA.serverDefault + 'css/bootstrap-3.3.7.min.css', - dashboard_css: NETDATA.serverDefault + 'dashboard.css?v20161002-1', + bootstrap_css: NETDATA.serverDefault + 'css/bootstrap-3.3.7.css', + dashboard_css: NETDATA.serverDefault + 'dashboard.css?v20161229-2', background: '#FFFFFF', foreground: '#000000', - grid: '#DDDDDD', - axis: '#CCCCCC', + grid: '#F0F0F0', + axis: '#F0F0F0', colors: [ '#3366CC', '#DC3912', '#109618', '#FF9900', '#990099', '#DD4477', '#3B3EAC', '#66AA00', '#0099C6', '#B82E2E', '#AAAA11', '#5574A6', '#994499', '#22AA99', '#6633CC', '#E67300', '#316395', '#8B0707', @@ -142,12 +141,12 @@ gauge_gradient: false }, slate: { - bootstrap_css: NETDATA.serverDefault + 'css/bootstrap.slate.min.css?v20161002-1', - dashboard_css: NETDATA.serverDefault + 'dashboard.slate.css?v20161002-1', + bootstrap_css: NETDATA.serverDefault + 'css/bootstrap-slate-flat-3.3.7.css?v20161229-1', + dashboard_css: NETDATA.serverDefault + 'dashboard.slate.css?v20161229-2', background: '#272b30', foreground: '#C8C8C8', - grid: '#35393e', - axis: '#35393e', + grid: '#283236', + axis: '#283236', /* colors: [ '#55bb33', '#ff2222', '#0099C6', '#faa11b', '#adbce0', '#DDDD00', '#4178ba', '#f58122', '#a5cc39', '#f58667', '#f5ef89', '#cf93c0', '#a5d18a', '#b8539d', '#3954a3', '#c8a9cf', '#c7de8a', '#fad20a', @@ -195,10 +194,7 @@ if(typeof netdataRegistry === 'undefined') { // backward compatibility - if(typeof netdataNoRegistry !== 'undefined' && netdataNoRegistry === false) - netdataRegistry = true; - else - netdataRegistry = false; + netdataRegistry = (typeof netdataNoRegistry !== 'undefined' && netdataNoRegistry === false); } if(netdataRegistry === false && typeof netdataRegistryCallback === 'function') netdataRegistry = true; @@ -249,7 +245,7 @@ // rendering the chart that is panned or zoomed). // Used with .current.global_pan_sync_time - last_resized: new Date().getTime(), // the timestamp of the last resize request + last_resized: Date.now(), // the timestamp of the last resize request last_page_scroll: 0, // the timestamp the last time the page was scrolled @@ -332,7 +328,9 @@ async_on_scroll: false, // sync/async onscroll handler onscroll_worker_duration_threshold: 30, // time in ms, to consider slow the onscroll handler - setOptionCallback: function() { ; } + retries_on_data_failures: 3, // how many retries to make if we can't fetch chart data from the server + + setOptionCallback: function() { } }, debug: { @@ -341,7 +339,7 @@ focus: false, visibility: false, chart_data_url: false, - chart_errors: false, // FIXME + chart_errors: false, // FIXME: remember to set it to false before merging chart_timing: false, chart_calls: false, libraries: false, @@ -430,7 +428,11 @@ }; NETDATA.localStorageGetRecursive = function(obj, prefix, callback) { - for(var i in obj) { + var keys = Object.keys(obj); + var len = keys.length; + while(len--) { + var i = keys[len]; + if(typeof obj[i] === 'object') { //console.log('object ' + prefix + '.' + i.toString()); NETDATA.localStorageGetRecursive(obj[i], prefix + '.' + i.toString(), callback); @@ -477,7 +479,10 @@ NETDATA.setOption('stop_updates_when_focus_is_lost', true); NETDATA.resetOptions = function() { - for(var i in NETDATA.localStorage.default) { + var keys = Object.keys(NETDATA.localStorage.default); + var len = keys.length; + while(len--) { + var i = keys[len]; var a = i.split('.'); if(a[0] === 'options') { @@ -493,16 +498,20 @@ } } } - } + }; // ---------------------------------------------------------------------------------------------------------------- if(NETDATA.options.debug.main_loop === true) console.log('welcome to NETDATA'); + NETDATA.onresizeCallback = null; NETDATA.onresize = function() { - NETDATA.options.last_resized = new Date().getTime(); + NETDATA.options.last_resized = Date.now(); NETDATA.onscroll(); + + if(typeof NETDATA.onresizeCallback === 'function') + NETDATA.onresizeCallback(); }; NETDATA.onscroll_updater_count = 0; @@ -514,7 +523,7 @@ NETDATA.onscroll_updater = function() { NETDATA.onscroll_updater_running = true; NETDATA.onscroll_updater_count++; - var start = new Date().getTime(); + var start = Date.now(); var targets = NETDATA.options.targets; var len = targets.length; @@ -546,7 +555,7 @@ targets[len].isVisible(); } - var end = new Date().getTime(); + var end = Date.now(); // console.log('scroll No ' + NETDATA.onscroll_updater_count + ' calculation took ' + (end - start).toString() + ' ms'); if(NETDATA.options.current.async_on_scroll === false) { @@ -574,7 +583,7 @@ NETDATA.onscroll = function() { // console.log('onscroll'); - NETDATA.options.last_page_scroll = new Date().getTime(); + NETDATA.options.last_page_scroll = Date.now(); NETDATA.options.auto_refresher_stop_until = 0; if(NETDATA.options.targets === null) return; @@ -641,7 +650,7 @@ NETDATA.error = function(code, msg) { NETDATA.errorLast.code = code; NETDATA.errorLast.message = msg; - NETDATA.errorLast.datetime = new Date().getTime(); + NETDATA.errorLast.datetime = Date.now(); console.log("ERROR " + code + ": " + NETDATA.errorCodes[code].message + ": " + msg); @@ -661,6 +670,119 @@ }; // ---------------------------------------------------------------------------------------------------------------- + // commonMin & commonMax + + NETDATA.commonMin = { + keys: {}, + latest: {}, + + get: function(state) { + if(typeof state.__commonMin === 'undefined') { + // get the commonMin setting + var self = $(state.element); + state.__commonMin = self.data('common-min') || null; + } + + var min = state.data.min; + var name = state.__commonMin; + + if(name === null) { + // we don't need commonMin + //state.log('no need for commonMin'); + return min; + } + + var t = this.keys[name]; + if(typeof t === 'undefined') { + // add our commonMin + this.keys[name] = {}; + t = this.keys[name]; + } + + var uuid = state.uuid; + if(typeof t[uuid] !== 'undefined') { + if(t[uuid] === min) { + //state.log('commonMin ' + state.__commonMin + ' not changed: ' + this.latest[name]); + return this.latest[name]; + } + else if(min < this.latest[name]) { + //state.log('commonMin ' + state.__commonMin + ' increased: ' + min); + t[uuid] = min; + this.latest[name] = min; + return min; + } + } + + // add our min + t[uuid] = min; + + // find the common min + var m = min; + for(var i in t) + if(t[i] < m) m = t[i]; + + //state.log('commonMin ' + state.__commonMin + ' updated: ' + m); + this.latest[name] = m; + return m; + } + }; + + NETDATA.commonMax = { + keys: {}, + latest: {}, + + get: function(state) { + if(typeof state.__commonMax === 'undefined') { + // get the commonMax setting + var self = $(state.element); + state.__commonMax = self.data('common-max') || null; + } + + var max = state.data.max; + var name = state.__commonMax; + + if(name === null) { + // we don't need commonMax + //state.log('no need for commonMax'); + return max; + } + + var t = this.keys[name]; + if(typeof t === 'undefined') { + // add our commonMax + this.keys[name] = {}; + t = this.keys[name]; + } + + var uuid = state.uuid; + if(typeof t[uuid] !== 'undefined') { + if(t[uuid] === max) { + //state.log('commonMax ' + state.__commonMax + ' not changed: ' + this.latest[name]); + return this.latest[name]; + } + else if(max > this.latest[name]) { + //state.log('commonMax ' + state.__commonMax + ' increased: ' + max); + t[uuid] = max; + this.latest[name] = max; + return max; + } + } + + // add our max + t[uuid] = max; + + // find the common max + var m = max; + for(var i in t) + if(t[i] > m) m = t[i]; + + //state.log('commonMax ' + state.__commonMax + ' updated: ' + m); + this.latest[name] = m; + return m; + } + }; + + // ---------------------------------------------------------------------------------------------------------------- // Chart Registry // When multiple charts need the same chart, we avoid downloading it @@ -670,6 +792,13 @@ // Every time we download a chart definition, we save it here with .add() // Then we try to get it back with .get(). If that fails, we download it. + NETDATA.fixHost = function(host) { + while(host.slice(-1) === '/') + host = host.substring(0, host.length - 1); + + return host; + }; + NETDATA.chartRegistry = { charts: {}, @@ -703,8 +832,7 @@ }, downloadAll: function(host, callback) { - while(host.slice(-1) === '/') - host = host.substring(0, host.length - 1); + host = NETDATA.fixHost(host); var self = this; @@ -722,13 +850,13 @@ else NETDATA.error(406, host + '/api/v1/charts'); if(typeof callback === 'function') - callback(data); + return callback(data); }) .fail(function() { NETDATA.error(405, host + '/api/v1/charts'); if(typeof callback === 'function') - callback(null); + return callback(null); }); } }; @@ -764,7 +892,7 @@ if(this.master !== null && this.master !== state) this.master.resetChart(true, true); - var now = new Date().getTime(); + var now = Date.now(); this.master = state; this.seq = now; this.force_after_ms = after; @@ -836,7 +964,7 @@ this.value_div = null; this.color = NETDATA.themes.current.foreground; - if(parent.selected_count > parent.unselected_count) + if(parent.unselected_count === 0) this.selected = true; else this.selected = false; @@ -853,7 +981,7 @@ dimensionStatus.prototype.setOptions = function(name_div, value_div, color) { this.color = color; - if(this.name_div != name_div) { + if(this.name_div !== name_div) { this.name_div = name_div; this.name_div.title = this.label; this.name_div.style.color = this.color; @@ -863,7 +991,7 @@ this.name_div.className = 'netdata-legend-name selected'; } - if(this.value_div != value_div) { + if(this.value_div !== value_div) { this.value_div = value_div; this.value_div.title = this.label; this.value_div.style.color = this.color; @@ -968,26 +1096,34 @@ }; dimensionsVisibility.prototype.invalidateAll = function() { - for(var d in this.dimensions) - this.dimensions[d].invalidate(); + var keys = Object.keys(this.dimensions); + var len = keys.length; + while(len--) + this.dimensions[keys[len]].invalidate(); }; dimensionsVisibility.prototype.selectAll = function() { - for(var d in this.dimensions) - this.dimensions[d].select(); + var keys = Object.keys(this.dimensions); + var len = keys.length; + while(len--) + this.dimensions[keys[len]].select(); }; dimensionsVisibility.prototype.countSelected = function() { - var i = 0; - for(var d in this.dimensions) - if(this.dimensions[d].isSelected()) i++; + var selected = 0; + var keys = Object.keys(this.dimensions); + var len = keys.length; + while(len--) + if(this.dimensions[keys[len]].isSelected()) selected++; - return i; + return selected; }; dimensionsVisibility.prototype.selectNone = function() { - for(var d in this.dimensions) - this.dimensions[d].unselect(); + var keys = Object.keys(this.dimensions); + var len = keys.length; + while(len--) + this.dimensions[keys[len]].unselect(); }; dimensionsVisibility.prototype.selected2BooleanArray = function(array) { @@ -995,20 +1131,19 @@ this.selected_count = 0; this.unselected_count = 0; - for(var i = 0, len = array.length; i < len ; i++) { - var ds = this.dimensions[array[i]]; + var len = array.length; + while(len--) { + var ds = this.dimensions[array[len]]; if(typeof ds === 'undefined') { // console.log(array[i] + ' is not found'); - ret.push(false); - continue; + ret.unshift(false); } - - if(ds.isSelected()) { - ret.push(true); + else if(ds.isSelected()) { + ret.unshift(true); this.selected_count++; } else { - ret.push(false); + ret.unshift(false); this.unselected_count++; } } @@ -1083,6 +1218,7 @@ // the user given dimensions of the element this.width = self.data('width') || NETDATA.chartDefaults.width; this.height = self.data('height') || NETDATA.chartDefaults.height; + this.height_original = this.height; if(this.settings_id !== null) { this.height = NETDATA.localStorageGet('chart_heights.' + this.settings_id, this.height, function(height) { @@ -1119,6 +1255,9 @@ // the chart library requested by the user this.library_name = self.data('chart-library') || NETDATA.chartDefaults.library; + // how many retries we have made to load chart data from the server + this.retries_on_data_failures = 0; + // object - the chart library used this.library = null; @@ -1140,8 +1279,7 @@ title_date: null, title_time: null, title_units: null, - nano: null, - nano_options: null, + perfect_scroller: null, // the container to apply perfect scroller to series: null }; @@ -1150,7 +1288,8 @@ this.title = self.data('title') || null; // the title of the chart this.units = self.data('units') || null; // the units of the chart dimensions - this.append_options = self.data('append-options') || null; // the units of the chart dimensions + this.append_options = self.data('append-options') || null; // additional options to pass to netdata + this.override_options = self.data('override-options') || null; // override options to pass to netdata this.running = false; // boolean - true when the chart is being refreshed now this.validated = false; // boolean - has the chart been validated? @@ -1167,6 +1306,14 @@ this.view_after = 0; this.view_before = 0; + this.value_decimal_detail = -1; + var d = self.data('decimal-digits'); + if(typeof d === 'number') { + this.value_decimal_detail = 1; + while(d-- > 0) + this.value_decimal_detail *= 10; + } + this.auto = { name: 'auto', autorefresh: true, @@ -1238,7 +1385,7 @@ that.element.innerHTML = ''; that.element_message = document.createElement('div'); - that.element_message.className = ' netdata-message hidden'; + that.element_message.className = 'netdata-message icon hidden'; that.element.appendChild(that.element_message); that.element_chart = document.createElement('div'); @@ -1268,9 +1415,9 @@ if(typeof(that.library.aspect_ratio) === 'undefined') { if(typeof(that.height) === 'string') - $(that.element).css('height', that.height); + that.element.style.height = that.height; else if(typeof(that.height) === 'number') - $(that.element).css('height', that.height + 'px'); + that.element.style.height = that.height.toString() + 'px'; } else { var w = that.element.offsetWidth; @@ -1280,13 +1427,13 @@ that.tm.last_resized = 0; } else - $(that.element).css('height', (that.element.offsetWidth * that.library.aspect_ratio / 100).toString() + 'px'); + that.element.style.height = (w * that.library.aspect_ratio / 100).toString() + 'px'; } if(NETDATA.chartDefaults.min_width !== null) $(that.element).css('min-width', NETDATA.chartDefaults.min_width); - that.tm.last_dom_created = new Date().getTime(); + that.tm.last_dom_created = Date.now(); showLoading(); }; @@ -1331,15 +1478,18 @@ that.data_before = 0; // milliseconds - the last timestamp of the data that.data_update_every = 0; // milliseconds - the frequency to update the data - that.tm.last_initialized = new Date().getTime(); + that.tm.last_initialized = Date.now(); createDOM(); that.setMode('auto'); }; var maxMessageFontSize = function() { + var screenHeight = screen.height; + var el = that.element; + // normally we want a font size, as tall as the element - var h = that.element_message.clientHeight; + var h = el.clientHeight; // but give it some air, 20% let's say, or 5 pixels min var lost = Math.max(h * 0.2, 5); @@ -1350,7 +1500,7 @@ // but check the width too // it should fit 10 characters in it - var w = that.element_message.clientWidth / 10; + var w = el.clientWidth / 10; if(h > w) { paddingTop += (h - w) / 2; h = w; @@ -1358,9 +1508,9 @@ // and don't make it too huge // 5% of the screen size is good - if(h > screen.height / 20) { - paddingTop += (h - (screen.height / 20)) / 2; - h = screen.height / 20; + if(h > screenHeight / 20) { + paddingTop += (h - (screenHeight / 20)) / 2; + h = screenHeight / 20; } // set it @@ -1368,25 +1518,17 @@ that.element_message.style.paddingTop = paddingTop.toString() + 'px'; }; - var showMessage = function(msg) { - that.element_message.className = 'netdata-message'; - that.element_message.innerHTML = msg; - that.element_message.style.fontSize = 'x-small'; - that.element_message.style.paddingTop = '0px'; - that.___messageHidden___ = undefined; - }; - var showMessageIcon = function(icon) { that.element_message.innerHTML = icon; - that.element_message.className = 'netdata-message icon'; maxMessageFontSize(); + $(that.element_message).removeClass('hidden'); that.___messageHidden___ = undefined; }; var hideMessage = function() { if(typeof that.___messageHidden___ === 'undefined') { that.___messageHidden___ = true; - that.element_message.className = 'netdata-message hidden'; + $(that.element_message).addClass('hidden'); } }; @@ -1433,7 +1575,7 @@ showRendering(); that.element_chart.style.display = 'none'; if(that.element_legend !== null) that.element_legend.style.display = 'none'; - that.tm.last_hidden = new Date().getTime(); + that.tm.last_hidden = Date.now(); // de-allocate data // This works, but I not sure there are no corner cases somewhere @@ -1459,7 +1601,7 @@ init(); } else { - that.tm.last_unhidden = new Date().getTime(); + that.tm.last_unhidden = Date.now(); that.element_chart.style.display = ''; if(that.element_legend !== null) that.element_legend.style.display = ''; resizeChart(); @@ -1544,13 +1686,13 @@ else if(typeof that.library.resize === 'function') { that.library.resize(that); - if(that.element_legend_childs.nano !== null && that.element_legend_childs.nano_options !== null) - $(that.element_legend_childs.nano).nanoScroller(); + if(that.element_legend_childs.perfect_scroller !== null) + Ps.update(that.element_legend_childs.perfect_scroller); maxMessageFontSize(); } - that.tm.last_resized = new Date().getTime(); + that.tm.last_resized = Date.now(); } }; @@ -1567,7 +1709,7 @@ if(that.settings_id !== null) NETDATA.localStorageSet('chart_heights.' + that.settings_id, h); - var now = new Date().getTime(); + var now = Date.now(); NETDATA.options.last_page_scroll = now; NETDATA.options.auto_refresher_stop_until = now + NETDATA.options.current.stop_updates_while_resizing; @@ -1602,24 +1744,54 @@ this.event_resize.chart_last_w = this.element.clientWidth; this.event_resize.chart_last_h = this.element.clientHeight; - var now = new Date().getTime(); - if(now - this.event_resize.last <= NETDATA.options.current.double_click_speed) { + var now = Date.now(); + if(now - this.event_resize.last <= NETDATA.options.current.double_click_speed && this.element_legend_childs.perfect_scroller !== null) { // double click / double tap event + // console.dir(this.element_legend_childs.content); + // console.dir(this.element_legend_childs.perfect_scroller); + // the optimal height of the chart // showing the entire legend var optimal = this.event_resize.chart_last_h - + this.element_legend_childs.content.scrollHeight - - this.element_legend_childs.content.clientHeight; + + this.element_legend_childs.perfect_scroller.scrollHeight + - this.element_legend_childs.perfect_scroller.clientHeight; // if we are not optimal, be optimal - if(this.event_resize.chart_last_h != optimal) + if(this.event_resize.chart_last_h !== optimal) { + // this.log('resize to optimal, current = ' + this.event_resize.chart_last_h.toString() + 'px, original = ' + this.event_resize.chart_original_h.toString() + 'px, optimal = ' + optimal.toString() + 'px, internal = ' + this.height_original.toString()); resizeChartToHeight(optimal.toString() + 'px'); + } - // else if we do not have the original height - // reset to the original height - else if(this.event_resize.chart_last_h != this.event_resize.chart_original_h) + // else if the current height is not the original/saved height + // reset to the original/saved height + else if(this.event_resize.chart_last_h !== this.event_resize.chart_original_h) { + // this.log('resize to original, current = ' + this.event_resize.chart_last_h.toString() + 'px, original = ' + this.event_resize.chart_original_h.toString() + 'px, optimal = ' + optimal.toString() + 'px, internal = ' + this.height_original.toString()); resizeChartToHeight(this.event_resize.chart_original_h.toString() + 'px'); + } + + // else if the current height is not the internal default height + // reset to the internal default height + else if((this.event_resize.chart_last_h.toString() + 'px') !== this.height_original) { + // this.log('resize to internal default, current = ' + this.event_resize.chart_last_h.toString() + 'px, original = ' + this.event_resize.chart_original_h.toString() + 'px, optimal = ' + optimal.toString() + 'px, internal = ' + this.height_original.toString()); + resizeChartToHeight(this.height_original.toString()); + } + + // else if the current height is not the firstchild's clientheight + // resize to it + else if(typeof this.element_legend_childs.perfect_scroller.firstChild !== 'undefined') { + var parent_rect = this.element.getBoundingClientRect(); + var content_rect = this.element_legend_childs.perfect_scroller.firstElementChild.getBoundingClientRect(); + var wanted = content_rect.top - parent_rect.top + this.element_legend_childs.perfect_scroller.firstChild.clientHeight + 18; // 15 = toolbox + 3 space + + // console.log(parent_rect); + // console.log(content_rect); + // console.log(wanted); + + // this.log('resize to firstChild, current = ' + this.event_resize.chart_last_h.toString() + 'px, original = ' + this.event_resize.chart_original_h.toString() + 'px, optimal = ' + optimal.toString() + 'px, internal = ' + this.height_original.toString() + 'px, firstChild = ' + wanted.toString() + 'px' ); + if(this.event_resize.chart_last_h !== wanted) + resizeChartToHeight(wanted.toString() + 'px'); + } } else { this.event_resize.last = now; @@ -1675,7 +1847,7 @@ var noDataToShow = function() { showMessageIcon('<i class="fa fa-warning"></i> empty'); that.legendUpdateDOM(); - that.tm.last_autorefreshed = new Date().getTime(); + that.tm.last_autorefreshed = Date.now(); // that.data_update_every = 30 * 1000; //that.element_chart.style.display = 'none'; //if(that.element_legend !== null) that.element_legend.style.display = 'none'; @@ -1705,7 +1877,7 @@ this.current.force_before_ms = null; this.current.force_after_ms = null; - this.tm.last_mode_switch = new Date().getTime(); + this.tm.last_mode_switch = Date.now(); }; // ---------------------------------------------------------------------------------------------------------------- @@ -1717,9 +1889,9 @@ return; if(typeof ms === 'number') - NETDATA.globalSelectionSync.dont_sync_before = new Date().getTime() + ms; + NETDATA.globalSelectionSync.dont_sync_before = Date.now() + ms; else - NETDATA.globalSelectionSync.dont_sync_before = new Date().getTime() + NETDATA.options.current.sync_selection_delay; + NETDATA.globalSelectionSync.dont_sync_before = Date.now() + NETDATA.options.current.sync_selection_delay; }; // can we globally apply selection sync? @@ -1727,17 +1899,14 @@ if(NETDATA.options.current.sync_selection === false) return false; - if(NETDATA.globalSelectionSync.dont_sync_before > new Date().getTime()) + if(NETDATA.globalSelectionSync.dont_sync_before > Date.now()) return false; return true; }; this.globalSelectionSyncIsMaster = function() { - if(NETDATA.globalSelectionSync.state === this) - return true; - else - return false; + return (NETDATA.globalSelectionSync.state === this); }; // this chart is the master of the global selection sync @@ -1806,12 +1975,8 @@ // sync all the visible charts to the given time // this is to be called from the chart libraries this.globalSelectionSync = function(t) { - if(this.globalSelectionSyncAbility() === false) { - if(this.debug === true) - this.log('sync: cannot sync (yet?).'); - + if(this.globalSelectionSyncAbility() === false) return; - } if(this.globalSelectionSyncIsMaster() === false) { if(this.debug === true) @@ -1819,12 +1984,8 @@ this.globalSelectionSyncBeMaster(); - if(this.globalSelectionSyncAbility() === false) { - if(this.debug === true) - this.log('sync: cannot sync (yet?).'); - + if(this.globalSelectionSyncAbility() === false) return; - } } NETDATA.globalSelectionSync.last_t = t; @@ -2042,7 +2203,7 @@ if(this.debug === true) this.log(logme + 'ACCEPTING UPDATE: current/min duration: ' + (current_duration / 1000).toString() + '/' + (this.fixed_min_duration / 1000).toString() + ', wanted duration: ' + (wanted_duration / 1000).toString() + ', duration diff: ' + (Math.round(Math.abs(current_duration - wanted_duration) / 1000)).toString() + ', movement: ' + (movement / 1000).toString() + ', tolerance: ' + (tolerance / 1000).toString() + ', returning: ' + ret); - this.current.force_update_at = new Date().getTime() + NETDATA.options.current.pan_and_zoom_delay; + this.current.force_update_at = Date.now() + NETDATA.options.current.pan_and_zoom_delay; this.current.force_after_ms = after; this.current.force_before_ms = before; NETDATA.globalPanAndZoom.setMaster(this, after, before); @@ -2053,6 +2214,9 @@ if(value === null || value === 'undefined') return '-'; if(typeof value !== 'number') return value; + if(this.value_decimal_detail !== -1) + return (Math.round(value * this.value_decimal_detail) / this.value_decimal_detail).toLocaleString(); + var abs = Math.abs(value); if(abs >= 1000) return (Math.round(value)).toLocaleString(); if(abs >= 100 ) return (Math.round(value * 10) / 10).toLocaleString(); @@ -2066,6 +2230,10 @@ if(typeof series === 'undefined') return; if(series.value === null && series.user === null) return; + /* + // this slows down firefox and edge significantly + // since it requires to use innerHTML(), instead of innerText() + // if the value has not changed, skip DOM update //if(series.last === value) return; @@ -2080,15 +2248,48 @@ else s += '<i class="fa fa-angle-left" style="width: 8px; text-align: center; overflow: hidden; vertical-align: middle;"></i>'; } else s += '<i class="fa fa-angle-right" style="width: 8px; text-align: center; overflow: hidden; vertical-align: middle;"></i>'; + series.last = v; } else { - s = r = value; + if(value === null) + s = r = ''; + else + s = r = value; + series.last = value; } + */ + + var s = this.legendFormatValue(value); + + // caching: do not update the update to show the same value again + if(s === series.last_shown_value) return; + series.last_shown_value = s; + + if(series.value !== null) series.value.innerText = s; + if(series.user !== null) series.user.innerText = s; + }; + + this.__legendSetDateString = function(date) { + if(date !== this.__last_shown_legend_date) { + this.element_legend_childs.title_date.innerText = date; + this.__last_shown_legend_date = date; + } + }; + + this.__legendSetTimeString = function(time) { + if(time !== this.__last_shown_legend_time) { + this.element_legend_childs.title_time.innerText = time; + this.__last_shown_legend_time = time; + } + }; - if(series.value !== null) series.value.innerHTML = s; - if(series.user !== null) series.user.innerHTML = r; + this.__legendSetUnitsString = function(units) { + if(units !== this.__last_shown_legend_units) { + this.element_legend_childs.title_units.innerText = units; + this.__last_shown_legend_units = units; + } }; this.legendSetDate = function(ms) { @@ -2100,24 +2301,24 @@ var d = new Date(ms); if(this.element_legend_childs.title_date) - this.element_legend_childs.title_date.innerHTML = d.toLocaleDateString(); + this.__legendSetDateString(d.toLocaleDateString()); if(this.element_legend_childs.title_time) - this.element_legend_childs.title_time.innerHTML = d.toLocaleTimeString(); + this.__legendSetTimeString(d.toLocaleTimeString()); if(this.element_legend_childs.title_units) - this.element_legend_childs.title_units.innerHTML = this.units; + this.__legendSetUnitsString(this.units) }; this.legendShowUndefined = function() { if(this.element_legend_childs.title_date) - this.element_legend_childs.title_date.innerHTML = ' '; + this.__legendSetDateString(' '); if(this.element_legend_childs.title_time) - this.element_legend_childs.title_time.innerHTML = this.chart.name; + this.__legendSetTimeString(this.chart.name); if(this.element_legend_childs.title_units) - this.element_legend_childs.title_units.innerHTML = ' '; + this.__legendSetUnitsString(' ') if(this.data && this.element_legend_childs.series !== null) { var labels = this.data.dimension_names; @@ -2177,8 +2378,9 @@ if(typeof this.colors_assigned[label] === 'undefined') { if(this.colors_available.length === 0) { - for(var i = 0, len = NETDATA.themes.current.colors.length; i < len ; i++) - this.colors_available.push(NETDATA.themes.current.colors[i]); + var len = NETDATA.themes.current.colors.length; + while(len--) + this.colors_available.unshift(NETDATA.themes.current.colors[len]); } this.colors_assigned[label] = this.colors_available.shift(); @@ -2200,8 +2402,13 @@ this.colors = new Array(); this.colors_available = new Array(); - var i, len; + // add the standard colors + var len = NETDATA.themes.current.colors.length; + while(len--) + this.colors_available.unshift(NETDATA.themes.current.colors[len]); + + // add the user supplied colors var c = $(this.element).data('colors'); // this.log('read colors: ' + c); if(typeof c !== 'undefined' && c !== null && c.length > 0) { @@ -2213,24 +2420,21 @@ var added = 0; while(added < 20) { - for(i = 0, len = c.length; i < len ; i++) { + len = c.length; + while(len--) { added++; - this.colors_available.push(c[i]); - // this.log('adding color: ' + c[i]); + this.colors_available.unshift(c[len]); + // this.log('adding color: ' + c[len]); } } } } - // push all the standard colors too - for(i = 0, len = NETDATA.themes.current.colors.length; i < len ; i++) - this.colors_available.push(NETDATA.themes.current.colors[i]); - return this.colors; }; this.legendUpdateDOM = function() { - var needed = false; + var needed = false, dim, keys, len, i; // check that the legend DOM is up to date for the downloaded dimensions if(typeof this.element_legend_childs.series !== 'object' || this.element_legend_childs.series === null) { @@ -2274,9 +2478,12 @@ if(this.colors === null) { // this is the first time we update the chart // let's assign colors to all dimensions - if(this.library.track_colors() === true) - for(var dim in this.chart.dimensions) - this._chartDimensionColor(this.chart.dimensions[dim].name); + if(this.library.track_colors() === true) { + keys = Object.keys(this.chart.dimensions); + len = keys.length; + for(i = 0; i < len ;i++) + this._chartDimensionColor(this.chart.dimensions[keys[i]].name); + } } // we will re-generate the colors for the chart // based on the selected dimensions @@ -2292,10 +2499,12 @@ var color = state._chartDimensionColor(name); var user_element = null; - var user_id = self.data('show-value-of-' + dim + '-at') || null; + var user_id = self.data('show-value-of-' + name.toLowerCase() + '-at') || null; + if(user_id === null) + user_id = self.data('show-value-of-' + dim.toLowerCase() + '-at') || null; if(user_id !== null) { user_element = document.getElementById(user_id) || null; - if(user_element === null) + if (user_element === null) state.log('Cannot find element with id: ' + user_id); } @@ -2303,7 +2512,8 @@ name: document.createElement('span'), value: document.createElement('span'), user: user_element, - last: null + last: null, + last_shown_value: null }; var label = state.element_legend_childs.series[name]; @@ -2344,18 +2554,7 @@ title_date: document.createElement('span'), title_time: document.createElement('span'), title_units: document.createElement('span'), - nano: document.createElement('div'), - nano_options: { - paneClass: 'netdata-legend-series-pane', - sliderClass: 'netdata-legend-series-slider', - contentClass: 'netdata-legend-series-content', - enabledClass: '__enabled', - flashedClass: '__flashed', - activeClass: '__active', - tabIndex: -1, - alwaysVisible: true, - sliderMinHeight: 10 - }, + perfect_scroller: document.createElement('div'), series: {} }; @@ -2363,7 +2562,7 @@ if(this.library.toolboxPanAndZoom !== null) { - function get_pan_and_zoom_step(event) { + var get_pan_and_zoom_step = function(event) { if (event.ctrlKey) return NETDATA.options.current.pan_and_zoom_factor * NETDATA.options.current.pan_and_zoom_factor_multiplier_control; @@ -2375,7 +2574,7 @@ else return NETDATA.options.current.pan_and_zoom_factor; - } + }; this.element_legend_childs.toolbox.className += ' netdata-legend-toolbox'; this.element.appendChild(this.element_legend_childs.toolbox); @@ -2553,11 +2752,11 @@ this.element_legend.appendChild(document.createElement('br')); - this.element_legend_childs.nano.className = 'netdata-legend-series'; - this.element_legend.appendChild(this.element_legend_childs.nano); + this.element_legend_childs.perfect_scroller.className = 'netdata-legend-series'; + this.element_legend.appendChild(this.element_legend_childs.perfect_scroller); content.className = 'netdata-legend-series-content'; - this.element_legend_childs.nano.appendChild(content); + this.element_legend_childs.perfect_scroller.appendChild(content); if(NETDATA.options.current.show_help === true) $(content).popover({ @@ -2585,8 +2784,7 @@ title_date: null, title_time: null, title_units: null, - nano: null, - nano_options: null, + perfect_scroller: null, series: {} }; } @@ -2596,13 +2794,15 @@ if(this.debug === true) this.log('labels from data: "' + this.element_legend_childs.series.labels_key + '"'); - for(var i = 0, len = this.data.dimension_names.length; i < len ;i++) { + for(i = 0, len = this.data.dimension_names.length; i < len ;i++) { genLabel(this, content, this.data.dimension_ids[i], this.data.dimension_names[i], i); } } else { var tmp = new Array(); - for(var dim in this.chart.dimensions) { + keys = Object.keys(this.chart.dimensions); + for(i = 0, len = keys.length; i < len ;i++) { + dim = keys[i]; tmp.push(this.chart.dimensions[dim].name); genLabel(this, content, dim, this.chart.dimensions[dim].name, i); } @@ -2621,8 +2821,22 @@ this.element_legend_childs.hidden = document.createElement('div'); el.appendChild(this.element_legend_childs.hidden); - if(this.element_legend_childs.nano !== null && this.element_legend_childs.nano_options !== null) - $(this.element_legend_childs.nano).nanoScroller(this.element_legend_childs.nano_options); + if(this.element_legend_childs.perfect_scroller !== null) { + Ps.initialize(this.element_legend_childs.perfect_scroller, { + wheelSpeed: 0.2, + wheelPropagation: true, + swipePropagation: true, + minScrollbarLength: null, + maxScrollbarLength: null, + useBothWheelAxes: false, + suppressScrollX: true, + suppressScrollY: false, + scrollXMarginOffset: 0, + scrollYMarginOffset: 0, + theme: 'default' + }); + Ps.update(this.element_legend_childs.perfect_scroller); + } this.legendShowLatestValues(); }; @@ -2733,7 +2947,12 @@ this.data_url += "&format=" + this.library.format(); this.data_url += "&points=" + (this.data_points * points_multiplier).toString(); this.data_url += "&group=" + this.method; - this.data_url += "&options=" + this.library.options(this); + + if(this.override_options !== null) + this.data_url += "&options=" + this.override_options.toString(); + else + this.data_url += "&options=" + this.library.options(this); + this.data_url += '|jsonwrap'; if(NETDATA.options.current.eliminate_zero_dimensions === true) @@ -2772,7 +2991,7 @@ this.updates_since_last_unhide++; this.updates_since_last_creation++; - var started = new Date().getTime(); + var started = Date.now(); // if the result is JSON, find the latest update-every this.data_update_every = data.view_update_every * 1000; @@ -2850,7 +3069,7 @@ NETDATA.globalSelectionSync.stop(); // update the performance counters - var now = new Date().getTime(); + var now = Date.now(); this.tm.last_updated = now; // don't update last_autorefreshed if this chart is @@ -2868,7 +3087,7 @@ NETDATA.options.auto_refresher_fast_weight += this.refresh_dt_ms; if(this.refresh_dt_element !== null) - this.refresh_dt_element.innerHTML = this.refresh_dt_ms.toString(); + this.refresh_dt_element.innerText = this.refresh_dt_ms.toString(); }; this.updateChart = function(callback) { @@ -2879,8 +3098,10 @@ if(this.debug === true) this.log('I am already updating...'); - if(typeof callback === 'function') callback(); - return false; + if(typeof callback === 'function') + return callback(); + + return; } // due to late initialization of charts and libraries @@ -2889,29 +3110,37 @@ if(this.debug === true) this.log('I am not enabled'); - if(typeof callback === 'function') callback(); - return false; + if(typeof callback === 'function') + return callback(); + + return; } if(canBeRendered() === false) { - if(typeof callback === 'function') callback(); - return false; - } + if(typeof callback === 'function') + return callback(); - if(this.chart === null) { - this.getChart(function() { that.updateChart(callback); }); - return false; + return; } + if(this.chart === null) + return this.getChart(function() { + return that.updateChart(callback); + }); + if(this.library.initialized === false) { if(this.library.enabled === true) { - this.library.initialize(function() { that.updateChart(callback); }); - return false; + return this.library.initialize(function () { + return that.updateChart(callback); + }); } else { error('chart library "' + this.library_name + '" is not available.'); - if(typeof callback === 'function') callback(); - return false; + + if(typeof callback === 'function') + return callback(); + + return; } } @@ -2933,10 +3162,15 @@ url: this.data_url, cache: false, async: true, + headers: { + 'Cache-Control': 'no-cache, no-store', + 'Pragma': 'no-cache' + }, xhrFields: { withCredentials: true } // required for the cookie }) .done(function(data) { that.xhr = undefined; + that.retries_on_data_failures = 0; if(that.debug === true) that.log('data received. updating chart.'); @@ -2946,18 +3180,28 @@ .fail(function(msg) { that.xhr = undefined; - if(msg.statusText !== 'abort') - error('data download failed for url: ' + that.data_url); + if(msg.statusText !== 'abort') { + that.retries_on_data_failures++; + if(that.retries_on_data_failures > NETDATA.options.current.retries_on_data_failures) { + // that.log('failed ' + that.retries_on_data_failures.toString() + ' times - giving up'); + that.retries_on_data_failures = 0; + error('data download failed for url: ' + that.data_url); + } + else { + that.tm.last_autorefreshed = Date.now(); + // that.log('failed ' + that.retries_on_data_failures.toString() + ' times, but I will retry'); + } + } }) .always(function() { that.xhr = undefined; NETDATA.statistics.refreshes_active--; that._updating = false; - if(typeof callback === 'function') callback(); - }); - return true; + if(typeof callback === 'function') + return callback(); + }); }; this.isVisible = function(nocache) { @@ -2971,7 +3215,7 @@ if(nocache === false && this.tm.last_visible_check > NETDATA.options.last_page_scroll) return this.___isVisible___; - this.tm.last_visible_check = new Date().getTime(); + this.tm.last_visible_check = Date.now(); var wh = window.innerHeight; var x = this.element.getBoundingClientRect(); @@ -3014,7 +3258,7 @@ }; this.canBeAutoRefreshed = function() { - var now = new Date().getTime(); + var now = Date.now(); if(this.running === true) { if(this.debug === true) @@ -3121,12 +3365,12 @@ state.running = false; if(typeof callback !== 'undefined') - callback(); + return callback(); }); } else { if(typeof callback !== 'undefined') - callback(); + return callback(); } }; @@ -3135,7 +3379,7 @@ this.chart_url = chart.url; this.data_update_every = chart.update_every * 1000; this.data_points = Math.round(this.chartWidth() / this.chartPixelsPerPoint()); - this.tm.last_info_downloaded = new Date().getTime(); + this.tm.last_info_downloaded = Date.now(); if(this.title === null) this.title = chart.title; @@ -3149,7 +3393,9 @@ this.chart = NETDATA.chartRegistry.get(this.host, this.id); if(this.chart) { this._defaultsFromDownloadedChart(this.chart); - if(typeof callback === 'function') callback(); + + if(typeof callback === 'function') + return callback(); } else { this.chart_url = "/api/v1/chart?chart=" + this.id; @@ -3173,7 +3419,8 @@ error('chart not found on url "' + that.chart_url + '"'); }) .always(function() { - if(typeof callback === 'function') callback(); + if(typeof callback === 'function') + return callback(); }); } }; @@ -3234,7 +3481,7 @@ script.src = NETDATA.jQuery; // script.onabort = onError; - script.onerror = function(err, t) { NETDATA.error(101, NETDATA.jQuery); }; + script.onerror = function() { NETDATA.error(101, NETDATA.jQuery); }; if(typeof callback === "function") script.onload = callback; @@ -3242,7 +3489,7 @@ s.parentNode.insertBefore(script, s); } else if(typeof callback === "function") - callback(); + return callback(); }; NETDATA._loadCSS = function(filename) { @@ -3319,10 +3566,12 @@ }; NETDATA.pause = function(callback) { - if(NETDATA.options.pause === true) - callback(); - else - NETDATA.options.pauseCallback = callback; + if(typeof callback === 'function') { + if (NETDATA.options.pause === true) + return callback(); + else + NETDATA.options.pauseCallback = callback; + } }; NETDATA.unpause = function() { @@ -3485,7 +3734,7 @@ }; NETDATA.parseDom = function(callback) { - NETDATA.options.last_page_scroll = new Date().getTime(); + NETDATA.options.last_page_scroll = Date.now(); NETDATA.options.updated_dom = false; var targets = $('div[data-netdata]'); //.filter(':visible'); @@ -3501,7 +3750,8 @@ NETDATA.options.targets.push(NETDATA.chartState(targets[len])); } - if(typeof callback === 'function') callback(); + if(typeof callback === 'function') + return callback(); }; // this is the main function - where everything starts @@ -3577,13 +3827,13 @@ }) .always(function() { if(typeof callback === "function") - callback(); + return callback(); }); } else { NETDATA.chartLibraries.peity.enabled = false; if(typeof callback === "function") - callback(); + return callback(); } }; @@ -3639,13 +3889,13 @@ }) .always(function() { if(typeof callback === "function") - callback(); + return callback(); }); } else { NETDATA.chartLibraries.sparkline.enabled = false; if(typeof callback === "function") - callback(); + return callback(); } }; @@ -3661,7 +3911,7 @@ var self = $(state.element); var type = self.data('sparkline-type') || 'line'; var lineColor = self.data('sparkline-linecolor') || state.chartColors()[0]; - var fillColor = self.data('sparkline-fillcolor') || (state.chart.chart_type === 'line')?NETDATA.themes.current.background:NETDATA.colorLuminance(lineColor, NETDATA.chartDefaults.fill_luminance); + var fillColor = self.data('sparkline-fillcolor') || ((state.chart.chart_type === 'line')?NETDATA.themes.current.background:NETDATA.colorLuminance(lineColor, NETDATA.chartDefaults.fill_luminance)); var chartRangeMin = self.data('sparkline-chartrangemin') || undefined; var chartRangeMax = self.data('sparkline-chartrangemax') || undefined; var composite = self.data('sparkline-composite') || undefined; @@ -3709,6 +3959,8 @@ if(minSpotColor === 'disable') minSpotColor=''; if(maxSpotColor === 'disable') maxSpotColor=''; + // state.log('sparkline type ' + type + ', lineColor: ' + lineColor + ', fillColor: ' + fillColor); + state.sparkline_options = { type: type, lineColor: lineColor, @@ -3824,7 +4076,7 @@ }) .always(function() { if(typeof callback === "function") - callback(); + return callback(); }); }; @@ -3847,13 +4099,13 @@ if(NETDATA.chartLibraries.dygraph.enabled === true && NETDATA.options.current.smooth_plot === true) NETDATA.dygraphSmoothInitialize(callback); else if(typeof callback === "function") - callback(); + return callback(); }); } else { NETDATA.chartLibraries.dygraph.enabled = false; if(typeof callback === "function") - callback(); + return callback(); } }; @@ -3884,25 +4136,33 @@ state.log('dygraphChartUpdate() forced zoom update'); options.dateWindow = (state.requested_padding !== null)?[ state.view_after, state.view_before ]:null; - options.valueRange = state.dygraph_options.valueRange; options.isZoomedIgnoreProgrammaticZoom = true; state.dygraph_force_zoom = false; } else if(state.current.name !== 'auto') { if(NETDATA.options.debug.dygraph === true || state.debug === true) state.log('dygraphChartUpdate() loose update'); - - options.valueRange = state.dygraph_options.valueRange; } else { if(NETDATA.options.debug.dygraph === true || state.debug === true) state.log('dygraphChartUpdate() strict update'); options.dateWindow = (state.requested_padding !== null)?[ state.view_after, state.view_before ]:null; - options.valueRange = state.dygraph_options.valueRange; options.isZoomedIgnoreProgrammaticZoom = true; } + options.valueRange = state.dygraph_options.valueRange; + + var oldMax = null, oldMin = null; + if(state.__commonMin !== null) { + state.data.min = state.dygraph_instance.axes_[0].extremeRange[0]; + oldMin = options.valueRange[0] = NETDATA.commonMin.get(state); + } + if(state.__commonMax !== null) { + state.data.max = state.dygraph_instance.axes_[0].extremeRange[1]; + oldMax = options.valueRange[1] = NETDATA.commonMax.get(state); + } + if(state.dygraph_smooth_eligible === true) { if((NETDATA.options.current.smooth_plot === true && state.dygraph_options.plotter !== smoothPlotter) || (NETDATA.options.current.smooth_plot === false && state.dygraph_options.plotter === smoothPlotter)) { @@ -3913,7 +4173,24 @@ dygraph.updateOptions(options); - state.dygraph_last_rendered = new Date().getTime(); + var redraw = false; + if(oldMin !== null && oldMin > state.dygraph_instance.axes_[0].extremeRange[0]) { + state.data.min = state.dygraph_instance.axes_[0].extremeRange[0]; + options.valueRange[0] = NETDATA.commonMin.get(state); + redraw = true; + } + if(oldMax !== null && oldMax < state.dygraph_instance.axes_[0].extremeRange[1]) { + state.data.max = state.dygraph_instance.axes_[0].extremeRange[1]; + options.valueRange[1] = NETDATA.commonMax.get(state); + redraw = true; + } + + if(redraw === true) { + // state.log('forcing redraw to adapt to common- min/max'); + dygraph.updateOptions(options); + } + + state.dygraph_last_rendered = Date.now(); return true; }; @@ -3947,7 +4224,7 @@ title: self.data('dygraph-title') || state.title, titleHeight: self.data('dygraph-titleheight') || 19, - legend: self.data('dygraph-legend') || 'always', // 'onmouseover', + legend: self.data('dygraph-legend') || 'always', // we need this to get selection events labels: data.result.labels, labelsDiv: self.data('dygraph-labelsdiv') || state.element_legend_childs.hidden, labelsDivStyles: self.data('dygraph-labelsdivstyles') || { 'fontSize':'1px' }, @@ -3963,7 +4240,7 @@ xRangePad: self.data('dygraph-xrangepad') || 0, yRangePad: self.data('dygraph-yrangepad') || 1, - valueRange: self.data('dygraph-valuerange') || null, + valueRange: self.data('dygraph-valuerange') || [ null, null ], ylabel: state.units, yLabelWidth: self.data('dygraph-ylabelwidth') || 12, @@ -3992,19 +4269,19 @@ strokeBorderColor: self.data('dygraph-strokebordercolor') || NETDATA.themes.current.background, strokeBorderWidth: self.data('dygraph-strokeborderwidth') || (chart_type === 'stacked')?0.0:0.0, - fillGraph: self.data('dygraph-fillgraph') || (chart_type === 'area' || chart_type === 'stacked')?true:false, - fillAlpha: self.data('dygraph-fillalpha') || (chart_type === 'stacked')?NETDATA.options.current.color_fill_opacity_stacked:NETDATA.options.current.color_fill_opacity_area, - stackedGraph: self.data('dygraph-stackedgraph') || (chart_type === 'stacked')?true:false, + fillGraph: self.data('dygraph-fillgraph') || ((chart_type === 'area' || chart_type === 'stacked')?true:false), + fillAlpha: self.data('dygraph-fillalpha') || ((chart_type === 'stacked')?NETDATA.options.current.color_fill_opacity_stacked:NETDATA.options.current.color_fill_opacity_area), + stackedGraph: self.data('dygraph-stackedgraph') || ((chart_type === 'stacked')?true:false), stackedGraphNaNFill: self.data('dygraph-stackedgraphnanfill') || 'none', drawAxis: self.data('dygraph-drawaxis') || true, axisLabelFontSize: self.data('dygraph-axislabelfontsize') || 10, axisLineColor: self.data('dygraph-axislinecolor') || NETDATA.themes.current.axis, - axisLineWidth: self.data('dygraph-axislinewidth') || 0.3, + axisLineWidth: self.data('dygraph-axislinewidth') || 1.0, drawGrid: self.data('dygraph-drawgrid') || true, gridLinePattern: self.data('dygraph-gridlinepattern') || null, - gridLineWidth: self.data('dygraph-gridlinewidth') || 0.4, + gridLineWidth: self.data('dygraph-gridlinewidth') || 1.0, gridLineColor: self.data('dygraph-gridlinecolor') || NETDATA.themes.current.grid, maxNumberWidth: self.data('dygraph-maxnumberwidth') || 8, @@ -4026,9 +4303,12 @@ return NETDATA.zeropad(d.getHours()) + ":" + NETDATA.zeropad(d.getMinutes()) + ":" + NETDATA.zeropad(d.getSeconds()); }, valueFormatter: function (ms) { - var d = new Date(ms); - return d.toLocaleDateString() + ' ' + d.toLocaleTimeString(); - // return NETDATA.zeropad(d.getHours()) + ":" + NETDATA.zeropad(d.getMinutes()) + ":" + NETDATA.zeropad(d.getSeconds()); + //var d = new Date(ms); + //return d.toLocaleDateString() + ' ' + d.toLocaleTimeString(); + + // no need to return anything here + return ' '; + } }, y: { @@ -4054,8 +4334,10 @@ var i = data.series.length; while(i--) { var series = data.series[i]; - if(!series.isVisible) continue; - state.legendSetLabelValue(series.label, series.y); + if(series.isVisible === true) + state.legendSetLabelValue(series.label, series.y); + else + state.legendSetLabelValue(series.label, null); } } @@ -4165,6 +4447,7 @@ state.globalSelectionSyncStop(); state.globalSelectionSyncDelay(); state.setMode('pan'); + context.is2DPan = false; Dygraph.movePan(event, dygraph, context); } else if(context.isZooming) { @@ -4201,9 +4484,9 @@ state.log('interactionModel.dblclick()'); NETDATA.resetAllCharts(state); }, - mousewheel: function(event, dygraph, context) { + wheel: function(event, dygraph, context) { if(NETDATA.options.debug.dygraph === true || state.debug === true) - state.log('interactionModel.mousewheel()'); + state.log('interactionModel.wheel()'); // Take the offset of a mouse event on the dygraph canvas and // convert it to a pair of percentages from the bottom left. @@ -4271,7 +4554,15 @@ state.globalSelectionSyncDelay(); // http://dygraphs.com/gallery/interaction-api.js - var normal = (event.detail) ? event.detail * -1 : event.wheelDelta / 40; + var normal_def; + if(typeof event.wheelDelta === 'number' && !isNaN(event.wheelDelta)) + // chrome + normal_def = event.wheelDelta / 40; + else + // firefox + normal_def = event.deltaY * -1.2; + + var normal = (event.detail) ? event.detail * -1 : normal_def; var percentage = normal / 50; if (!(event.offsetX && event.offsetY)){ @@ -4284,7 +4575,6 @@ var yPct = percentages[1]; var new_x_range = zoomRange(dygraph, percentage, xPct, yPct); - var after = new_x_range[0]; var before = new_x_range[1]; @@ -4320,7 +4610,7 @@ // the internal default of dygraphs context.touchDirections = { x: true, y: false }; - state.dygraph_last_touch_start = new Date().getTime(); + state.dygraph_last_touch_start = Date.now(); state.dygraph_last_touch_move = 0; if(typeof event.touches[0].pageX === 'number') @@ -4335,7 +4625,7 @@ state.dygraph_user_action = true; Dygraph.defaultInteractionModel.touchmove(event, dygraph, context); - state.dygraph_last_touch_move = new Date().getTime(); + state.dygraph_last_touch_move = Date.now(); }, touchend: function(event, dygraph, context) { if(NETDATA.options.debug.dygraph === true || state.debug === true) @@ -4354,7 +4644,7 @@ } // if it was double tap within double click time, reset the charts - var now = new Date().getTime(); + var now = Date.now(); if(typeof state.dygraph_last_touch_end !== 'undefined') { if(state.dygraph_last_touch_move === 0) { var dt = now - state.dygraph_last_touch_end; @@ -4395,7 +4685,18 @@ state.dygraph_force_zoom = false; state.dygraph_user_action = false; - state.dygraph_last_rendered = new Date().getTime(); + state.dygraph_last_rendered = Date.now(); + + if(typeof state.dygraph_instance.axes_[0].extremeRange !== 'undefined') { + state.__commonMin = self.data('common-min') || null; + state.__commonMax = self.data('common-max') || null; + } + else { + state.log('incompatible version of dygraphs detected'); + state.__commonMin = null; + state.__commonMax = null; + } + return true; }; @@ -4415,7 +4716,7 @@ else { NETDATA.chartLibraries.morris.enabled = false; if(typeof callback === "function") - callback(); + return callback(); } } else { @@ -4436,14 +4737,14 @@ }) .always(function() { if(typeof callback === "function") - callback(); + return callback(); }); } } else { NETDATA.chartLibraries.morris.enabled = false; if(typeof callback === "function") - callback(); + return callback(); } }; @@ -4502,13 +4803,13 @@ }) .always(function() { if(typeof callback === "function") - callback(); + return callback(); }); } else { NETDATA.chartLibraries.raphael.enabled = false; if(typeof callback === "function") - callback(); + return callback(); } }; @@ -4546,7 +4847,7 @@ else { NETDATA.chartLibraries.c3.enabled = false; if(typeof callback === "function") - callback(); + return callback(); } } else { @@ -4567,14 +4868,14 @@ }) .always(function() { if(typeof callback === "function") - callback(); + return callback(); }); } } else { NETDATA.chartLibraries.c3.enabled = false; if(typeof callback === "function") - callback(); + return callback(); } }; @@ -4666,13 +4967,13 @@ }) .always(function() { if(typeof callback === "function") - callback(); + return callback(); }); } else { NETDATA.chartLibraries.d3.enabled = false; if(typeof callback === "function") - callback(); + return callback(); } }; @@ -4706,13 +5007,13 @@ NETDATA.chartLibraries.google.enabled = false; NETDATA.error(100, NETDATA.google_js); if(typeof callback === "function") - callback(); + return callback(); }); } else { NETDATA.chartLibraries.google.enabled = false; if(typeof callback === "function") - callback(); + return callback(); } }; @@ -4816,14 +5117,28 @@ // ---------------------------------------------------------------------------------------------------------------- - NETDATA.percentFromValueMax = function(value, max) { - if(value === null) value = 0; + NETDATA.easypiechartPercentFromValueMinMax = function(value, min, max) { + if(typeof value !== 'number') value = 0; + if(typeof min !== 'number') min = 0; + if(typeof max !== 'number') max = 0; + + if(min > value) min = value; if(max < value) max = value; + // make sure it is zero based + if(min > 0) min = 0; + if(max < 0) max = 0; + var pcent = 0; - if(max !== 0) { - pcent = Math.round(value * 100 / max); - if(pcent === 0 && value > 0) pcent = 1; + if(value >= 0) { + if(max !== 0) + pcent = Math.round(value * 100 / max); + if(pcent === 0) pcent = 0.1; + } + else { + if(min !== 0) + pcent = Math.round(-value * 100 / min); + if(pcent === 0) pcent = -0.1; } return pcent; @@ -4849,13 +5164,13 @@ }) .always(function() { if(typeof callback === "function") - callback(); + return callback(); }) } else { NETDATA.chartLibraries.easypiechart.enabled = false; if(typeof callback === "function") - callback(); + return callback(); } }; @@ -4871,7 +5186,7 @@ NETDATA.easypiechartChartUpdate(state, state.data); } else { - state.easyPieChartLabel.innerHTML = state.legendFormatValue(null); + state.easyPieChartLabel.innerText = state.legendFormatValue(null); state.easyPieChart_instance.update(0); } state.easyPieChart_instance.enableAnimation(); @@ -4896,12 +5211,13 @@ } var value = state.data.result[state.data.result.length - 1 - slot]; - var max = (state.easyPieChartMax === null)?state.data.max:state.easyPieChartMax; - var pcent = NETDATA.percentFromValueMax(value, max); + var min = (state.easyPieChartMin === null)?NETDATA.commonMin.get(state):state.easyPieChartMin; + var max = (state.easyPieChartMax === null)?NETDATA.commonMax.get(state):state.easyPieChartMax; + var pcent = NETDATA.easypiechartPercentFromValueMinMax(value, min, max); state.easyPieChartEvent.value = value; state.easyPieChartEvent.pcent = pcent; - state.easyPieChartLabel.innerHTML = state.legendFormatValue(value); + state.easyPieChartLabel.innerText = state.legendFormatValue(value); if(state.easyPieChartEvent.timer === null) { state.easyPieChart_instance.disableAnimation(); @@ -4916,20 +5232,20 @@ }; NETDATA.easypiechartChartUpdate = function(state, data) { - var value, max, pcent; + var value, min, max, pcent; if(NETDATA.globalPanAndZoom.isActive() === true || state.isAutoRefreshable() === false) { value = null; - max = 0; pcent = 0; } else { value = data.result[0]; - max = (state.easyPieChartMax === null)?data.max:state.easyPieChartMax; - pcent = NETDATA.percentFromValueMax(value, max); + min = (state.easyPieChartMin === null)?NETDATA.commonMin.get(state):state.easyPieChartMin; + max = (state.easyPieChartMax === null)?NETDATA.commonMax.get(state):state.easyPieChartMax; + pcent = NETDATA.easypiechartPercentFromValueMinMax(value, min, max); } - state.easyPieChartLabel.innerHTML = state.legendFormatValue(value); + state.easyPieChartLabel.innerText = state.legendFormatValue(value); state.easyPieChart_instance.update(pcent); return true; }; @@ -4939,17 +5255,25 @@ var chart = $(state.element_chart); var value = data.result[0]; + var min = self.data('easypiechart-min-value') || null; var max = self.data('easypiechart-max-value') || null; var adjust = self.data('easypiechart-adjust') || null; + if(min === null) { + min = NETDATA.commonMin.get(state); + state.easyPieChartMin = null; + } + else + state.easyPieChartMin = min; + if(max === null) { - max = data.max; + max = NETDATA.commonMax.get(state); state.easyPieChartMax = null; } else state.easyPieChartMax = max; - var pcent = NETDATA.percentFromValueMax(value, max); + var pcent = NETDATA.easypiechartPercentFromValueMinMax(value, min, max); chart.data('data-percent', pcent); @@ -4971,7 +5295,7 @@ var valuetop = Math.round((size - valuefontsize - (size / 40)) / 2); state.easyPieChartLabel = document.createElement('span'); state.easyPieChartLabel.className = 'easyPieChartLabel'; - state.easyPieChartLabel.innerHTML = state.legendFormatValue(value); + state.easyPieChartLabel.innerText = state.legendFormatValue(value); state.easyPieChartLabel.style.fontSize = valuefontsize + 'px'; state.easyPieChartLabel.style.top = valuetop.toString() + 'px'; state.element_chart.appendChild(state.easyPieChartLabel); @@ -4980,7 +5304,7 @@ var titletop = Math.round(valuetop - (titlefontsize * 2) - (size / 40)); state.easyPieChartTitle = document.createElement('span'); state.easyPieChartTitle.className = 'easyPieChartTitle'; - state.easyPieChartTitle.innerHTML = state.title; + state.easyPieChartTitle.innerText = state.title; state.easyPieChartTitle.style.fontSize = titlefontsize + 'px'; state.easyPieChartTitle.style.lineHeight = titlefontsize + 'px'; state.easyPieChartTitle.style.top = titletop.toString() + 'px'; @@ -4990,13 +5314,23 @@ var unittop = Math.round(valuetop + (valuefontsize + unitfontsize) + (size / 40)); state.easyPieChartUnits = document.createElement('span'); state.easyPieChartUnits.className = 'easyPieChartUnits'; - state.easyPieChartUnits.innerHTML = state.units; + state.easyPieChartUnits.innerText = state.units; state.easyPieChartUnits.style.fontSize = unitfontsize + 'px'; state.easyPieChartUnits.style.top = unittop.toString() + 'px'; state.element_chart.appendChild(state.easyPieChartUnits); + var barColor = self.data('easypiechart-barcolor'); + if(typeof barColor === 'undefined' || barColor === null) + barColor = state.chartColors()[0]; + else { + // <div ... data-easypiechart-barcolor="(function(percent){return(percent < 50 ? '#5cb85c' : percent < 85 ? '#f0ad4e' : '#cb3935');})" ...></div> + var tmp = eval(barColor); + if(typeof tmp === 'function') + barColor = tmp; + } + chart.easyPieChart({ - barColor: self.data('easypiechart-barcolor') || state.chartColors()[0], //'#ef1e25', + barColor: barColor, trackColor: self.data('easypiechart-trackcolor') || NETDATA.themes.current.easypiechart_track, scaleColor: self.data('easypiechart-scalecolor') || NETDATA.themes.current.easypiechart_scale, scaleLength: self.data('easypiechart-scalelength') || 5, @@ -5005,7 +5339,7 @@ trackWidth: self.data('easypiechart-trackwidth') || undefined, size: self.data('easypiechart-size') || size, rotate: self.data('easypiechart-rotate') || 0, - animate: self.data('easypiechart-rotate') || {duration: 500, enabled: true}, + animate: self.data('easypiechart-animate') || {duration: 500, enabled: true}, easing: self.data('easypiechart-easing') || undefined }); @@ -5042,13 +5376,13 @@ }) .always(function() { if(typeof callback === "function") - callback(); + return callback(); }) } else { NETDATA.chartLibraries.gauge.enabled = false; if(typeof callback === "function") - callback(); + return callback(); } }; @@ -5076,7 +5410,7 @@ min = max; max = t; } - else if(min == max) + else if(min === max) max = min + 1; // gauge.js has an issue if the needle @@ -5104,15 +5438,15 @@ NETDATA.gaugeSetLabels = function(state, value, min, max) { if(state.___gaugeOld__.valueLabel !== value) { state.___gaugeOld__.valueLabel = value; - state.gaugeChartLabel.innerHTML = state.legendFormatValue(value); + state.gaugeChartLabel.innerText = state.legendFormatValue(value); } if(state.___gaugeOld__.minLabel !== min) { state.___gaugeOld__.minLabel = min; - state.gaugeChartMin.innerHTML = state.legendFormatValue(min); + state.gaugeChartMin.innerText = state.legendFormatValue(min); } if(state.___gaugeOld__.maxLabel !== max) { state.___gaugeOld__.maxLabel = max; - state.gaugeChartMax.innerHTML = state.legendFormatValue(max); + state.gaugeChartMax.innerText = state.legendFormatValue(max); } }; @@ -5155,12 +5489,16 @@ } var value = state.data.result[state.data.result.length - 1 - slot]; - var max = (state.gaugeMax === null)?state.data.max:state.gaugeMax; - var min = 0; + var min = (state.gaugeMin === null)?NETDATA.commonMin.get(state):state.gaugeMin; + var max = (state.gaugeMax === null)?NETDATA.commonMax.get(state):state.gaugeMax; + + // make sure it is zero based + if(min > 0) min = 0; + if(max < 0) max = 0; state.gaugeEvent.value = value; - state.gaugeEvent.max = max; state.gaugeEvent.min = min; + state.gaugeEvent.max = max; NETDATA.gaugeSetLabels(state, value, min, max); if(state.gaugeEvent.timer === null) { @@ -5186,9 +5524,15 @@ } else { value = data.result[0]; - min = 0; - max = (state.gaugeMax === null)?data.max:state.gaugeMax; + min = (state.gaugeMin === null)?NETDATA.commonMin.get(state):state.gaugeMin; + max = (state.gaugeMax === null)?NETDATA.commonMax.get(state):state.gaugeMax; + if(value < min) min = value; if(value > max) max = value; + + // make sure it is zero based + if(min > 0) min = 0; + if(max < 0) max = 0; + NETDATA.gaugeSetLabels(state, value, min, max); } @@ -5201,6 +5545,7 @@ // var chart = $(state.element_chart); var value = data.result[0]; + var min = self.data('gauge-min-value') || null; var max = self.data('gauge-max-value') || null; var adjust = self.data('gauge-adjust') || null; var pointerColor = self.data('gauge-pointer-color') || NETDATA.themes.current.gauge_pointer; @@ -5209,13 +5554,24 @@ var stopColor = self.data('gauge-stop-color') || void 0; var generateGradient = self.data('gauge-generate-gradient') || false; + if(min === null) { + min = NETDATA.commonMin.get(state); + state.gaugeMin = null; + } + else + state.gaugeMin = min; + if(max === null) { - max = data.max; + max = NETDATA.commonMax.get(state); state.gaugeMax = null; } else state.gaugeMax = max; + // make sure it is zero based + if(min > 0) min = 0; + if(max < 0) max = 0; + var width = state.chartWidth(), height = state.chartHeight(); //, ratio = 1.5; //switch(adjust) { // case 'width': width = height * ratio; break; @@ -5300,7 +5656,7 @@ var titletop = 0; state.gaugeChartTitle = document.createElement('span'); state.gaugeChartTitle.className = 'gaugeChartTitle'; - state.gaugeChartTitle.innerHTML = state.title; + state.gaugeChartTitle.innerText = state.title; state.gaugeChartTitle.style.fontSize = titlefontsize + 'px'; state.gaugeChartTitle.style.lineHeight = titlefontsize + 'px'; state.gaugeChartTitle.style.top = titletop.toString() + 'px'; @@ -5309,7 +5665,7 @@ var unitfontsize = Math.round(titlefontsize * 0.9); state.gaugeChartUnits = document.createElement('span'); state.gaugeChartUnits.className = 'gaugeChartUnits'; - state.gaugeChartUnits.innerHTML = state.units; + state.gaugeChartUnits.innerText = state.units; state.gaugeChartUnits.style.fontSize = unitfontsize + 'px'; state.element_chart.appendChild(state.gaugeChartUnits); @@ -5333,7 +5689,7 @@ state.___gaugeOld__ = { value: value, - min: 0, + min: min, max: max, valueLabel: null, minLabel: null, @@ -5345,8 +5701,8 @@ state.gauge_instance.maxValue = 100; NETDATA.gaugeAnimation(state, animate); - NETDATA.gaugeSet(state, value, 0, max); - NETDATA.gaugeSetLabels(state, value, 0, max); + NETDATA.gaugeSet(state, value, min, max); + NETDATA.gaugeSetLabels(state, value, min, max); NETDATA.gaugeAnimation(state, true); return true; }; @@ -5581,7 +5937,7 @@ async: false, isAlreadyLoaded: function() { // check if bootstrap is loaded - if(typeof $().emulateTransitionEnd == 'function') + if(typeof $().emulateTransitionEnd === 'function') return true; else { if(typeof netdataNoBootstrap !== 'undefined' && netdataNoBootstrap) @@ -5592,7 +5948,7 @@ } }, { - url: NETDATA.serverDefault + 'lib/jquery.nanoscroller-0.8.7.min.js', + url: NETDATA.serverDefault + 'lib/perfect-scrollbar-0.6.15.min.js', isAlreadyLoaded: function() { return false; } } ]; @@ -5608,7 +5964,7 @@ } }, { - url: NETDATA.serverDefault + 'css/font-awesome.min.css?v4.6.3', + url: NETDATA.serverDefault + 'css/font-awesome.min.css?v4.7.0', isAlreadyLoaded: function() { return false; } }, { @@ -5621,7 +5977,7 @@ NETDATA.loadRequiredJs = function(index, callback) { if(index >= NETDATA.requiredJs.length) { if(typeof callback === 'function') - callback(); + return callback(); return; } @@ -5713,7 +6069,7 @@ var value = entry.value; if(NETDATA.alarms.current !== null) { var t = NETDATA.alarms.current.alarms[entry.chart + '.' + entry.name]; - if(typeof t !== 'undefined' && entry.status == t.status) + if(typeof t !== 'undefined' && entry.status === t.status) value = t.value; } @@ -5914,6 +6270,10 @@ url: NETDATA.alarms.server + '/api/v1/alarms?' + what.toString(), async: true, cache: false, + headers: { + 'Cache-Control': 'no-cache, no-store', + 'Pragma': 'no-cache' + }, xhrFields: { withCredentials: true } // required for the cookie }) .done(function(data) { @@ -5921,13 +6281,13 @@ NETDATA.alarms.first_notification_id = data.latest_alarm_log_unique_id; if(typeof callback === 'function') - callback(data); + return callback(data); }) .fail(function() { NETDATA.error(415, NETDATA.alarms.server); if(typeof callback === 'function') - callback(null); + return callback(null); }); }, @@ -5958,27 +6318,29 @@ url: NETDATA.alarms.server + '/api/v1/alarm_log?after=' + last_id.toString(), async: true, cache: false, + headers: { + 'Cache-Control': 'no-cache, no-store', + 'Pragma': 'no-cache' + }, xhrFields: { withCredentials: true } // required for the cookie }) .done(function(data) { if(typeof callback === 'function') - callback(data); + return callback(data); }) .fail(function() { NETDATA.error(416, NETDATA.alarms.server); if(typeof callback === 'function') - callback(null); + return callback(null); }); }, init: function() { - var host = NETDATA.serverDefault; - while(host.slice(-1) === '/') - host = host.substring(0, host.length - 1); - NETDATA.alarms.server = host; + NETDATA.alarms.server = NETDATA.fixHost(NETDATA.serverDefault); - NETDATA.alarms.last_notification_id = NETDATA.localStorageGet('last_notification_id', NETDATA.alarms.last_notification_id, null); + NETDATA.alarms.last_notification_id = + NETDATA.localStorageGet('last_notification_id', NETDATA.alarms.last_notification_id, null); if(NETDATA.alarms.onclick === null) NETDATA.alarms.onclick = NETDATA.alarms.scrollToAlarm; @@ -6017,7 +6379,7 @@ NETDATA.registry.machines = {}; NETDATA.registry.machines_array = new Array(); - var now = new Date().getTime(); + var now = Date.now(); var apu = person_urls; var i = apu.length; while(i--) { @@ -6074,8 +6436,7 @@ }, hello: function(host, callback) { - while(host.slice(-1) === '/') - host = host.substring(0, host.length - 1); + host = NETDATA.fixHost(host); // send HELLO to a netdata server: // 1. verifies the server is reachable @@ -6084,6 +6445,10 @@ url: host + '/api/v1/registry?action=hello', async: true, cache: false, + headers: { + 'Cache-Control': 'no-cache, no-store', + 'Pragma': 'no-cache' + }, xhrFields: { withCredentials: true } // required for the cookie }) .done(function(data) { @@ -6093,13 +6458,13 @@ } if(typeof callback === 'function') - callback(data); + return callback(data); }) .fail(function() { NETDATA.error(407, host); if(typeof callback === 'function') - callback(null); + return callback(null); }); }, @@ -6113,6 +6478,10 @@ url: NETDATA.registry.server + '/api/v1/registry?action=access&machine=' + NETDATA.registry.machine_guid + '&name=' + encodeURIComponent(NETDATA.registry.hostname) + '&url=' + encodeURIComponent(NETDATA.serverDefault), // + '&visible_url=' + encodeURIComponent(document.location), async: true, cache: false, + headers: { + 'Cache-Control': 'no-cache, no-store', + 'Pragma': 'no-cache' + }, xhrFields: { withCredentials: true } // required for the cookie }) .done(function(data) { @@ -6132,7 +6501,7 @@ } else { if(typeof callback === 'function') - callback(null); + return callback(null); } } else { @@ -6140,14 +6509,14 @@ NETDATA.registry.person_guid = data.person_guid; if(typeof callback === 'function') - callback(data.urls); + return callback(data.urls); } }) .fail(function() { NETDATA.error(410, NETDATA.registry.server); if(typeof callback === 'function') - callback(null); + return callback(null); }); }, @@ -6157,6 +6526,10 @@ url: NETDATA.registry.server + '/api/v1/registry?action=delete&machine=' + NETDATA.registry.machine_guid + '&name=' + encodeURIComponent(NETDATA.registry.hostname) + '&url=' + encodeURIComponent(NETDATA.serverDefault) + '&delete_url=' + encodeURIComponent(delete_url), async: true, cache: false, + headers: { + 'Cache-Control': 'no-cache, no-store', + 'Pragma': 'no-cache' + }, xhrFields: { withCredentials: true } // required for the cookie }) .done(function(data) { @@ -6166,13 +6539,13 @@ } if(typeof callback === 'function') - callback(data); + return callback(data); }) .fail(function() { NETDATA.error(412, NETDATA.registry.server); if(typeof callback === 'function') - callback(null); + return callback(null); }); }, @@ -6182,6 +6555,10 @@ url: NETDATA.registry.server + '/api/v1/registry?action=search&machine=' + NETDATA.registry.machine_guid + '&name=' + encodeURIComponent(NETDATA.registry.hostname) + '&url=' + encodeURIComponent(NETDATA.serverDefault) + '&for=' + machine_guid, async: true, cache: false, + headers: { + 'Cache-Control': 'no-cache, no-store', + 'Pragma': 'no-cache' + }, xhrFields: { withCredentials: true } // required for the cookie }) .done(function(data) { @@ -6191,13 +6568,13 @@ } if(typeof callback === 'function') - callback(data); + return callback(data); }) .fail(function() { NETDATA.error(418, NETDATA.registry.server); if(typeof callback === 'function') - callback(null); + return callback(null); }); }, @@ -6207,6 +6584,10 @@ url: NETDATA.registry.server + '/api/v1/registry?action=switch&machine=' + NETDATA.registry.machine_guid + '&name=' + encodeURIComponent(NETDATA.registry.hostname) + '&url=' + encodeURIComponent(NETDATA.serverDefault) + '&to=' + new_person_guid, async: true, cache: false, + headers: { + 'Cache-Control': 'no-cache, no-store', + 'Pragma': 'no-cache' + }, xhrFields: { withCredentials: true } // required for the cookie }) .done(function(data) { @@ -6216,13 +6597,13 @@ } if(typeof callback === 'function') - callback(data); + return callback(data); }) .fail(function() { NETDATA.error(414, NETDATA.registry.server); if(typeof callback === 'function') - callback(null); + return callback(null); }); } }; @@ -6251,6 +6632,4 @@ } }); }); - - // window.NETDATA = NETDATA; -// })(window, document); +})(window, document); diff --git a/web/dashboard.slate.css b/web/dashboard.slate.css index cd7972cf9..36ea6dc6a 100644 --- a/web/dashboard.slate.css +++ b/web/dashboard.slate.css @@ -1,355 +1,320 @@ html, body { - /*font-family: Calibri,"Segoe UI","Helvetica Neue",Helvetica,Arial,sans-serif;*/ - font-family: "Helvetica Neue",Helvetica,Arial,sans-serif; - font-style: normal; - font-variant: normal; - color: #878b90; + /*font-family: Calibri,"Segoe UI","Helvetica Neue",Helvetica,Arial,sans-serif;*/ + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-style: normal; + font-variant: normal; + color: #878b90; +} + +/* fixes for default slate theme */ +code { + color: #bbb; /*#c7254e;*/ + background-color: #555; /* #f9f2f4; */ } .dashboard-sidebar .nav > .active > a, .dashboard-sidebar .nav > .active:hover > a, .dashboard-sidebar .nav > .active:focus > a { - color: #765d9c; - border-left: 2px solid #765d9c; + color: #765d9c; + border-left: 2px solid #765d9c; } .morelink { - color: #765d9c; - text-decoration: none; + color: #765d9c; + text-decoration: none; } + .morelink:hover { - color: #563d7c; - text-decoration: none; + color: #563d7c; + text-decoration: none; } + .morelink:focus { - color: #765d9c; - text-decoration: none; + color: #765d9c; + text-decoration: none; } .netdata-chart-alignment { - margin-left: 55px; + margin-left: 55px; } .netdata-chart-row { - width: 100%; - text-align: center; - display: flex; - display: -webkit-flex; - display: -moz-flex; - align-items: flex-end; - -moz-align-items: flex-end; - -webkit-align-items: flex-end; - justify-content: center; - -moz--webkit-justify-content: center; - -moz-justify-content: center; + width: 100%; + text-align: center; + display: flex; + display: -webkit-flex; + display: -moz-flex; + align-items: flex-end; + -moz-align-items: flex-end; + -webkit-align-items: flex-end; + justify-content: center; + -moz--webkit-justify-content: center; + -moz-justify-content: center; } .netdata-container { - display: inline-block; - overflow: hidden; + display: inline-block; + overflow: hidden; - /* required for child elements to have absolute position */ - position: relative; - - /* width and height is given per chart with data-width and data-height */ + /* required for child elements to have absolute position */ + position: relative; + + /* width and height is given per chart with data-width and data-height */ } .netdata-aspect { - position: relative; - width: 100%; - padding: 0px; - margin: 0px; + position: relative; + width: 100%; + padding: 0px; + margin: 0px; } .netdata-container-with-legend { - display: inline-block; - overflow: hidden; + display: inline-block; + overflow: hidden; + + /* fix minimum scrollbar issue in firefox */ + min-height: 99px; - /* fix minimum scrollbar issue in firefox */ - min-height: 99px; + /* required for child elements to have absolute position */ + position: relative; - /* required for child elements to have absolute position */ - position: relative; - - /* width and height is given per chart with data-width and data-height */ + /* width and height is given per chart with data-width and data-height */ } .netdata-legend-resize-handler { - display: block; - position: absolute; - bottom: 0px; - right: 0px; - height: 15px; - width: 30px; - background-color: #272b30; - font-size: 12px; - vertical-align: middle; - line-height: 15px; - cursor: ns-resize; - color: #373b40; - text-align: center; - overflow: hidden; - z-index: 20; - padding: 0px; - margin: 0px; + display: block; + position: absolute; + bottom: 0px; + right: 0px; + height: 15px; + width: 30px; + background-color: #272b30; + font-size: 12px; + vertical-align: middle; + line-height: 15px; + cursor: ns-resize; + color: #373b40; + text-align: center; + overflow: hidden; + z-index: 20; + padding: 0px; + margin: 0px; } .netdata-legend-toolbox { - display: block; - position: absolute; - bottom: 0px; - right: 30px; - height: 15px; - width: 110px; - background-color: #272b30; - font-size: 12px; - vertical-align: middle; - line-height: 15px; - color: #373b40; - text-align: center; - overflow: hidden; - z-index: 20; - padding: 0px; - margin: 0px; + display: block; + position: absolute; + bottom: 0px; + right: 30px; + height: 15px; + width: 110px; + background-color: #272b30; + font-size: 12px; + vertical-align: middle; + line-height: 15px; + color: #373b40; + text-align: center; + overflow: hidden; + z-index: 20; + padding: 0px; + margin: 0px; } .netdata-legend-toolbox-button { - display: inline-block; - position: relative; - height: 15px; - width: 18px; - background-color: #272b30; - font-size: 12px; - vertical-align: middle; - line-height: 15px; - color: #474b50; - text-align: center; - overflow: hidden; - z-index: 21; - padding: 0px; - margin: 0px; - cursor: pointer; + display: inline-block; + position: relative; + height: 15px; + width: 18px; + background-color: #272b30; + font-size: 12px; + vertical-align: middle; + line-height: 15px; + color: #474b50; + text-align: center; + overflow: hidden; + z-index: 21; + padding: 0px; + margin: 0px; + cursor: pointer; } .netdata-message { - display: inline-block; - text-align: left; - vertical-align: top; - font-weight: bold; - font-size: x-small; - width: 100%; - height: 100%; - overflow: hidden; - background: inherit; - z-index: 0; + display: inline-block; + text-align: left; + vertical-align: top; + font-weight: bold; + font-size: x-small; + width: 100%; + height: 100%; + overflow: hidden; + background: inherit; + z-index: 0; } .netdata-message.hidden { - display: none; + display: none; } .netdata-message.icon { - color: #2f3338; - text-align: center; - vertical-align: middle; + color: #2f3338; + text-align: center; + vertical-align: middle; } .netdata-chart-legend { - position: absolute; /* within .netdata-container */ - top: 0; - right: 0; - background-color: #272b30; - overflow: hidden; - text-overflow: ellipsis; - line-height: 14px; - display: block; - width: 140px; /* --legend-width */ - height: calc(100% - 15px); /* 10px for the resize handler and 5px for the top margin */ - font-size: 10px; - margin-top: 5px; - text-align: left; - /* width and height is calculated (depends on the appearance of the legend) */ + position: absolute; /* within .netdata-container */ + top: 0; + right: 0; + overflow: hidden; + text-overflow: ellipsis; + line-height: 14px; + display: block; + width: 140px; /* --legend-width */ + height: calc(100% - 15px); /* 10px for the resize handler and 5px for the top margin */ + font-size: 10px; + margin-top: 5px; + text-align: left; + /* width and height is calculated (depends on the appearance of the legend) */ } .netdata-legend-title-date { - font-size: 10px; - font-weight: normal; - margin-top: 0px; + font-size: 10px; + font-weight: normal; + margin-top: 0px; } + .netdata-legend-title-time { - font-size: 11px; - font-weight: bold; - margin-top: 0px; + font-size: 11px; + font-weight: bold; + margin-top: 0px; } + .netdata-legend-title-units { - position: absolute; - right: 5px; - float: right; - font-size: 10px; - vertical-align: top; - font-weight: normal; - margin-top: 0px; + position: absolute; + right: 10px; + float: right; + font-size: 11px; + vertical-align: top; + font-weight: normal; + margin-top: 0px; } + .netdata-legend-series { - position: absolute; - width: 140px; /* --legend-width */ - height: calc(100% - 50px); - overflow: hidden; - text-overflow: ellipsis; - line-height: 14px; - display: block; - font-size: 10px; - margin-top: 0px; -} - -.netdata-legend-series > .netdata-legend-series-content { - position : absolute; - overflow : scroll; - overflow-x : hidden; - top : 0; - right : 0; - bottom : 0; - left : 0; - text-overflow: ellipsis; -} -.netdata-legend-series > .netdata-legend-series-content:focus { - outline: thin dotted; -} -.netdata-legend-series > .netdata-legend-series-content::-webkit-scrollbar { - display: block; /* was 'none', but chrome was hidding content with it */ -} -.has-scrollbar > .netdata-legend-series-content::-webkit-scrollbar { - display: block; -} -.netdata-legend-series > .netdata-legend-series-pane { -/* background : rgba(0,0,0,.25);*/ - background : #373b40; - position : absolute; - width : 4px; - right : 0; - top : 0; - bottom : 0; -/* visibility : hidden\9; */ /* Target only IE7 and IE8 with this hack */ -/* opacity : .01; */ -/* -webkit-transition : .2s; */ -/* -moz-transition : .2s; */ -/* -o-transition : .2s; */ -/* transition : .2s; */ - -moz-border-radius : 4px; - -webkit-border-radius : 4px; - border-radius : 4px; -} -.netdata-legend-series > .netdata-legend-series-pane > .netdata-legend-series-slider { - background : #222; /* nano scroller handler color */ -/* background : rgba(0,0,0,.5);*/ - position : relative; - margin : 0 1px; - -moz-border-radius : 2px; - -webkit-border-radius : 2px; - border-radius : 2px; -} -.netdata-legend-series:hover > .netdata-legend-series-pane, .netdata-legend-series-pane.active, .netdata-legend-series-pane.flashed { -/* visibility : visible\9; */ /* Target only IE7 and IE8 with this hack */ -/* opacity : 0.99;*/ + position: absolute; + width: 140px; /* legend-width */ + height: calc(100% - 50px); + overflow: hidden; + text-overflow: ellipsis; + line-height: 14.5px; /* line spacing at the legend */ + display: block; + font-size: 10px; + margin-top: 0px; } .netdata-legend-name-table-line { - display: inline-block; - width: 13px; - height: 4px; - border-width: 0px; - border-bottom-width: 2px; - border-bottom-style: solid; - border-bottom-color: #272b30; + display: inline-block; + width: 13px; + height: 4px; + border-width: 0px; + border-bottom-width: 2px; + border-bottom-style: solid; + border-bottom-color: #272b30; } + .netdata-legend-name-table-area { - display: inline-block; - width: 13px; - height: 5px; - border-width: 1px; - border-top-width: 1px; - border-top-style: solid; - border-top-color: inherit; + display: inline-block; + width: 13px; + height: 5px; + border-width: 1px; + border-top-width: 1px; + border-top-style: solid; + border-top-color: inherit; } + .netdata-legend-name-table-stacked { - display: inline-block; - width: 13px; - height: 5px; - border-width: 1px; - border-top-width: 1px; - border-top-style: solid; - border-top-color: inherit; + display: inline-block; + width: 13px; + height: 5px; + border-width: 1px; + border-top-width: 1px; + border-top-style: solid; + border-top-color: inherit; } + .netdata-legend-name-tr { } + .netdata-legend-name-td { } + .netdata-legend-name { - text-align: left; - font-size: 10px; - font-weight: bold; - vertical-align: bottom; - margin-top: 0px; - z-index: 9; - padding: 0px; - width: 80px !important; - max-width: 80px !important; - text-overflow: ellipsis; - overflow: hidden; - white-space: nowrap; - display: inline-block; - cursor: pointer; + text-align: left; + font-size: 11px; /* legend: dimension name size */ + font-weight: bold; + vertical-align: bottom; + margin-top: 0px; + z-index: 9; + padding: 0px; + width: 80px !important; + max-width: 80px !important; + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + display: inline-block; + cursor: pointer; } .netdata-legend-value { - /*margin-left: 14px;*/ - position: absolute; - right: 5px; - float: right; - text-align: right; - font-size: 10px; - font-weight: bold; - vertical-align: bottom; - background-color: #272b30; - margin-top: 0px; - z-index: 10; - padding: 0px; - padding-left: 15px; - cursor: pointer; - /* -webkit-font-smoothing: none; */ + /*margin-left: 14px;*/ + position: absolute; + right: 10px; + float: right; + text-align: right; + font-size: 11px; /* legend: dimension value size */ + font-weight: bold; + vertical-align: bottom; + background-color: #272b30; + margin-top: 0px; + z-index: 10; + padding: 0px; + padding-left: 15px; + cursor: pointer; + /* -webkit-font-smoothing: none; */ } + .netdata-legend-name.not-selected { - font-weight: normal; - opacity: 0.3; + font-weight: normal; + opacity: 0.3; } .netdata-chart { - position: absolute; /* within .netdata-container */ - top: 0; /* within .netdata-container */ - left: 0; /* within .netdata-container */ - display: inline-block; - overflow: hidden; - width: 100%; - height: 100%; - z-index: 5; + position: absolute; /* within .netdata-container */ + top: 0; /* within .netdata-container */ + left: 0; /* within .netdata-container */ + display: inline-block; + overflow: hidden; + width: 100%; + height: 100%; + z-index: 5; - /* width and height is calculated (depends on the appearance of the legend) */ + /* width and height is calculated (depends on the appearance of the legend) */ } .netdata-chart-with-legend-right { - position: absolute; /* within .netdata-container */ - top: 0; /* within .netdata-container */ - left: 0; /* within .netdata-container */ - display: block; - overflow: hidden; - margin-right: 140px; /* --legend-width */ - width: calc(100% - 140px); /* --legend-width */ - height: 100%; - z-index: 5; - flex-grow: 1; + position: absolute; /* within .netdata-container */ + top: 0; /* within .netdata-container */ + left: 0; /* within .netdata-container */ + display: block; + overflow: hidden; + margin-right: 140px; /* --legend-width */ + width: calc(100% - 140px); /* --legend-width */ + height: 100%; + z-index: 5; + flex-grow: 1; - /* width and height is calculated (depends on the appearance of the legend) */ + /* width and height is calculated (depends on the appearance of the legend) */ } .netdata-peity-chart { @@ -376,179 +341,344 @@ body { } .dygraph-axis-label { - color: #6c7075; + color: #6c7075; } .dygraph-label-rotate-left { - text-align: center; - /* See http://caniuse.com/#feat=transforms2d */ - transform: rotate(90deg); - -webkit-transform: rotate(90deg); - -moz-transform: rotate(90deg); - -o-transform: rotate(90deg); - -ms-transform: rotate(90deg); + text-align: center; + /* See http://caniuse.com/#feat=transforms2d */ + transform: rotate(90deg); + -webkit-transform: rotate(90deg); + -moz-transform: rotate(90deg); + -o-transform: rotate(90deg); + -ms-transform: rotate(90deg); } /* For y2-axis label */ .dygraph-label-rotate-right { - text-align: center; - /* See http://caniuse.com/#feat=transforms2d */ - transform: rotate(-90deg); - -webkit-transform: rotate(-90deg); - -moz-transform: rotate(-90deg); - -o-transform: rotate(-90deg); - -ms-transform: rotate(-90deg); + text-align: center; + /* See http://caniuse.com/#feat=transforms2d */ + transform: rotate(-90deg); + -webkit-transform: rotate(-90deg); + -moz-transform: rotate(-90deg); + -o-transform: rotate(-90deg); + -ms-transform: rotate(-90deg); } .dygraph-title { - text-indent: 56px; - text-align: left; - position: absolute; - left: 0px; - top: 4px; - font-size: 11px; - font-weight: bold; - text-overflow: ellipsis; - overflow: hidden; - white-space: nowrap; + text-indent: 56px; + text-align: left; + position: absolute; + left: 0px; + top: 4px; + font-size: 11px; + font-weight: bold; + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; } /* fix for sparkline tooltip under bootstrap */ .jqstooltip { - width: auto !important; - height: auto !important; + width: auto !important; + height: auto !important; } .easyPieChart { - position: relative; - text-align: center; + position: relative; + text-align: center; } .easyPieChart canvas { - position: absolute; - top: 0; - left: 0; + position: absolute; + top: 0; + left: 0; } .easyPieChartLabel { - display: inline-block; - position: absolute; - float: left; - left: 0; - width: 100%; - text-align: center; - color: #BBB; - font-weight: normal; - text-shadow: #272b30 0px 0px 1px; - /* -webkit-font-smoothing: none; */ + display: inline-block; + position: absolute; + float: left; + left: 0; + width: 100%; + text-align: center; + color: #BBB; + font-weight: normal; + text-shadow: #272b30 0px 0px 1px; + /* -webkit-font-smoothing: none; */ } .easyPieChartTitle { - display: inline-block; - position: absolute; - float: left; - left: 0; - width: 64%; - margin-left: 18%; - text-align: center; - color: #676b70; - font-weight: normal; + display: inline-block; + position: absolute; + float: left; + left: 0; + width: 64%; + margin-left: 18%; + text-align: center; + color: #676b70; + font-weight: normal; } .easyPieChartUnits { - display: inline-block; - position: absolute; - float: left; - left: 0; - width: 60%; - margin-left: 20%; - text-align: center; - color: #676b70; - font-weight: normal; + display: inline-block; + position: absolute; + float: left; + left: 0; + width: 60%; + margin-left: 20%; + text-align: center; + color: #676b70; + font-weight: normal; } .gaugeChart { - position: relative; - text-align: center; + position: relative; + text-align: center; } .gaugeChart canvas { - position: absolute; - top: 0; - left: 0; - z-index: 0; + position: absolute; + top: 0; + left: 0; + z-index: 0; } .gaugeChartLabel { - display: inline-block; - position: absolute; - float: left; - left: 0; - width: 100%; - text-align: center; - color: #BBB; - font-weight: bold; - z-index: 1; - text-shadow: #272b30 0px 0px 1px; - /* text-shadow: #CCC 1px 1px 0px, #CCC -1px -1px 0px, #CCC 1px -1px 0px, #CCC -1px 1px 0px; */ - /* -webkit-text-stroke: 1px #777; */ - /* -webkit-font-smoothing: none; */ + display: inline-block; + position: absolute; + float: left; + left: 0; + width: 100%; + text-align: center; + color: #BBB; + font-weight: bold; + z-index: 1; + text-shadow: #272b30 0px 0px 1px; + /* text-shadow: #CCC 1px 1px 0px, #CCC -1px -1px 0px, #CCC 1px -1px 0px, #CCC -1px 1px 0px; */ + /* -webkit-text-stroke: 1px #777; */ + /* -webkit-font-smoothing: none; */ } .gaugeChartTitle { - display: inline-block; - position: absolute; - float: left; - left: 0; - width: 100%; - text-align: center; - color: #676b70; - font-weight: bold; + display: inline-block; + position: absolute; + float: left; + left: 0; + width: 100%; + text-align: center; + color: #676b70; + font-weight: bold; } .gaugeChartUnits { - display: inline-block; - position: absolute; - float: left; - left: 0; - bottom: 0; - width: 100%; - text-align: left; - margin-left: 5%; - color: #676b70; - font-weight: normal; + display: inline-block; + position: absolute; + float: left; + left: 0; + bottom: 0; + width: 100%; + text-align: left; + margin-left: 5%; + color: #676b70; + font-weight: normal; } .gaugeChartMin { - display: inline-block; - position: absolute; - float: left; - left: 0; - bottom: 10%; - width: 92%; - margin-left: 8%; - text-align: left; - color: #676b70; - font-weight: normal; + display: inline-block; + position: absolute; + float: left; + left: 0; + bottom: 10%; + width: 92%; + margin-left: 8%; + text-align: left; + color: #676b70; + font-weight: normal; } .gaugeChartMax { - display: inline-block; - position: absolute; - float: left; - left: 0; - bottom: 10%; - width: 95%; - margin-right: 5%; - text-align: right; - color: #676b70; - font-weight: normal; + display: inline-block; + position: absolute; + float: left; + left: 0; + bottom: 10%; + width: 95%; + margin-right: 5%; + text-align: right; + color: #676b70; + font-weight: normal; } .popover-title { - font-weight: bold; - font-size: 12px; + font-weight: bold; + font-size: 12px; } + .popover-content { - font-size: 11px; + font-size: 11px; +} + +/* ---------------------------------------------------------------------------- + perfect-scrollbar settings + */ + +.ps-container { + -ms-touch-action: auto; + touch-action: auto; + overflow: hidden !important; + -ms-overflow-style: none; +} + +@supports (-ms-overflow-style: none) { + .ps-container { + overflow: auto !important; + } +} + +@media screen and (-ms-high-contrast: active), (-ms-high-contrast: none) { + .ps-container { + overflow: auto !important; + } +} + +.ps-container.ps-active-x > .ps-scrollbar-x-rail, +.ps-container.ps-active-y > .ps-scrollbar-y-rail { + display: block; + background-color: transparent; +} + +.ps-container.ps-in-scrolling.ps-x > .ps-scrollbar-x-rail { + background-color: transparent; /* background color when dragged away */ + opacity: 0.9; +} + +.ps-container.ps-in-scrolling.ps-x > .ps-scrollbar-x-rail > .ps-scrollbar-x { + background-color: #aaa; /* scrollbar color when dragged away */ + height: 5px; +} + +.ps-container.ps-in-scrolling.ps-y > .ps-scrollbar-y-rail { + background-color: transparent; /* background color when dragged away */ + opacity: 0.9; +} + +.ps-container.ps-in-scrolling.ps-y > .ps-scrollbar-y-rail > .ps-scrollbar-y { + background-color: #aaa; /* scrollbar color when dragged away */ + width: 5px; +} + +.ps-container > .ps-scrollbar-x-rail { + display: none; + position: absolute; + /* please don't change 'position' */ + opacity: 0.2; /* the opacity when not on hover of the content */ + -webkit-transition: background-color .2s linear, opacity .2s linear; + -o-transition: background-color .2s linear, opacity .2s linear; + -moz-transition: background-color .2s linear, opacity .2s linear; + transition: background-color .2s linear, opacity .2s linear; + bottom: 0px; + /* there must be 'bottom' for ps-scrollbar-x-rail */ + height: 15px; +} + +.ps-container > .ps-scrollbar-x-rail > .ps-scrollbar-x { + position: absolute; + /* please don't change 'position' */ + background-color: #666; /* #aaa; the color on content hover */ + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; + -webkit-transition: background-color .2s linear, height .2s linear, width .2s ease-in-out, -webkit-border-radius .2s ease-in-out; + transition: background-color .2s linear, height .2s linear, width .2s ease-in-out, -webkit-border-radius .2s ease-in-out; + -o-transition: background-color .2s linear, height .2s linear, width .2s ease-in-out, border-radius .2s ease-in-out; + -moz-transition: background-color .2s linear, height .2s linear, width .2s ease-in-out, border-radius .2s ease-in-out, -moz-border-radius .2s ease-in-out; + transition: background-color .2s linear, height .2s linear, width .2s ease-in-out, border-radius .2s ease-in-out; + transition: background-color .2s linear, height .2s linear, width .2s ease-in-out, border-radius .2s ease-in-out, -webkit-border-radius .2s ease-in-out, -moz-border-radius .2s ease-in-out; + bottom: 2px; + /* there must be 'bottom' for ps-scrollbar-x */ + height: 5px; /* the width of the scrollbar */ +} + +.ps-container > .ps-scrollbar-x-rail:hover > .ps-scrollbar-x, .ps-container > .ps-scrollbar-x-rail:active > .ps-scrollbar-x { + height: 5px; +} + +.ps-container > .ps-scrollbar-y-rail { + display: none; + position: absolute; + /* please don't change 'position' */ + opacity: 0.2; /* the opacity when not on hover of the content */ + -webkit-transition: background-color .2s linear, opacity .2s linear; + -o-transition: background-color .2s linear, opacity .2s linear; + -moz-transition: background-color .2s linear, opacity .2s linear; + transition: background-color .2s linear, opacity .2s linear; + right: 0; + /* there must be 'right' for ps-scrollbar-y-rail */ + width: 15px; +} + +.ps-container > .ps-scrollbar-y-rail > .ps-scrollbar-y { + position: absolute; + /* please don't change 'position' */ + background-color: #666; /* #aaa; the color on content hover */ + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; + -webkit-transition: background-color .2s linear, height .2s linear, width .2s ease-in-out, -webkit-border-radius .2s ease-in-out; + transition: background-color .2s linear, height .2s linear, width .2s ease-in-out, -webkit-border-radius .2s ease-in-out; + -o-transition: background-color .2s linear, height .2s linear, width .2s ease-in-out, border-radius .2s ease-in-out; + -moz-transition: background-color .2s linear, height .2s linear, width .2s ease-in-out, border-radius .2s ease-in-out, -moz-border-radius .2s ease-in-out; + transition: background-color .2s linear, height .2s linear, width .2s ease-in-out, border-radius .2s ease-in-out; + transition: background-color .2s linear, height .2s linear, width .2s ease-in-out, border-radius .2s ease-in-out, -webkit-border-radius .2s ease-in-out, -moz-border-radius .2s ease-in-out; + right: 2px; + /* there must be 'right' for ps-scrollbar-y */ + width: 5px; /* the width of the scrollbar */ +} + +.ps-container > .ps-scrollbar-y-rail:hover > .ps-scrollbar-y, .ps-container > .ps-scrollbar-y-rail:active > .ps-scrollbar-y { + width: 5px; +} + +.ps-container:hover.ps-in-scrolling.ps-x > .ps-scrollbar-x-rail { + background-color: transparent; /* background color when dragged */ + opacity: 0.9; +} + +.ps-container:hover.ps-in-scrolling.ps-x > .ps-scrollbar-x-rail > .ps-scrollbar-x { + background-color: #bbb; /* scrollbar color when dragged */ + height: 5px; +} + +.ps-container:hover.ps-in-scrolling.ps-y > .ps-scrollbar-y-rail { + background-color: transparent; /* background color when dragged */ + opacity: 0.9; +} + +.ps-container:hover.ps-in-scrolling.ps-y > .ps-scrollbar-y-rail > .ps-scrollbar-y { + background-color: #bbb; /* scrollbar color when dragged */ + width: 5px; +} + +.ps-container:hover > .ps-scrollbar-x-rail, +.ps-container:hover > .ps-scrollbar-y-rail { + opacity: 0.6; +} + +.ps-container:hover > .ps-scrollbar-x-rail:hover { + background-color: transparent; /* the background color on hover of the scrollbar */ + opacity: 0.9; +} + +.ps-container:hover > .ps-scrollbar-x-rail:hover > .ps-scrollbar-x { + background-color: #999; /* scrollbar color on hover */ +} + +.ps-container:hover > .ps-scrollbar-y-rail:hover { + background-color: transparent; /* the background color on hover of the scrollbar */ + opacity: 0.9; +} + +.ps-container:hover > .ps-scrollbar-y-rail:hover > .ps-scrollbar-y { + background-color: #999; /* scrollbar color on hover */ } diff --git a/web/dashboard_info.js b/web/dashboard_info.js index 68df1eb9a..24a579cfe 100644 --- a/web/dashboard_info.js +++ b/web/dashboard_info.js @@ -1,730 +1,799 @@ -
-var netdataDashboard = window.netdataDashboard || {};
-
-// menu
-// information about the main menus
-
-netdataDashboard.menu = {
- 'system': {
- title: 'System Overview',
- icon: '<i class="fa fa-bookmark" aria-hidden="true"></i>',
- info: 'Overview of the key system metrics.'
- },
-
- 'ap': {
- title: 'Access Points',
- icon: '<i class="fa fa-wifi" aria-hidden="true"></i>',
- info: undefined
- },
-
- 'tc': {
- title: 'Quality of Service',
- icon: '<i class="fa fa-globe" aria-hidden="true"></i>',
- info: 'Netdata collects and visualizes tc class utilization using its <a href="https://github.com/firehol/netdata/blob/master/plugins.d/tc-qos-helper.sh" target="_blank">tc-helper plugin</a>. If you also use <a href="http://firehol.org/#fireqos" target="_blank">FireQOS</a> for setting up QoS, netdata automatically collects interface and class names. If your QoS configuration includes overheads calculation, the values shown here will include these overheads (the total bandwidth for the same interface as reported in the Network Interfaces section, will be lower than the total bandwidth reported here). Also, data collection may have a slight time difference compared to the interface (QoS data collection is implemented with a BASH script, so a shift in data collection of a few milliseconds should be justified).'
- },
-
- 'net': {
- title: 'Network Interfaces',
- icon: '<i class="fa fa-share-alt" aria-hidden="true"></i>',
- info: 'Per network interface statistics collected from <code>/proc/net/dev</code>.'
- },
-
- 'ipv4': {
- title: 'IPv4 Networking',
- icon: '<i class="fa fa-cloud" aria-hidden="true"></i>',
- info: undefined
- },
-
- 'ipv6': {
- title: 'IPv6 Networking',
- icon: '<i class="fa fa-cloud" aria-hidden="true"></i>',
- info: undefined
- },
-
- 'ipvs': {
- title: 'IP Virtual Server',
- icon: '<i class="fa fa-eye" aria-hidden="true"></i>',
- info: undefined
- },
-
- 'netfilter': {
- title: 'Firewall (netfilter)',
- icon: '<i class="fa fa-shield" aria-hidden="true"></i>',
- info: undefined
- },
-
- 'cpu': {
- title: 'CPUs',
- icon: '<i class="fa fa-bolt" aria-hidden="true"></i>',
- info: undefined
- },
-
- 'mem': {
- title: 'Memory',
- icon: '<i class="fa fa-bolt" aria-hidden="true"></i>',
- info: undefined
- },
-
- 'disk': {
- title: 'Disks',
- icon: '<i class="fa fa-folder" aria-hidden="true"></i>',
- info: 'Charts with performance information for all the system disks. Special care has been given to present disk performance metrics in a way compatible with <code>iostat -x</code>. netdata by default prevents rendering performance charts for individual partitions and unmounted virtual disks. Disabled charts can still be enabled by altering the relative settings in the netdata configuration file.'
- },
-
- 'sensors': {
- title: 'Sensors',
- icon: '<i class="fa fa-leaf" aria-hidden="true"></i>',
- info: undefined
- },
-
- 'nfsd': {
- title: 'NFS Server',
- icon: '<i class="fa fa-folder-open" aria-hidden="true"></i>',
- info: undefined
- },
-
- 'nfs': {
- title: 'NFS Client',
- icon: '<i class="fa fa-folder-open" aria-hidden="true"></i>',
- info: undefined
- },
-
- 'apps': {
- title: 'Applications',
- icon: '<i class="fa fa-heartbeat" aria-hidden="true"></i>',
- info: 'Per application statistics are collected using netdata\'s <code>apps.plugin</code>. This plugin walks through the entire <code>/proc</code> filesystem and aggregates statistics for applications of interest, defined in <code>/etc/netdata/apps_groups.conf</code> (the default is <a href="https://github.com/firehol/netdata/blob/master/conf.d/apps_groups.conf" target="_blank">here</a>). The plugin internally builds a process tree (much like <code>ps fax</code> does), and groups processes together (evaluating both child and parent processes) so that the result is always a chart with a predefined set of dimensions (of course, only application groups found running are reported). The reported values are compatible with <code>top</code>, although the netdata plugin counts also the resources of exited children (unlike <code>top</code> which shows only the resources of the currently running processes). So for processes like shell scripts, the reported values include the resources used by the commands these scripts run within each timeframe.',
- height: 1.5
- },
-
- 'users': {
- title: 'Users',
- icon: '<i class="fa fa-user" aria-hidden="true"></i>',
- info: 'Per user statistics are collected using netdata\'s <code>apps.plugin</code>. This plugin walks through the entire <code>/proc</code> filesystem and aggregates statistics per user. The reported values are compatible with <code>top</code>, although the netdata plugin counts also the resources of exited children (unlike <code>top</code> which shows only the resources of the currently running processes). So for processes like shell scripts, the reported values include the resources used by the commands these scripts run within each timeframe.',
- height: 1.5
- },
-
- 'groups': {
- title: 'User Groups',
- icon: '<i class="fa fa-users" aria-hidden="true"></i>',
- info: 'Per user group statistics are collected using netdata\'s <code>apps.plugin</code>. This plugin walks through the entire <code>/proc</code> filesystem and aggregates statistics per user group. The reported values are compatible with <code>top</code>, although the netdata plugin counts also the resources of exited children (unlike <code>top</code> which shows only the resources of the currently running processes). So for processes like shell scripts, the reported values include the resources used by the commands these scripts run within each timeframe.',
- height: 1.5
- },
-
- 'netdata': {
- title: 'Netdata Monitoring',
- icon: '<i class="fa fa-bar-chart" aria-hidden="true"></i>',
- info: undefined
- },
-
- 'example': {
- title: 'Example Charts',
- info: undefined
- },
-
- 'cgroup': {
- title: '',
- icon: '<i class="fa fa-th" aria-hidden="true"></i>',
- info: undefined
- },
-
- 'cgqemu': {
- title: '',
- icon: '<i class="fa fa-th-large" aria-hidden="true"></i>',
- info: undefined
- },
-
- 'memcached': {
- title: 'memcached',
- icon: '<i class="fa fa-database" aria-hidden="true"></i>',
- info: undefined
- },
-
- 'mysql': {
- title: 'MySQL',
- icon: '<i class="fa fa-database" aria-hidden="true"></i>',
- info: undefined
- },
-
- 'redis': {
- title: 'Redis',
- icon: '<i class="fa fa-database" aria-hidden="true"></i>',
- info: undefined
- },
-
- 'retroshare': {
- title: 'RetroShare',
- icon: '<i class="fa fa-share-alt" aria-hidden="true"></i>',
- info: undefined
- },
-
- 'ipfs': {
- title: 'IPFS',
- icon: '<i class="fa fa-folder-open" aria-hidden="true"></i>',
- info: undefined
- },
-
- 'phpfpm': {
- title: 'PHP-FPM',
- icon: '<i class="fa fa-eye" aria-hidden="true"></i>',
- info: undefined,
- },
-
- 'postfix': {
- title: 'postfix',
- icon: '<i class="fa fa-envelope" aria-hidden="true"></i>',
- info: undefined,
- },
-
- 'nginx': {
- title: 'nginx',
- icon: '<i class="fa fa-eye" aria-hidden="true"></i>',
- info: undefined,
- },
-
- 'apache': {
- title: 'Apache',
- icon: '<i class="fa fa-eye" aria-hidden="true"></i>',
- info: undefined,
- },
-
- 'named': {
- title: 'named',
- icon: '<i class="fa fa-tag" aria-hidden="true"></i>',
- info: undefined
- },
-
- 'squid': {
- title: 'squid',
- icon: '<i class="fa fa-exchange" aria-hidden="true"></i>',
- info: undefined
- },
-
- 'nut': {
- title: 'UPS',
- icon: '<i class="fa fa-battery-half" aria-hidden="true"></i>',
- info: undefined
- },
-
- 'apcupsd': {
- title: 'UPS',
- icon: '<i class="fa fa-battery-half" aria-hidden="true"></i>',
- info: undefined
- },
-
- 'smawebbox': {
- title: 'Solar Power',
- icon: '<i class="fa fa-sun-o" aria-hidden="true"></i>',
- info: undefined
- },
-
- 'snmp': {
- title: 'SNMP',
- icon: '<i class="fa fa-random" aria-hidden="true"></i>',
- info: undefined
- }
-};
-
-// submenu
-// information about the submenus
-netdataDashboard.submenu = {
- 'mem.ksm': {
- title: 'Memory Deduper',
- info: 'Kernel Same-page Merging (KSM) performance monitoring, read from several files in <code>/sys/kernel/mm/ksm/</code>. KSM is a memory-saving de-duplication feature in the Linux kernel (since version 2.6.32). The KSM daemon ksmd periodically scans those areas of user memory which have been registered with it, looking for pages of identical content which can be replaced by a single write-protected page (which is automatically copied if a process later wants to update its content). KSM was originally developed for use with KVM (where it was known as Kernel Shared Memory), to fit more virtual machines into physical memory, by sharing the data common between them. But it can be useful to any application which generates many instances of the same data.'
- },
-
- 'ipv4.ecn': {
- info: '<a href="https://en.wikipedia.org/wiki/Explicit_Congestion_Notification" target="_blank">Explicit Congestion Notification (ECN)</a> is a TCP extension that allows end-to-end notification of network congestion without dropping packets. ECN is an optional feature that may be used between two ECN-enabled endpoints when the underlying network infrastructure also supports it.'
- },
-
- 'netfilter.conntrack': {
- title: 'Connection Tracker',
- info: 'Netfilter Connection Tracker performance monitoring, read from <code>/proc/net/stat/nf_conntrack</code>. The connection tracker keeps track of all connections of the machine, inbound and outbound. It works by keeping a database with all open connections, tracking network and address translation and connection expectations.'
- },
-
- 'netfilter.nfacct': {
- title: 'Bandwidth Accounting',
- info: 'The following information is read using the <code>nfacct.plugin</code>.'
- },
-
- 'netfilter.synproxy': {
- title: 'DDoS Protection',
- info: 'DDoS Protection performance monitoring read from <code>/proc/net/stat/synproxy</code>. <a href="https://github.com/firehol/firehol/wiki/Working-with-SYNPROXY" target="_blank">SYNPROXY</a> is a TCP SYN packets proxy. It is used to protect any TCP server (like a web server) from SYN floods and similar DDoS attacks. It is a netfilter module, in the Linux kernel (since version 3.12). It is optimized to handle millions of packets per second utilizing all CPUs available without any concurrency locking between the connections. It can be used for any kind of TCP traffic (even encrypted), since it does not interfere with the content itself.'
- },
-
- 'system.softnet_stat': {
- title: 'softnet',
- info: 'Statistics for CPUs SoftIRQs related to network receive work, read from <code>/proc/net/softnet_stat</code>. Break down per CPU core can be found at <a href="#cpu_softnet_stat">CPU / softnet statistics</a>. <b>processed</b> states the number of packets processed, <b>dropped</b> is the number packets dropped because the network device backlog was full (to fix them use <code>sysctl</code> to increase <code>net.core.netdev_max_backlog</code>), <b>squeezed</b> is the number of packets dropped because the network device budget ran out (to fix them use <code>sysctl</code> to increase <code>net.core.netdev_budget</code>). More information about identifying and troubleshooting network driver related issues can be found at <a href="https://access.redhat.com/sites/default/files/attachments/20150325_network_performance_tuning.pdf" target="_blank">Red Hat Enterprise Linux Network Performance Tuning Guide</a>.'
- },
-
- 'cpu.softnet_stat': {
- title: 'softnet',
- info: 'Statistics for per CPUs core SoftIRQs related to network receive work, read from <code>/proc/net/softnet_stat</code>. Total for all CPU cores can be found at <a href="#system_softnet_stat">System / softnet statistics</a>. <b>processed</b> states the number of packets processed, <b>dropped</b> is the number packets dropped because the network device backlog was full (to fix them use <code>sysctl</code> to increase <code>net.core.netdev_max_backlog</code>), <b>squeezed</b> is the number of packets dropped because the network device budget ran out (to fix them use <code>sysctl</code> to increase <code>net.core.netdev_budget</code>). More information about identifying and troubleshooting network driver related issues can be found at <a href="https://access.redhat.com/sites/default/files/attachments/20150325_network_performance_tuning.pdf" target="_blank">Red Hat Enterprise Linux Network Performance Tuning Guide</a>.'
- }
-};
-
-// chart
-// information works on the context of a chart
-// Its purpose is to set:
-//
-// info: the text above the charts
-// heads: the representation of the chart at the top the subsection (second level menu)
-// mainheads: the representation of the chart at the top of the section (first level menu)
-// colors: the dimension colors of the chart (the default colors are appended)
-// height: the ratio of the chart height relative to the default
-//
-netdataDashboard.context = {
- 'system.cpu': {
- info: 'Total CPU utilization (all cores). 100% here means there is no CPU idle time at all. You can get per core usage at the <a href="#cpu">CPUs</a> section and per application usage at the <a href="#apps">Applications Monitoring</a> section.<br/>Keep an eye on <b>iowait</b> ' + sparkline('system.cpu', 'iowait', '%') + '. If it is constantly high, your disks are a bottleneck and they slow your system down.<br/>Another important metric worth monitoring, is <b>softirq</b> ' + sparkline('system.cpu', 'softirq', '%') + '. A constantly high percentage of softirq may indicate network drivers issues.'
- },
-
- 'system.load': {
- info: 'Current system load, i.e. the number of processes using CPU or waiting for system resources (usually CPU and disk). The 3 metrics refer to 1, 5 and 15 minute averages. Linux calculates this once every 5 seconds. Netdata reads them from <code>/proc/loadavg</code>. For more information check <a href="https://en.wikipedia.org/wiki/Load_(computing)" target="_blank">this wikipedia article</a>',
- height: 0.7
- },
-
- 'system.io': {
- info: 'Total Disk I/O, for all disks, read from <code>/proc/vmstat</code>. You can get detailed information about each disk at the <a href="#disk">Disks</a> section and per application Disk usage at the <a href="#apps">Applications Monitoring</a> section.'
- },
-
- 'system.swapio': {
- info: 'Total Swap I/O, read from <code>/proc/vmstat</code>. (netdata measures both <code>in</code> and <code>out</code>. If either of them is not shown in the chart, it is because it is zero - you can change the page settings to always render all the available dimensions on all charts).'
- },
-
- 'system.pgfaults': {
- info: 'Total page faults, read from <code>/proc/vmstat</code>. <b>Major page faults</b> indicates that the system is using its swap. You can find which applications use the swap at the <a href="#apps">Applications Monitoring</a> section.'
- },
-
- 'system.entropy': {
- colors: '#CC22AA',
- info: '<a href="https://en.wikipedia.org/wiki/Entropy_(computing)" target="_blank">Entropy</a>, read from <code>/proc/sys/kernel/random/entropy_avail</code>, is like a pool of random numbers (<a href="https://en.wikipedia.org/wiki//dev/random" target="_blank">/dev/random</a>) that are mainly used in cryptography. It is advised that the pool remains always <a href="https://blog.cloudflare.com/ensuring-randomness-with-linuxs-random-number-generator/" target="_blank">above 200</a>. If the pool of entropy gets empty, you risk your security to be predictable and you should install a user-space random numbers generating daemon, like <a href="http://www.issihosts.com/haveged/" target="_blank">haveged</a> or <code>rng-tools</code> (i.e. <b>rngd</b>), to keep the pool in healthy levels.'
- },
-
- 'system.forks': {
- colors: '#5555DD',
- info: 'The number of new processes created per second, read from <code>/proc/stat</code>.'
- },
-
- 'system.intr': {
- colors: '#DD5555',
- info: 'Total number of CPU interrupts, read from <code>/proc/stat</code>. Check <code>system.interrupts</code> that gives more detail about each interrupt and also the <a href="#cpu">CPUs</a> section where interrupts are analyzed per CPU core.'
- },
-
- 'system.interrupts': {
- info: 'CPU interrupts in detail, read from <code>/proc/interrupts</code>. At the <a href="#cpu">CPUs</a> section, interrupts are analyzed per CPU core.'
- },
-
- 'system.softirqs': {
- info: 'CPU softirqs in detail, read from <code>/proc/softirqs</code>. At the <a href="#cpu">CPUs</a> section, softirqs are analyzed per CPU core.'
- },
-
- 'system.processes': {
- info: 'System processes, read from <code>/proc/stat</code>. <b>Running</b> are the processes in the CPU. <b>Blocked</b> are processes that are willing to enter the CPU, but they cannot, e.g. because they wait for disk activity.'
- },
-
- 'system.active_processes': {
- info: 'All system processes, read from <code>/proc/loadavg</code>.'
- },
-
- 'system.ctxt': {
- info: '<a href="https://en.wikipedia.org/wiki/Context_switch" target="_blank">Context Switches</a>, read from <code>/proc/stat</code>, is the switching of the CPU from one process, task or thread to another. If there are many processes or threads willing to execute and very few CPU cores available to handle them, the system is making more context switching to balance the CPU resources among them. The whole process is computationally intensive. The more the context switches, the slower the system gets.'
- },
-
- 'system.idlejitter': {
- colors: '#5555AA',
- info: 'Idle jitter is calculated by netdata. A thread is spawned that requests to sleep for a few microseconds. When the system wakes it up, it measures how many microseconds have passed. The difference between the requested and the actual duration of the sleep, is the <b>idle jitter</b>. This number is useful in real-time environments, where CPU jitter can affect the quality of the service (like VoIP media gateways).'
- },
-
- 'system.ipv4': {
- info: 'Total IPv4 Traffic, read from <code>/proc/net/netstat</code>.'
- },
-
- 'system.ipv6': {
- info: 'Total IPv6 Traffic, read from <code>/proc/net/snmp6</code>.'
- },
-
- 'system.ram': {
- info: 'System memory, read from <code>/proc/meminfo</code>.'
- },
-
- 'system.swap': {
- info: 'System swap memory, read from <code>/proc/meminfo</code>.'
- },
-
- // ------------------------------------------------------------------------
- // MEMORY
-
- 'mem.ksm_savings': {
- heads: [
- netdataDashboard.gaugeChart('Saved', '12%', 'savings', '#0099CC')
- ]
- },
-
- 'mem.ksm_ratios': {
- heads: [
- function(id) {
- return '<div data-netdata="' + id + '"'
- + ' data-gauge-max-value="100"'
- + ' data-chart-library="gauge"'
- + ' data-title="Savings"'
- + ' data-units="percentage %"'
- + ' data-gauge-adjust="width"'
- + ' data-width="12%"'
- + ' data-before="0"'
- + ' data-after="-CHART_DURATION"'
- + ' data-points="CHART_DURATION"'
- + ' role="application"></div>';
- }
- ]
- },
-
- 'mem.committed': {
- colors: NETDATA.colors[3]
- },
-
- // ------------------------------------------------------------------------
- // network interfaces
-
- 'net.drops': {
- info: 'Packets that have been dropped at the network interface level. These are the same counters reported by <code>ifconfig</code> as <code>RX dropped</code> (inbound) and <code>TX dropped</code> (outbound). <b>inbound</b> packets can be dropped at the network interface level due to <a href="#system_softnet_stat">softnet backlog</a> overflow, bad / unintented VLAN tags, unknown or unregistered protocols, IPv6 frames when the server is not configured for IPv6. Check <a href="https://www.novell.com/support/kb/doc.php?id=7007165" target="_blank">this document</a> for more information.'
- },
-
- // ------------------------------------------------------------------------
- // IPv4
-
- 'ipv4.tcpmemorypressures': {
- info: 'Number of times a socket was put in <b>memory pressure</b> due to a non fatal memory allocation failure (the kernel attempts to work around this situation by reducing the send buffers, etc).'
- },
-
- 'ipv4.tcpconnaborts': {
- info: 'TCP connection aborts. <b>baddata</b> (<code>TCPAbortOnData</code>) happens while the connection is on <code>FIN_WAIT1</code> and the kernel receives a packet with a sequence number beyond the last one for this connection - the kernel responds with <code>RST</code> (closes the connection). <b>userclosed</b> (<code>TCPAbortOnClose</code>) happens when the kernel receives data on an already closed connection and responds with <code>RST</code>. <b>nomemory</b> (<code>TCPAbortOnMemory</code> happens when there are too many orphaned sockets (not attached to an fd) and the kernel has to drop a connection - sometimes it will send an <code>RST</code>, sometimes it won\'t. <b>timeout</b> (<code>TCPAbortOnTimeout</code>) happens when a connection times out. <b>linger</b> (<code>TCPAbortOnLinger</code>) happens when the kernel killed a socket that was already closed by the application and lingered around for long enough. <b>failed</b> (<code>TCPAbortFailed</code>) happens when the kernel attempted to send an <code>RST</code> but failed because there was no memory available.'
- },
-
- // ------------------------------------------------------------------------
- // APPS
-
- 'apps.cpu': {
- height: 2.0
- },
-
- 'apps.mem': {
- info: 'Real memory (RAM) used by applications. This does not include shared memory.'
- },
-
- 'apps.vmem': {
- info: 'Virtual memory allocated by applications. Please check <a href="https://github.com/firehol/netdata/wiki/netdata-virtual-memory-size" target="_blank">this article</a> for more information.'
- },
-
- 'apps.preads': {
- height: 2.0
- },
-
- 'apps.pwrites': {
- height: 2.0
- },
-
- // ------------------------------------------------------------------------
- // USERS
-
- 'users.cpu': {
- height: 2.0
- },
-
- 'users.mem': {
- info: 'Real memory (RAM) used per user. This does not include shared memory.'
- },
-
- 'users.vmem': {
- info: 'Virtual memory allocated per user. Please check <a href="https://github.com/firehol/netdata/wiki/netdata-virtual-memory-size" target="_blank">this article</a> for more information.'
- },
-
- 'users.preads': {
- height: 2.0
- },
-
- 'users.pwrites': {
- height: 2.0
- },
-
- // ------------------------------------------------------------------------
- // GROUPS
-
- 'groups.cpu': {
- height: 2.0
- },
-
- 'groups.mem': {
- info: 'Real memory (RAM) used per user group. This does not include shared memory.'
- },
-
- 'groups.vmem': {
- info: 'Virtual memory allocated per user group. Please check <a href="https://github.com/firehol/netdata/wiki/netdata-virtual-memory-size" target="_blank">this article</a> for more information.'
- },
-
- 'groups.preads': {
- height: 2.0
- },
-
- 'groups.pwrites': {
- height: 2.0
- },
-
- // ------------------------------------------------------------------------
- // NETWORK QoS
-
- 'tc.qos': {
- heads: [
- function(id) {
- if(id.match(/.*-ifb$/))
- return netdataDashboard.gaugeChart('Inbound', '12%', '', '#5555AA');
- else
- return netdataDashboard.gaugeChart('Outbound', '12%', '', '#AA9900');
- }
- ]
- },
-
- // ------------------------------------------------------------------------
- // NETWORK INTERFACES
-
- 'net.net': {
- heads: [
- netdataDashboard.gaugeChart('Received', '12%', 'received'),
- netdataDashboard.gaugeChart('Sent', '12%', 'sent')
- ]
- },
-
- // ------------------------------------------------------------------------
- // NETFILTER
-
- 'netfilter.sockets': {
- colors: '#88AA00',
- heads: [
- netdataDashboard.gaugeChart('Active Connections', '12%', '', '#88AA00')
- ]
- },
-
- 'netfilter.new': {
- heads: [
- netdataDashboard.gaugeChart('New Connections', '12%', 'new', '#5555AA')
- ]
- },
-
- // ------------------------------------------------------------------------
- // DISKS
-
- 'disk.util': {
- colors: '#FF5588',
- heads: [
- netdataDashboard.gaugeChart('Utilization', '12%', '', '#FF5588')
- ],
- info: 'Disk Utilization measures the amount of time the disk was busy with something. This is not related to its performance. 100% means that the Linux kernel always had an outstanding operation on the disk. Keep in mind that depending on the underlying technology of the disk, 100% here may or may not be an indication of congestion.'
- },
-
- 'disk.backlog': {
- colors: '#0099CC',
- info: 'Backlog is an indication of the duration of pending disk operations. On every I/O event the Linux kernel is multiplying the time spent doing I/O since the last update of this field with the number of pending operations. While not accurate, this metric can provide an indication of the expected completion time of the operations in progress.'
- },
-
- 'disk.io': {
- heads: [
- netdataDashboard.gaugeChart('Read', '12%', 'reads'),
- netdataDashboard.gaugeChart('Write', '12%', 'writes')
- ],
- info: 'Amount of data transferred to and from disk.'
- },
-
- 'disk.ops': {
- info: 'Completed disk I/O operations. Keep in mind the number of operations requested might be higher, since the Linux kernel is able to merge adjacent to each other (see merged operations chart).'
- },
-
- 'disk.qops': {
- info: 'I/O operations currently in progress. This metric is a snapshot - it is not an average over the last interval.'
- },
-
- 'disk.iotime': {
- height: 0.5,
- info: 'The sum of the duration of all completed I/O operations. This number can exceed the interval if the disk is able to execute I/O operations in parallel.'
- },
- 'disk.mops': {
- height: 0.5,
- info: 'The number of merged disk operations. The Linux kernel is able to merge adjacent I/O operations, for example two 4KB reads can become one 8KB read before given to disk.'
- },
- 'disk.svctm': {
- height: 0.5,
- info: 'The average service time for completed I/O operations. This metric is calculated using the total busy time of the disk and the number of completed operations. If the disk is able to execute multiple parallel operations the reporting average service time will be misleading.'
- },
- 'disk.avgsz': {
- height: 0.5,
- info: 'The average I/O operation size.'
- },
- 'disk.await': {
- height: 0.5,
- info: 'The average time for I/O requests issued to the device to be served. This includes the time spent by the requests in queue and the time spent servicing them.'
- },
-
- 'disk.space': {
- info: 'Disk space utilization. reserved for root is automatically reserved by the system to prevent the root user from getting out of space.'
- },
- 'disk.inodes': {
- info: 'inodes (or index nodes) are filesystem objects (e.g. files and directories). On many types of file system implementations, the maximum number of inodes is fixed at filesystem creation, limiting the maximum number of files the filesystem can hold. It is possible for a device to run out of inodes. When this happens, new files cannot be created on the device, even though there may be free space available.'
- },
-
- 'mysql.net': {
- info: 'The amount of data sent to mysql clients (<strong>out</strong>) and received from mysql clients (<strong>in</strong>).'
- },
-
- // ------------------------------------------------------------------------
- // MYSQL
-
- 'mysql.queries': {
- info: 'The number of statements executed by the server.<ul>' +
- '<li><strong>queries</strong> counts the statements executed within stored SQL programs.</li>' +
- '<li><strong>questions</strong> counts the statements sent to the mysql server by mysql clients.</li>' +
- '<li><strong>slow queries</strong> counts the number of statements that took more than <a href="http://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html#sysvar_long_query_time" target="_blank">long_query_time</a> seconds to be executed.' +
- ' For more information about slow queries check the mysql <a href="http://dev.mysql.com/doc/refman/5.7/en/slow-query-log.html" target="_blank">slow query log</a>.</li>' +
- '</ul>'
- },
-
- 'mysql.handlers': {
- info: 'Usage of the internal handlers of mysql. This chart provides very good insights of what the mysql server is actually doing.' +
- ' (if the chart is not showing all these dimensions it is because they are zero - set <strong>Which dimensions to show?</strong> to <strong>All</strong> from the dashboard settings, to render even the zero values)<ul>' +
- '<li><strong>commit</strong>, the number of internal <a href="http://dev.mysql.com/doc/refman/5.7/en/commit.html" target="_blank">COMMIT</a> statements.</li>' +
- '<li><strong>delete</strong>, the number of times that rows have been deleted from tables.</li>' +
- '<li><strong>prepare</strong>, a counter for the prepare phase of two-phase commit operations.</li>' +
- '<li><strong>read first</strong>, the number of times the first entry in an index was read. A high value suggests that the server is doing a lot of full index scans; e.g. <strong>SELECT col1 FROM foo</strong>, with col1 indexed.</li>' +
- '<li><strong>read key</strong>, the number of requests to read a row based on a key. If this value is high, it is a good indication that your tables are properly indexed for your queries.</li>' +
- '<li><strong>read next</strong>, the number of requests to read the next row in key order. This value is incremented if you are querying an index column with a range constraint or if you are doing an index scan.</li>' +
- '<li><strong>read prev</strong>, the number of requests to read the previous row in key order. This read method is mainly used to optimize <strong>ORDER BY ... DESC</strong>.</li>' +
- '<li><strong>read rnd</strong>, the number of requests to read a row based on a fixed position. A high value indicates you are doing a lot of queries that require sorting of the result. You probably have a lot of queries that require MySQL to scan entire tables or you have joins that do not use keys properly.</li>' +
- '<li><strong>read rnd next</strong>, the number of requests to read the next row in the data file. This value is high if you are doing a lot of table scans. Generally this suggests that your tables are not properly indexed or that your queries are not written to take advantage of the indexes you have.</li>' +
- '<li><strong>rollback</strong>, the number of requests for a storage engine to perform a rollback operation.</li>' +
- '<li><strong>savepoint</strong>, the number of requests for a storage engine to place a savepoint.</li>' +
- '<li><strong>savepoint rollback</strong>, the number of requests for a storage engine to roll back to a savepoint.</li>' +
- '<li><strong>update</strong>, the number of requests to update a row in a table.</li>' +
- '<li><strong>write</strong>, the number of requests to insert a row in a table.</li>' +
- '</ul>'
- },
-
- 'mysql.table_locks': {
- info: 'MySQL table locks counters: <ul>' +
- '<li><strong>immediate</strong>, the number of times that a request for a table lock could be granted immediately.</li>' +
- '<li><strong>waited</strong>, the number of times that a request for a table lock could not be granted immediately and a wait was needed. If this is high and you have performance problems, you should first optimize your queries, and then either split your table or tables or use replication.</li>' +
- '</ul>'
- },
-
- // ------------------------------------------------------------------------
- // APACHE
-
- 'apache.connections': {
- colors: NETDATA.colors[4],
- mainheads: [
- netdataDashboard.gaugeChart('Connections', '12%', '', NETDATA.colors[4])
- ]
- },
-
- 'apache.requests': {
- colors: NETDATA.colors[0],
- mainheads: [
- netdataDashboard.gaugeChart('Requests', '12%', '', NETDATA.colors[0])
- ]
- },
-
- 'apache.net': {
- colors: NETDATA.colors[3],
- mainheads: [
- netdataDashboard.gaugeChart('Bandwidth', '12%', '', NETDATA.colors[3])
- ]
- },
-
- 'apache.workers': {
- mainheads: [
- function(id) {
- return '<div data-netdata="' + id + '"'
- + ' data-dimensions="busy"'
- + ' data-append-options="percentage"'
- + ' data-gauge-max-value="100"'
- + ' data-chart-library="gauge"'
- + ' data-title="Workers Utilization"'
- + ' data-units="percentage %"'
- + ' data-gauge-adjust="width"'
- + ' data-width="12%"'
- + ' data-before="0"'
- + ' data-after="-CHART_DURATION"'
- + ' data-points="CHART_DURATION"'
- + ' role="application"></div>';
- }
- ]
- },
-
- 'apache.bytesperreq': {
- colors: NETDATA.colors[3],
- height: 0.5
- },
-
- 'apache.reqpersec': {
- colors: NETDATA.colors[4],
- height: 0.5
- },
-
- 'apache.bytespersec': {
- colors: NETDATA.colors[6],
- height: 0.5
- },
-
-
- // ------------------------------------------------------------------------
- // NGINX
-
- 'nginx.connections': {
- colors: NETDATA.colors[4],
- mainheads: [
- netdataDashboard.gaugeChart('Connections', '12%', '', NETDATA.colors[4])
- ]
- },
-
- 'nginx.requests': {
- colors: NETDATA.colors[0],
- mainheads: [
- netdataDashboard.gaugeChart('Requests', '12%', '', NETDATA.colors[0])
- ]
- },
-
- // ------------------------------------------------------------------------
- // NETDATA
-
- 'netdata.response_time': {
- info: 'The netdata API response time measures the time netdata needed to serve requests. This time includes everything, from the reception of the first byte of a request, to the dispatch of the last byte of its reply, therefore it includes all network latencies involved (i.e. a client over a slow network will influence these metrics).'
- },
-
- // ------------------------------------------------------------------------
- // RETROSHARE
- 'retroshare.bandwidth': {
- info: 'Shows inbound and outbound traffic.',
- mainheads: [
- netdataDashboard.gaugeChart('Received', '12%', 'bandwidth_down_kb'),
- netdataDashboard.gaugeChart('Sent', '12%', 'bandwidth_up_kb')
- ]
- },
-
- 'retroshare.peers': {
- info: 'Shows the number of (connected) friends.',
- mainheads: [
- function(id) {
- return '<div data-netdata="' + id + '"'
- + ' data-dimensions="peers_connected"'
- + ' data-append-options="friends"'
- + ' data-chart-library="easypiechart"'
- + ' data-title="connected friends"'
- + ' data-units=""'
- + ' data-width="8%"'
- + ' data-before="0"'
- + ' data-after="-CHART_DURATION"'
- + ' data-points="CHART_DURATION"'
- + ' role="application"></div>';
- }
- ]
- },
-
- 'retroshare.dht': {
- info: 'Shows statistics about RetroShare\'s DHT. These values are estimated!'
- }
-};
+ +var netdataDashboard = window.netdataDashboard || {}; + +// menu +// information about the main menus + +netdataDashboard.menu = { + 'system': { + title: 'System Overview', + icon: '<i class="fa fa-bookmark" aria-hidden="true"></i>', + info: 'Overview of the key system metrics.' + }, + + 'services': { + title: 'Systemd Services', + icon: '<i class="fa fa-cogs" aria-hidden="true"></i>', + info: 'Resources utilization of systemd services.' + }, + + 'ap': { + title: 'Access Points', + icon: '<i class="fa fa-wifi" aria-hidden="true"></i>', + info: undefined + }, + + 'tc': { + title: 'Quality of Service', + icon: '<i class="fa fa-globe" aria-hidden="true"></i>', + info: 'Netdata collects and visualizes tc class utilization using its <a href="https://github.com/firehol/netdata/blob/master/plugins.d/tc-qos-helper.sh" target="_blank">tc-helper plugin</a>. If you also use <a href="http://firehol.org/#fireqos" target="_blank">FireQOS</a> for setting up QoS, netdata automatically collects interface and class names. If your QoS configuration includes overheads calculation, the values shown here will include these overheads (the total bandwidth for the same interface as reported in the Network Interfaces section, will be lower than the total bandwidth reported here). Also, data collection may have a slight time difference compared to the interface (QoS data collection is implemented with a BASH script, so a shift in data collection of a few milliseconds should be justified).' + }, + + 'net': { + title: 'Network Interfaces', + icon: '<i class="fa fa-share-alt" aria-hidden="true"></i>', + info: 'Per network interface statistics collected from <code>/proc/net/dev</code>.' + }, + + 'ipv4': { + title: 'IPv4 Networking', + icon: '<i class="fa fa-cloud" aria-hidden="true"></i>', + info: undefined + }, + + 'ipv6': { + title: 'IPv6 Networking', + icon: '<i class="fa fa-cloud" aria-hidden="true"></i>', + info: undefined + }, + + 'ipvs': { + title: 'IP Virtual Server', + icon: '<i class="fa fa-eye" aria-hidden="true"></i>', + info: undefined + }, + + 'netfilter': { + title: 'Firewall (netfilter)', + icon: '<i class="fa fa-shield" aria-hidden="true"></i>', + info: undefined + }, + + 'cpu': { + title: 'CPUs', + icon: '<i class="fa fa-bolt" aria-hidden="true"></i>', + info: undefined + }, + + 'mem': { + title: 'Memory', + icon: '<i class="fa fa-bolt" aria-hidden="true"></i>', + info: undefined + }, + + 'disk': { + title: 'Disks', + icon: '<i class="fa fa-folder" aria-hidden="true"></i>', + info: 'Charts with performance information for all the system disks. Special care has been given to present disk performance metrics in a way compatible with <code>iostat -x</code>. netdata by default prevents rendering performance charts for individual partitions and unmounted virtual disks. Disabled charts can still be enabled by altering the relative settings in the netdata configuration file.' + }, + + 'sensors': { + title: 'Sensors', + icon: '<i class="fa fa-leaf" aria-hidden="true"></i>', + info: undefined + }, + + 'nfsd': { + title: 'NFS Server', + icon: '<i class="fa fa-folder-open" aria-hidden="true"></i>', + info: undefined + }, + + 'nfs': { + title: 'NFS Client', + icon: '<i class="fa fa-folder-open" aria-hidden="true"></i>', + info: undefined + }, + + 'apps': { + title: 'Applications', + icon: '<i class="fa fa-heartbeat" aria-hidden="true"></i>', + info: 'Per application statistics are collected using netdata\'s <code>apps.plugin</code>. This plugin walks through the entire <code>/proc</code> filesystem and aggregates statistics for applications of interest, defined in <code>/etc/netdata/apps_groups.conf</code> (the default is <a href="https://github.com/firehol/netdata/blob/master/conf.d/apps_groups.conf" target="_blank">here</a>). The plugin internally builds a process tree (much like <code>ps fax</code> does), and groups processes together (evaluating both child and parent processes) so that the result is always a chart with a predefined set of dimensions (of course, only application groups found running are reported). The reported values are compatible with <code>top</code>, although the netdata plugin counts also the resources of exited children (unlike <code>top</code> which shows only the resources of the currently running processes). So for processes like shell scripts, the reported values include the resources used by the commands these scripts run within each timeframe.', + height: 1.5 + }, + + 'users': { + title: 'Users', + icon: '<i class="fa fa-user" aria-hidden="true"></i>', + info: 'Per user statistics are collected using netdata\'s <code>apps.plugin</code>. This plugin walks through the entire <code>/proc</code> filesystem and aggregates statistics per user. The reported values are compatible with <code>top</code>, although the netdata plugin counts also the resources of exited children (unlike <code>top</code> which shows only the resources of the currently running processes). So for processes like shell scripts, the reported values include the resources used by the commands these scripts run within each timeframe.', + height: 1.5 + }, + + 'groups': { + title: 'User Groups', + icon: '<i class="fa fa-users" aria-hidden="true"></i>', + info: 'Per user group statistics are collected using netdata\'s <code>apps.plugin</code>. This plugin walks through the entire <code>/proc</code> filesystem and aggregates statistics per user group. The reported values are compatible with <code>top</code>, although the netdata plugin counts also the resources of exited children (unlike <code>top</code> which shows only the resources of the currently running processes). So for processes like shell scripts, the reported values include the resources used by the commands these scripts run within each timeframe.', + height: 1.5 + }, + + 'netdata': { + title: 'Netdata Monitoring', + icon: '<i class="fa fa-bar-chart" aria-hidden="true"></i>', + info: undefined + }, + + 'example': { + title: 'Example Charts', + info: undefined + }, + + 'cgroup': { + title: '', + icon: '<i class="fa fa-th" aria-hidden="true"></i>', + info: undefined + }, + + 'cgqemu': { + title: '', + icon: '<i class="fa fa-th-large" aria-hidden="true"></i>', + info: undefined + }, + + 'fping': { + title: 'fping', + icon: '<i class="fa fa-exchange" aria-hidden="true"></i>', + info: undefined + }, + + 'memcached': { + title: 'memcached', + icon: '<i class="fa fa-database" aria-hidden="true"></i>', + info: undefined + }, + + 'mysql': { + title: 'MySQL', + icon: '<i class="fa fa-database" aria-hidden="true"></i>', + info: undefined + }, + + 'postgres': { + title: 'Postgres', + icon: '<i class="fa fa-database" aria-hidden="true"></i>', + info: undefined + }, + + 'redis': { + title: 'Redis', + icon: '<i class="fa fa-database" aria-hidden="true"></i>', + info: undefined + }, + + 'retroshare': { + title: 'RetroShare', + icon: '<i class="fa fa-share-alt" aria-hidden="true"></i>', + info: undefined + }, + + 'ipfs': { + title: 'IPFS', + icon: '<i class="fa fa-folder-open" aria-hidden="true"></i>', + info: undefined + }, + + 'phpfpm': { + title: 'PHP-FPM', + icon: '<i class="fa fa-eye" aria-hidden="true"></i>', + info: undefined + }, + + 'postfix': { + title: 'postfix', + icon: '<i class="fa fa-envelope" aria-hidden="true"></i>', + info: undefined + }, + + 'dovecot': { + title: 'Dovecot', + icon: '<i class="fa fa-envelope" aria-hidden="true"></i>', + info: undefined + }, + + 'hddtemp': { + title: 'HDD Temp', + icon: '<i class="fa fa-thermometer-full" aria-hidden="true"></i>', + info: undefined + }, + + 'nginx': { + title: 'nginx', + icon: '<i class="fa fa-eye" aria-hidden="true"></i>', + info: undefined + }, + + 'apache': { + title: 'Apache', + icon: '<i class="fa fa-eye" aria-hidden="true"></i>', + info: undefined + }, + + 'named': { + title: 'named', + icon: '<i class="fa fa-tag" aria-hidden="true"></i>', + info: undefined + }, + + 'squid': { + title: 'squid', + icon: '<i class="fa fa-exchange" aria-hidden="true"></i>', + info: undefined + }, + + 'nut': { + title: 'UPS', + icon: '<i class="fa fa-battery-half" aria-hidden="true"></i>', + info: undefined + }, + + 'apcupsd': { + title: 'UPS', + icon: '<i class="fa fa-battery-half" aria-hidden="true"></i>', + info: undefined + }, + + 'smawebbox': { + title: 'Solar Power', + icon: '<i class="fa fa-sun-o" aria-hidden="true"></i>', + info: undefined + }, + + 'snmp': { + title: 'SNMP', + icon: '<i class="fa fa-random" aria-hidden="true"></i>', + info: undefined + } +}; + +// submenu +// information about the submenus +netdataDashboard.submenu = { + 'mem.ksm': { + title: 'Memory Deduper', + info: 'Kernel Same-page Merging (KSM) performance monitoring, read from several files in <code>/sys/kernel/mm/ksm/</code>. KSM is a memory-saving de-duplication feature in the Linux kernel (since version 2.6.32). The KSM daemon ksmd periodically scans those areas of user memory which have been registered with it, looking for pages of identical content which can be replaced by a single write-protected page (which is automatically copied if a process later wants to update its content). KSM was originally developed for use with KVM (where it was known as Kernel Shared Memory), to fit more virtual machines into physical memory, by sharing the data common between them. But it can be useful to any application which generates many instances of the same data.' + }, + + 'mem.numa': { + info: 'Non-Uniform Memory Access (NUMA) is a hierarchical memory design the memory access time is dependent on locality. Under NUMA, a processor can access its own local memory faster than non-local memory (memory local to another processor or memory shared between processors). The individual metrics are described in the <a href="https://www.kernel.org/doc/Documentation/numastat.txt" target="_blank">Linux kernel documentation</a>.' + }, + + 'ipv4.ecn': { + info: '<a href="https://en.wikipedia.org/wiki/Explicit_Congestion_Notification" target="_blank">Explicit Congestion Notification (ECN)</a> is a TCP extension that allows end-to-end notification of network congestion without dropping packets. ECN is an optional feature that may be used between two ECN-enabled endpoints when the underlying network infrastructure also supports it.' + }, + + 'netfilter.conntrack': { + title: 'Connection Tracker', + info: 'Netfilter Connection Tracker performance monitoring, read from <code>/proc/net/stat/nf_conntrack</code>. The connection tracker keeps track of all connections of the machine, inbound and outbound. It works by keeping a database with all open connections, tracking network and address translation and connection expectations.' + }, + + 'netfilter.nfacct': { + title: 'Bandwidth Accounting', + info: 'The following information is read using the <code>nfacct.plugin</code>.' + }, + + 'netfilter.synproxy': { + title: 'DDoS Protection', + info: 'DDoS Protection performance monitoring read from <code>/proc/net/stat/synproxy</code>. <a href="https://github.com/firehol/firehol/wiki/Working-with-SYNPROXY" target="_blank">SYNPROXY</a> is a TCP SYN packets proxy. It is used to protect any TCP server (like a web server) from SYN floods and similar DDoS attacks. It is a netfilter module, in the Linux kernel (since version 3.12). It is optimized to handle millions of packets per second utilizing all CPUs available without any concurrency locking between the connections. It can be used for any kind of TCP traffic (even encrypted), since it does not interfere with the content itself.' + }, + + 'system.softnet_stat': { + title: 'softnet', + info: 'Statistics for CPUs SoftIRQs related to network receive work, read from <code>/proc/net/softnet_stat</code>. Break down per CPU core can be found at <a href="#menu_cpu_submenu_softnet_stat">CPU / softnet statistics</a>. <b>processed</b> states the number of packets processed, <b>dropped</b> is the number packets dropped because the network device backlog was full (to fix them use <code>sysctl</code> to increase <code>net.core.netdev_max_backlog</code>), <b>squeezed</b> is the number of packets dropped because the network device budget ran out (to fix them use <code>sysctl</code> to increase <code>net.core.netdev_budget</code>). More information about identifying and troubleshooting network driver related issues can be found at <a href="https://access.redhat.com/sites/default/files/attachments/20150325_network_performance_tuning.pdf" target="_blank">Red Hat Enterprise Linux Network Performance Tuning Guide</a>.' + }, + + 'cpu.softnet_stat': { + title: 'softnet', + info: 'Statistics for per CPUs core SoftIRQs related to network receive work, read from <code>/proc/net/softnet_stat</code>. Total for all CPU cores can be found at <a href="#menu_system_submenu_softnet_stat">System / softnet statistics</a>. <b>processed</b> states the number of packets processed, <b>dropped</b> is the number packets dropped because the network device backlog was full (to fix them use <code>sysctl</code> to increase <code>net.core.netdev_max_backlog</code>), <b>squeezed</b> is the number of packets dropped because the network device budget ran out (to fix them use <code>sysctl</code> to increase <code>net.core.netdev_budget</code>). More information about identifying and troubleshooting network driver related issues can be found at <a href="https://access.redhat.com/sites/default/files/attachments/20150325_network_performance_tuning.pdf" target="_blank">Red Hat Enterprise Linux Network Performance Tuning Guide</a>.' + } +}; + +// chart +// information works on the context of a chart +// Its purpose is to set: +// +// info: the text above the charts +// heads: the representation of the chart at the top the subsection (second level menu) +// mainheads: the representation of the chart at the top of the section (first level menu) +// colors: the dimension colors of the chart (the default colors are appended) +// height: the ratio of the chart height relative to the default +// +netdataDashboard.context = { + 'system.cpu': { + info: 'Total CPU utilization (all cores). 100% here means there is no CPU idle time at all. You can get per core usage at the <a href="#menu_cpu">CPUs</a> section and per application usage at the <a href="#menu_apps">Applications Monitoring</a> section.<br/>Keep an eye on <b>iowait</b> ' + sparkline('system.cpu', 'iowait', '%') + '. If it is constantly high, your disks are a bottleneck and they slow your system down.<br/>Another important metric worth monitoring, is <b>softirq</b> ' + sparkline('system.cpu', 'softirq', '%') + '. A constantly high percentage of softirq may indicate network driver issues.', + valueRange: "[0, 100]" + }, + + 'system.load': { + info: 'Current system load, i.e. the number of processes using CPU or waiting for system resources (usually CPU and disk). The 3 metrics refer to 1, 5 and 15 minute averages. Linux calculates this once every 5 seconds. Netdata reads them from <code>/proc/loadavg</code>. For more information check <a href="https://en.wikipedia.org/wiki/Load_(computing)" target="_blank">this wikipedia article</a>', + height: 0.7 + }, + + 'system.io': { + info: 'Total Disk I/O, for all disks, read from <code>/proc/vmstat</code>. You can get detailed information about each disk at the <a href="#menu_disk">Disks</a> section and per application Disk usage at the <a href="#menu_apps">Applications Monitoring</a> section.' + }, + + 'system.swapio': { + info: 'Total Swap I/O, read from <code>/proc/vmstat</code>. (netdata measures both <code>in</code> and <code>out</code>. If either of them is not shown in the chart, it is because it is zero - you can change the page settings to always render all the available dimensions on all charts).' + }, + + 'system.pgfaults': { + info: 'Total page faults, read from <code>/proc/vmstat</code>. <b>Major page faults</b> indicates that the system is using its swap. You can find which applications use the swap at the <a href="#menu_apps">Applications Monitoring</a> section.' + }, + + 'system.entropy': { + colors: '#CC22AA', + info: '<a href="https://en.wikipedia.org/wiki/Entropy_(computing)" target="_blank">Entropy</a>, read from <code>/proc/sys/kernel/random/entropy_avail</code>, is like a pool of random numbers (<a href="https://en.wikipedia.org/wiki//dev/random" target="_blank">/dev/random</a>) that are mainly used in cryptography. It is advised that the pool remains always <a href="https://blog.cloudflare.com/ensuring-randomness-with-linuxs-random-number-generator/" target="_blank">above 200</a>. If the pool of entropy gets empty, you risk your security to be predictable and you should install a user-space random numbers generating daemon, like <code>haveged</code> or <code>rng-tools</code> (i.e. <b>rngd</b>), to keep the pool in healthy levels.' + }, + + 'system.forks': { + colors: '#5555DD', + info: 'The number of new processes created per second, read from <code>/proc/stat</code>.' + }, + + 'system.intr': { + colors: '#DD5555', + info: 'Total number of CPU interrupts, read from <code>/proc/stat</code>. Check <code>system.interrupts</code> that gives more detail about each interrupt and also the <a href="#menu_cpu">CPUs</a> section where interrupts are analyzed per CPU core.' + }, + + 'system.interrupts': { + info: 'CPU interrupts in detail, read from <code>/proc/interrupts</code>. At the <a href="#menu_cpu">CPUs</a> section, interrupts are analyzed per CPU core.' + }, + + 'system.softirqs': { + info: 'CPU softirqs in detail, read from <code>/proc/softirqs</code>. At the <a href="#menu_cpu">CPUs</a> section, softirqs are analyzed per CPU core.' + }, + + 'system.processes': { + info: 'System processes, read from <code>/proc/stat</code>. <b>Running</b> are the processes in the CPU. <b>Blocked</b> are processes that are willing to enter the CPU, but they cannot, e.g. because they wait for disk activity.' + }, + + 'system.active_processes': { + info: 'All system processes, read from <code>/proc/loadavg</code>.' + }, + + 'system.ctxt': { + info: '<a href="https://en.wikipedia.org/wiki/Context_switch" target="_blank">Context Switches</a>, read from <code>/proc/stat</code>, is the switching of the CPU from one process, task or thread to another. If there are many processes or threads willing to execute and very few CPU cores available to handle them, the system is making more context switching to balance the CPU resources among them. The whole process is computationally intensive. The more the context switches, the slower the system gets.' + }, + + 'system.idlejitter': { + colors: '#5555AA', + info: 'Idle jitter is calculated by netdata. A thread is spawned that requests to sleep for a few microseconds. When the system wakes it up, it measures how many microseconds have passed. The difference between the requested and the actual duration of the sleep, is the <b>idle jitter</b>. This number is useful in real-time environments, where CPU jitter can affect the quality of the service (like VoIP media gateways).' + }, + + 'system.ipv4': { + info: 'Total IPv4 Traffic, read from <code>/proc/net/netstat</code>.' + }, + + 'system.ipv6': { + info: 'Total IPv6 Traffic, read from <code>/proc/net/snmp6</code>.' + }, + + 'system.ram': { + info: 'System memory, read from <code>/proc/meminfo</code>.' + }, + + 'system.swap': { + info: 'System swap memory, read from <code>/proc/meminfo</code>.' + }, + + // ------------------------------------------------------------------------ + // MEMORY + + 'mem.ksm_savings': { + heads: [ + netdataDashboard.gaugeChart('Saved', '12%', 'savings', '#0099CC') + ] + }, + + 'mem.ksm_ratios': { + heads: [ + function(id) { + return '<div data-netdata="' + id + '"' + + ' data-gauge-max-value="100"' + + ' data-chart-library="gauge"' + + ' data-title="Savings"' + + ' data-units="percentage %"' + + ' data-gauge-adjust="width"' + + ' data-width="12%"' + + ' data-before="0"' + + ' data-after="-CHART_DURATION"' + + ' data-points="CHART_DURATION"' + + ' role="application"></div>'; + } + ] + }, + + 'mem.committed': { + colors: NETDATA.colors[3] + }, + + 'mem.pgfaults': { + info: 'A <a href="https://en.wikipedia.org/wiki/Page_fault" target="_blank">page fault</a> is a type of interrupt, called trap, raised by computer hardware when a running program accesses a memory page that is mapped into the virtual address space, but not actually loaded into main memory. If the page is loaded in memory at the time the fault is generated, but is not marked in the memory management unit as being loaded in memory, then it is called a <b>minor</b> or soft page fault. A <b>major</b> page fault is generated when the system needs to load the memory page from disk or swap memory. These values are read from <code>/proc/vmstat</code>.' + }, + + 'mem.committed': { + info: 'Committed Memory, read from <code>/proc/meminfo</code>, is the sum of all memory which has been allocated by processes.' + }, + + 'mem.writeback': { + info: 'Read from <code>/proc/meminfo</code>, <b>Dirty</b> is the amount of memory waiting to be written to disk. <b>Writeback</b> is how much memory is actively being written to disk.' + }, + + 'mem.kernel': { + info: 'Read from <code>/proc/meminfo</code>, This chart displays the total ammount of memory being used by the kernel. <b>Slab</b> is the amount of memory used by the kernel to cache data structures for its own use. <b>KernelStack</b> is the amount of memory allocated for each task done by the kernel. <b>PageTables</b> is the amount of memory decicated to the lowest level of page tables (A page table is used to turn a virtual address into a physical memory address). <b>VmallocUsed</b> is the amount of memory being used as virtual address space.' + }, + + 'mem.slab': { + info: 'Read from <code>/proc/meminfo</code>, <b>reclaimable</b> is the amount of memory which the kernel can reuse. <b>unreclaimable</b> can not be reused even when the kernel is lacking memory.' + }, + + // ------------------------------------------------------------------------ + // network interfaces + + 'net.drops': { + info: 'Packets that have been dropped at the network interface level. These are the same counters reported by <code>ifconfig</code> as <code>RX dropped</code> (inbound) and <code>TX dropped</code> (outbound). <b>inbound</b> packets can be dropped at the network interface level due to <a href="#menu_system_submenu_softnet_stat">softnet backlog</a> overflow, bad / unintented VLAN tags, unknown or unregistered protocols, IPv6 frames when the server is not configured for IPv6. Check <a href="https://www.novell.com/support/kb/doc.php?id=7007165" target="_blank">this document</a> for more information.' + }, + + // ------------------------------------------------------------------------ + // IPv4 + + 'ipv4.tcpmemorypressures': { + info: 'Number of times a socket was put in <b>memory pressure</b> due to a non fatal memory allocation failure (the kernel attempts to work around this situation by reducing the send buffers, etc).' + }, + + 'ipv4.tcpconnaborts': { + info: 'TCP connection aborts. <b>baddata</b> (<code>TCPAbortOnData</code>) happens while the connection is on <code>FIN_WAIT1</code> and the kernel receives a packet with a sequence number beyond the last one for this connection - the kernel responds with <code>RST</code> (closes the connection). <b>userclosed</b> (<code>TCPAbortOnClose</code>) happens when the kernel receives data on an already closed connection and responds with <code>RST</code>. <b>nomemory</b> (<code>TCPAbortOnMemory</code> happens when there are too many orphaned sockets (not attached to an fd) and the kernel has to drop a connection - sometimes it will send an <code>RST</code>, sometimes it won\'t. <b>timeout</b> (<code>TCPAbortOnTimeout</code>) happens when a connection times out. <b>linger</b> (<code>TCPAbortOnLinger</code>) happens when the kernel killed a socket that was already closed by the application and lingered around for long enough. <b>failed</b> (<code>TCPAbortFailed</code>) happens when the kernel attempted to send an <code>RST</code> but failed because there was no memory available.' + }, + + // ------------------------------------------------------------------------ + // APPS + + 'apps.cpu': { + height: 2.0 + }, + + 'apps.mem': { + info: 'Real memory (RAM) used by applications. This does not include shared memory.' + }, + + 'apps.vmem': { + info: 'Virtual memory allocated by applications. Please check <a href="https://github.com/firehol/netdata/wiki/netdata-virtual-memory-size" target="_blank">this article</a> for more information.' + }, + + 'apps.preads': { + height: 2.0 + }, + + 'apps.pwrites': { + height: 2.0 + }, + + // ------------------------------------------------------------------------ + // USERS + + 'users.cpu': { + height: 2.0 + }, + + 'users.mem': { + info: 'Real memory (RAM) used per user. This does not include shared memory.' + }, + + 'users.vmem': { + info: 'Virtual memory allocated per user. Please check <a href="https://github.com/firehol/netdata/wiki/netdata-virtual-memory-size" target="_blank">this article</a> for more information.' + }, + + 'users.preads': { + height: 2.0 + }, + + 'users.pwrites': { + height: 2.0 + }, + + // ------------------------------------------------------------------------ + // GROUPS + + 'groups.cpu': { + height: 2.0 + }, + + 'groups.mem': { + info: 'Real memory (RAM) used per user group. This does not include shared memory.' + }, + + 'groups.vmem': { + info: 'Virtual memory allocated per user group. Please check <a href="https://github.com/firehol/netdata/wiki/netdata-virtual-memory-size" target="_blank">this article</a> for more information.' + }, + + 'groups.preads': { + height: 2.0 + }, + + 'groups.pwrites': { + height: 2.0 + }, + + // ------------------------------------------------------------------------ + // NETWORK QoS + + 'tc.qos': { + heads: [ + function(id) { + if(id.match(/.*-ifb$/)) + return netdataDashboard.gaugeChart('Inbound', '12%', '', '#5555AA'); + else + return netdataDashboard.gaugeChart('Outbound', '12%', '', '#AA9900'); + } + ] + }, + + // ------------------------------------------------------------------------ + // NETWORK INTERFACES + + 'net.net': { + heads: [ + netdataDashboard.gaugeChart('Received', '12%', 'received'), + netdataDashboard.gaugeChart('Sent', '12%', 'sent') + ] + }, + + // ------------------------------------------------------------------------ + // NETFILTER + + 'netfilter.sockets': { + colors: '#88AA00', + heads: [ + netdataDashboard.gaugeChart('Active Connections', '12%', '', '#88AA00') + ] + }, + + 'netfilter.new': { + heads: [ + netdataDashboard.gaugeChart('New Connections', '12%', 'new', '#5555AA') + ] + }, + + // ------------------------------------------------------------------------ + // DISKS + + 'disk.util': { + colors: '#FF5588', + heads: [ + netdataDashboard.gaugeChart('Utilization', '12%', '', '#FF5588') + ], + info: 'Disk Utilization measures the amount of time the disk was busy with something. This is not related to its performance. 100% means that the Linux kernel always had an outstanding operation on the disk. Keep in mind that depending on the underlying technology of the disk, 100% here may or may not be an indication of congestion.' + }, + + 'disk.backlog': { + colors: '#0099CC', + info: 'Backlog is an indication of the duration of pending disk operations. On every I/O event the Linux kernel is multiplying the time spent doing I/O since the last update of this field with the number of pending operations. While not accurate, this metric can provide an indication of the expected completion time of the operations in progress.' + }, + + 'disk.io': { + heads: [ + netdataDashboard.gaugeChart('Read', '12%', 'reads'), + netdataDashboard.gaugeChart('Write', '12%', 'writes') + ], + info: 'Amount of data transferred to and from disk.' + }, + + 'disk.ops': { + info: 'Completed disk I/O operations. Keep in mind the number of operations requested might be higher, since the Linux kernel is able to merge adjacent to each other (see merged operations chart).' + }, + + 'disk.qops': { + info: 'I/O operations currently in progress. This metric is a snapshot - it is not an average over the last interval.' + }, + + 'disk.iotime': { + height: 0.5, + info: 'The sum of the duration of all completed I/O operations. This number can exceed the interval if the disk is able to execute I/O operations in parallel.' + }, + 'disk.mops': { + height: 0.5, + info: 'The number of merged disk operations. The Linux kernel is able to merge adjacent I/O operations, for example two 4KB reads can become one 8KB read before given to disk.' + }, + 'disk.svctm': { + height: 0.5, + info: 'The average service time for completed I/O operations. This metric is calculated using the total busy time of the disk and the number of completed operations. If the disk is able to execute multiple parallel operations the reporting average service time will be misleading.' + }, + 'disk.avgsz': { + height: 0.5, + info: 'The average I/O operation size.' + }, + 'disk.await': { + height: 0.5, + info: 'The average time for I/O requests issued to the device to be served. This includes the time spent by the requests in queue and the time spent servicing them.' + }, + + 'disk.space': { + info: 'Disk space utilization. reserved for root is automatically reserved by the system to prevent the root user from getting out of space.' + }, + 'disk.inodes': { + info: 'inodes (or index nodes) are filesystem objects (e.g. files and directories). On many types of file system implementations, the maximum number of inodes is fixed at filesystem creation, limiting the maximum number of files the filesystem can hold. It is possible for a device to run out of inodes. When this happens, new files cannot be created on the device, even though there may be free space available.' + }, + + 'mysql.net': { + info: 'The amount of data sent to mysql clients (<strong>out</strong>) and received from mysql clients (<strong>in</strong>).' + }, + + // ------------------------------------------------------------------------ + // MYSQL + + 'mysql.queries': { + info: 'The number of statements executed by the server.<ul>' + + '<li><strong>queries</strong> counts the statements executed within stored SQL programs.</li>' + + '<li><strong>questions</strong> counts the statements sent to the mysql server by mysql clients.</li>' + + '<li><strong>slow queries</strong> counts the number of statements that took more than <a href="http://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html#sysvar_long_query_time" target="_blank">long_query_time</a> seconds to be executed.' + + ' For more information about slow queries check the mysql <a href="http://dev.mysql.com/doc/refman/5.7/en/slow-query-log.html" target="_blank">slow query log</a>.</li>' + + '</ul>' + }, + + 'mysql.handlers': { + info: 'Usage of the internal handlers of mysql. This chart provides very good insights of what the mysql server is actually doing.' + + ' (if the chart is not showing all these dimensions it is because they are zero - set <strong>Which dimensions to show?</strong> to <strong>All</strong> from the dashboard settings, to render even the zero values)<ul>' + + '<li><strong>commit</strong>, the number of internal <a href="http://dev.mysql.com/doc/refman/5.7/en/commit.html" target="_blank">COMMIT</a> statements.</li>' + + '<li><strong>delete</strong>, the number of times that rows have been deleted from tables.</li>' + + '<li><strong>prepare</strong>, a counter for the prepare phase of two-phase commit operations.</li>' + + '<li><strong>read first</strong>, the number of times the first entry in an index was read. A high value suggests that the server is doing a lot of full index scans; e.g. <strong>SELECT col1 FROM foo</strong>, with col1 indexed.</li>' + + '<li><strong>read key</strong>, the number of requests to read a row based on a key. If this value is high, it is a good indication that your tables are properly indexed for your queries.</li>' + + '<li><strong>read next</strong>, the number of requests to read the next row in key order. This value is incremented if you are querying an index column with a range constraint or if you are doing an index scan.</li>' + + '<li><strong>read prev</strong>, the number of requests to read the previous row in key order. This read method is mainly used to optimize <strong>ORDER BY ... DESC</strong>.</li>' + + '<li><strong>read rnd</strong>, the number of requests to read a row based on a fixed position. A high value indicates you are doing a lot of queries that require sorting of the result. You probably have a lot of queries that require MySQL to scan entire tables or you have joins that do not use keys properly.</li>' + + '<li><strong>read rnd next</strong>, the number of requests to read the next row in the data file. This value is high if you are doing a lot of table scans. Generally this suggests that your tables are not properly indexed or that your queries are not written to take advantage of the indexes you have.</li>' + + '<li><strong>rollback</strong>, the number of requests for a storage engine to perform a rollback operation.</li>' + + '<li><strong>savepoint</strong>, the number of requests for a storage engine to place a savepoint.</li>' + + '<li><strong>savepoint rollback</strong>, the number of requests for a storage engine to roll back to a savepoint.</li>' + + '<li><strong>update</strong>, the number of requests to update a row in a table.</li>' + + '<li><strong>write</strong>, the number of requests to insert a row in a table.</li>' + + '</ul>' + }, + + 'mysql.table_locks': { + info: 'MySQL table locks counters: <ul>' + + '<li><strong>immediate</strong>, the number of times that a request for a table lock could be granted immediately.</li>' + + '<li><strong>waited</strong>, the number of times that a request for a table lock could not be granted immediately and a wait was needed. If this is high and you have performance problems, you should first optimize your queries, and then either split your table or tables or use replication.</li>' + + '</ul>' + }, + + // ------------------------------------------------------------------------ + // APACHE + + 'apache.connections': { + colors: NETDATA.colors[4], + mainheads: [ + netdataDashboard.gaugeChart('Connections', '12%', '', NETDATA.colors[4]) + ] + }, + + 'apache.requests': { + colors: NETDATA.colors[0], + mainheads: [ + netdataDashboard.gaugeChart('Requests', '12%', '', NETDATA.colors[0]) + ] + }, + + 'apache.net': { + colors: NETDATA.colors[3], + mainheads: [ + netdataDashboard.gaugeChart('Bandwidth', '12%', '', NETDATA.colors[3]) + ] + }, + + 'apache.workers': { + mainheads: [ + function(id) { + return '<div data-netdata="' + id + '"' + + ' data-dimensions="busy"' + + ' data-append-options="percentage"' + + ' data-gauge-max-value="100"' + + ' data-chart-library="gauge"' + + ' data-title="Workers Utilization"' + + ' data-units="percentage %"' + + ' data-gauge-adjust="width"' + + ' data-width="12%"' + + ' data-before="0"' + + ' data-after="-CHART_DURATION"' + + ' data-points="CHART_DURATION"' + + ' role="application"></div>'; + } + ] + }, + + 'apache.bytesperreq': { + colors: NETDATA.colors[3], + height: 0.5 + }, + + 'apache.reqpersec': { + colors: NETDATA.colors[4], + height: 0.5 + }, + + 'apache.bytespersec': { + colors: NETDATA.colors[6], + height: 0.5 + }, + + + // ------------------------------------------------------------------------ + // NGINX + + 'nginx.connections': { + colors: NETDATA.colors[4], + mainheads: [ + netdataDashboard.gaugeChart('Connections', '12%', '', NETDATA.colors[4]) + ] + }, + + 'nginx.requests': { + colors: NETDATA.colors[0], + mainheads: [ + netdataDashboard.gaugeChart('Requests', '12%', '', NETDATA.colors[0]) + ] + }, + + // ------------------------------------------------------------------------ + // NETDATA + + 'netdata.response_time': { + info: 'The netdata API response time measures the time netdata needed to serve requests. This time includes everything, from the reception of the first byte of a request, to the dispatch of the last byte of its reply, therefore it includes all network latencies involved (i.e. a client over a slow network will influence these metrics).' + }, + + // ------------------------------------------------------------------------ + // RETROSHARE + + 'retroshare.bandwidth': { + info: 'RetroShare inbound and outbound traffic.', + mainheads: [ + netdataDashboard.gaugeChart('Received', '12%', 'bandwidth_down_kb'), + netdataDashboard.gaugeChart('Sent', '12%', 'bandwidth_up_kb') + ] + }, + + 'retroshare.peers': { + info: 'Number of (connected) RetroShare friends.', + mainheads: [ + function(id) { + return '<div data-netdata="' + id + '"' + + ' data-dimensions="peers_connected"' + + ' data-append-options="friends"' + + ' data-chart-library="easypiechart"' + + ' data-title="connected friends"' + + ' data-units=""' + + ' data-width="8%"' + + ' data-before="0"' + + ' data-after="-CHART_DURATION"' + + ' data-points="CHART_DURATION"' + + ' role="application"></div>'; + } + ] + }, + + 'retroshare.dht': { + info: 'Statistics about RetroShare\'s DHT. These values are estimated!' + }, + + // ------------------------------------------------------------------------ + // fping + + 'fping.quality': { + colors: NETDATA.colors[10], + height: 0.5 + }, + + 'fping.packets': { + height: 0.5 + } + +}; diff --git a/web/demo.html b/web/demo.html deleted file mode 100644 index b886e1c58..000000000 --- a/web/demo.html +++ /dev/null @@ -1,50 +0,0 @@ -<!DOCTYPE html> -<html lang="en"> -<head> - <title>NetData Dashboard</title> - <meta name="application-name" content="netdata"> - - <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> - <meta charset="utf-8"> - <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> - <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"> - <meta name="author" content="costa@tsaousis.gr"> - - <meta property="og:locale" content="en_US" /> - <meta property="og:image" content="https://my-netdata.io/images/seo-performance-512.png"/> - <meta property="og:url" content="http://my-netdata.io/"/> - <meta property="og:type" content="website"/> - <meta property="og:site_name" content="netdata"/> - <meta property="og:title" content="netdata - real-time performance monitoring, done right!"/> - <meta property="og:description" content="Stunning real-time dashboards, blazingly fast and extremely interactive. Zero configuration, zero dependencies, zero maintenance." /> -</head> -<script type="text/javascript" src="dashboard.js?v20161004-1"></script> -<body> - -<div style="width: 100%; text-align: center;"> - <div data-netdata="netdata.server_cpu" - data-dimensions="user" - data-chart-library="gauge" - data-width="150px" - data-after="-60" - data-points="60" - data-title="Yes! Realtime!" - data-units="I am alive!" - data-colors="#FF5555" - ></div> - <br/> - <div data-netdata="netdata.server_cpu" - data-dimensions="user" - data-chart-library="dygraph" - data-dygraph-theme="sparkline" - data-width="200px" - data-height="30px" - data-after="-60" - data-points="60" - data-colors="#FF5555" - ></div> -</div> -</body> -</html> diff --git a/web/demo2.html b/web/demo2.html deleted file mode 100644 index eeb99f75e..000000000 --- a/web/demo2.html +++ /dev/null @@ -1,142 +0,0 @@ -<!DOCTYPE html> -<html lang="en"> -<head> - <title>NetData Dashboard</title> - <meta name="application-name" content="netdata"> - - <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> - <meta charset="utf-8"> - <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> - <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"> - <meta name="author" content="costa@tsaousis.gr"> - - <meta property="og:locale" content="en_US" /> - <meta property="og:image" content="https://my-netdata.io/images/seo-performance-512.png"/> - <meta property="og:url" content="http://my-netdata.io/"/> - <meta property="og:type" content="website"/> - <meta property="og:site_name" content="netdata"/> - <meta property="og:title" content="netdata - real-time performance monitoring, done right!"/> - <meta property="og:description" content="Stunning real-time dashboards, blazingly fast and extremely interactive. Zero configuration, zero dependencies, zero maintenance." /> -</head> -<script>var netdataTheme = 'slate';</script> -<script type="text/javascript" src="http://my-netdata.io/dashboard.js?v20161004-1"></script> -<body> - -<div class="container" style="width: 90%; padding-top: 10px; text-align: center; color: #AAA"> - <div style="font-size: 7vw;">why netdata?</div> - <br/> - <div style="font-size: 2vw; color: white;">These charts visualize the same data...</div> - - - <!-- Nav tabs --> - <ul class="nav nav-tabs" role="tablist"> - <li role="presentation" class="active"><a href="#gauge" aria-controls="gauge" role="tab" data-toggle="tab">Gauge.js</a></li> - <li role="presentation"><a href="#easypiechart" aria-controls="easypiechart" role="tab" data-toggle="tab">Easy Pie Chart</a></li> - </ul> - - <!-- Tab panes --> - <div class="tab-content"> - <div role="tabpanel" class="tab-pane active" id="gauge"> - - <div style="display: inline-block; width: 35.8%"> - <div style="font-size: 1.2vw; color: #666; padding-top: 10px;"><i class="fa fa-comment"></i> I can trace an issue like this</div> - <br/> - <div data-netdata="example.random2" - data-dimensions="random" - data-chart-library="gauge" - data-gauge-max-value="32767" - data-width="100%" - data-after="-600" - data-points="600" - data-title="1/second (netdata default)" - data-units="important metric" - data-colors="#5A5" - ></div> - </div> - <div style="display: inline-block; width: 50%"> - <div style="font-size: 1.2vw; color: #666;"><i class="fa fa-comment"></i> Can you trace an issue like these?<br/> <br/></div> - <div data-netdata="example.random2" - data-dimensions="random" - data-chart-library="gauge" - data-gauge-max-value="32767" - data-width="45%" - data-after="-600" - data-points="60" - data-title="Updates Every 10 Sec" - data-units="important metric" - data-colors="#C55" - ></div> - <div data-netdata="example.random2" - data-dimensions="random" - data-chart-library="gauge" - data-gauge-max-value="32767" - data-width="45%" - data-after="-600" - data-points="2" - data-title="Updates Every 5 Mins" - data-units="important metric" - data-colors="#C55" - ></div> - </div> - </div> - <div role="tabpanel" class="tab-pane" id="easypiechart"> - - <div style="display: inline-block; width: 25%"> - <div style="font-size: 1.2vw; color: #666; padding-top: 10px;"><i class="fa fa-comment"></i> I can trace an issue like this</div> - <br/> - <div data-netdata="example.random2" - data-dimensions="random" - data-chart-library="easypiechart" - data-easypiechart-max-value="32767" - data-width="100%" - data-after="-600" - data-points="600" - data-title="1/second (netdata default)" - data-units="important metric" - data-colors="#5A5" - ></div> - </div> - <div style="display: inline-block; width: 40%"> - <div style="font-size: 1.2vw; color: #666;"><i class="fa fa-comment"></i> Can you trace an issue like these?<br/> <br/></div> - <div data-netdata="example.random2" - data-dimensions="random" - data-chart-library="easypiechart" - data-easypiechart-max-value="32767" - data-width="45%" - data-after="-600" - data-points="60" - data-title="Updates Every 10 Sec" - data-units="important metric" - data-colors="#C55" - ></div> - <div data-netdata="example.random2" - data-dimensions="random" - data-chart-library="easypiechart" - data-easypiechart-max-value="32767" - data-width="45%" - data-after="-600" - data-points="2" - data-title="Updates Every 5 Mins" - data-units="important metric" - data-colors="#C55" - ></div> - </div> - </div> - </div> - <div style="font-size: 1.5vw;">Hover on the chart below, to see the selected value on the charts above!</div> - <div data-netdata="example.random2" - data-dimensions="random" - data-dygraph-theme="sparkline" - data-width="100%" - data-height="20vh" - data-after="-600" - data-points="600" - data-title="1/second (netdata default)" - data-units="something" - data-colors="#888" - ></div> -</div> -</body> -</html> diff --git a/web/demosites.html b/web/demosites.html deleted file mode 100644 index b59c2a177..000000000 --- a/web/demosites.html +++ /dev/null @@ -1,1043 +0,0 @@ -<!DOCTYPE html> -<html lang="en"> -<head> - <title>NetData - Real-time performance monitoring, done right!</title> - <meta name="application-name" content="netdata"> - - <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> - <meta charset="utf-8"> - <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> - <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"> - - <meta property="og:locale" content="en_US" /> - <meta property="og:image" content="https://my-netdata.io/images/seo-performance-512.png"/> - <meta property="og:url" content="http://my-netdata.io/"/> - <meta property="og:type" content="website"/> - <meta property="og:site_name" content="netdata"/> - <meta property="og:title" content="netdata - real-time performance monitoring, done right!"/> - <meta property="og:description" content="Stunning real-time dashboards, blazingly fast and extremely interactive. Zero configuration, zero dependencies, zero maintenance." /> -</head> - -<script> - // --- OPTIONS FOR THE DASHBOARD -- - - // this section has to appear before loading dashboard.js - - // Select a theme. - // uncomment on of the two themes: - - // var netdataTheme = 'default'; // this is white - var netdataTheme = 'slate'; // this is dark - - - // Set the default netdata server. - // on charts without a 'data-host', this one will be used. - // the default is the server that dashboard.js is downloaded from. - - // var netdataServer = 'http://my.server:19999/'; - </script> - - <!-- - --- LOAD dashboard.js --- - - to host this HTML file on your web server, - you have to load dashboard.js from the netdata server. - - So, pick one the two below - If you pick the first, set the server name/IP. - - The second assumes you host this file on /usr/share/netdata/web - and that you have chown it to be owned by netdata:netdata - --> - <!-- <script type="text/javascript" src="http://my.server:19999/dashboard.js"></script> --> - <script type="text/javascript" src="dashboard.js?v20161004-1"></script> - - <script> - // --- OPTIONS FOR THE CHARTS -- - - // destroy charts not shown (lowers memory on the browsers) - // set this to 'true' to destroy, 'false' to hide the charts - NETDATA.options.current.destroy_on_hide = true; - - // set this to false, to always show all dimensions - NETDATA.options.current.eliminate_zero_dimensions = true; - - // set this to false, to lower the pressure on the browser - NETDATA.options.current.concurrent_refreshes = false; - - // if you need to support slow mobile phones, set this to false - NETDATA.options.current.parallel_refresher = true; - - // set this to false, to always update the charts, even if focus is lost - NETDATA.options.current.stop_updates_when_focus_is_lost = true; -</script> -<style> - body { - font-size: 1vw; - } - - .mysparkline { - position: relative; - display: inline-block; - min-height: 50px; - width: 100%; - height: 8vmax; - text-align: left; - } - - .mysparkline-overchart-label { - position: absolute; - display: block; - top: 0; - left: 10px; - bottom: 0; - right: 0; - font-size: 1vmax; - z-index: 1; - } - - .mysparkline-overchart-value { - position: absolute; - display: block; - top: 1.1vmax; - left: 10px; - bottom: 0; - right: 0; - font-size: 5vmax; - z-index: 2; - text-shadow: #333 0px 0px 2px; - } - - .myfullchart { - position: relative; - display: inline-block; - width: 100%; - height: 14vmax; - min-height: 150px; - text-align: left; - } - - .mygauge-combo { - display: inline-block; - } - - .mygauge { - position: relative; - display: block; - width: 18vw; - height: 11vw; - } - - .mygauge-button { - display: block; - } - - .mytitle { - padding-top: 6vw; - padding-bottom: 1vw; - text-align: center; - font-size: 2.4vw; - } - - .mysubtitle { - padding-top: 2vw; - padding-bottom: 1vw; - text-align: center; - font-size: 1.8vw; - } - - .mycontent { - text-align: center; - font-size: 1.5vw; - } - - @media only screen and (min-width : 992px) { - .container { - width: 80%; - } - } - @media only screen and (max-width : 992px) { - .container { - width: 100%; - } - } -</style> - -<body style="text-align: center; background-color: #272b30;"> - -<div class="container"> - - <div style="text-align: center; font-size: 13vw; height: 14vw;"> - <b>netdata</b> - </div> - <div style="text-align: center; font-size: 2vw; height: 2.5vw;"> - real-time performance monitoring - </div> - <div style="width:80%; text-align: right; font-size: 2.7vw;"> - <strong>scaled out</strong>! - </div> - <div class="mytitle"> - pick a <b>netdata</b> demo server - </div> - <div class="mycontent"> - these demo servers show what you will get by installing <b>netdata</b> - </div> - - <div style="width: 100%; text-align: center; padding-top: 2vw;"> - <div style="width: 100%; text-align: center;"> - - <div class="mygauge-combo"> - <div class="mygauge"> - <div data-netdata="netdata.requests" - data-host="//london.my-netdata.io" - data-title="EU - London" - data-chart-library="gauge" - data-width="100%" - data-after="-300" - data-points="300" - data-colors="#558855" - ></div> - </div> - <div class="mygauge-button"> - <br/> <br/> - <button type="button" class="btn btn-default" data-toggle="button" aria-pressed="false" autocomplete="off" onclick="window.location='//london.my-netdata.io/default.html'" style="font-size: 1.0vw;">Enter London!</button> - <div style="font-size: 0.8vw;"> - Donated by DigitalOcean.com - </div> - </div> - </div> - <div class="mygauge-combo"> - <div class="mygauge"> - <div data-netdata="netdata.requests" - data-host="//atlanta.my-netdata.io" - data-title="US - Atlanta" - data-chart-library="gauge" - data-width="100%" - data-after="-300" - data-points="300" - data-colors="#AA5555" - ></div> - </div> - <div class="mygauge-button"> - <br/> <br/> - <button type="button" class="btn btn-default" data-toggle="button" aria-pressed="false" autocomplete="off" onclick="window.location='//atlanta.my-netdata.io/default.html'" style="font-size: 1.0vw;">Enter Atlanta!</button> - <div style="font-size: 0.8vw;"> - Donated by CDN77.com - </div> - </div> - </div> - <div class="mygauge-combo"> - <div class="mygauge"> - <div data-netdata="netdata.requests" - data-host="//sanfrancisco.netdata.rocks" - data-title="US - California" - data-chart-library="gauge" - data-width="100%" - data-after="-300" - data-points="300" - data-colors="#5555AA" - ></div> - </div> - <div class="mygauge-button"> - <br/> <br/> - <button type="button" class="btn btn-default" data-toggle="button" aria-pressed="false" autocomplete="off" onclick="window.location='//sanfrancisco.netdata.rocks/default.html'" style="font-size: 1.0vw;">Enter California!</button> - <div style="font-size: 0.8vw;"> - Donated by DigitalOcean.com - </div> - </div> - </div> - <div class="mygauge-combo"> - <div class="mygauge"> - <div data-netdata="netdata.requests" - data-host="//toronto.netdata.rocks" - data-title="Canada" - data-chart-library="gauge" - data-width="100%" - data-after="-300" - data-points="300" - data-colors="#885588" - ></div> - </div> - <div class="mygauge-button"> - <br/> <br/> - <button type="button" class="btn btn-default" data-toggle="button" aria-pressed="false" autocomplete="off" onclick="window.location='//toronto.netdata.rocks/default.html'" style="font-size: 1.0vw;">Enter Canada!</button> - <div style="font-size: 0.8vw;"> - Donated by DigitalOcean.com - </div> - </div> - </div> - <br/> <br/> - <div class="mygauge-combo"> - <div class="mygauge"> - <div data-netdata="netdata.requests" - data-host="//frankfurt.netdata.rocks" - data-title="EU - Germany" - data-chart-library="easypiechart" - data-width="75%" - data-after="-300" - data-points="300" - data-colors="#AAAA55" - ></div> - </div> - <div class="mygauge-button"> - <br/> <br/> - <button type="button" class="btn btn-default" data-toggle="button" aria-pressed="false" autocomplete="off" onclick="window.location='//frankfurt.netdata.rocks/default.html'" style="font-size: 1.0vw;">Enter Germany!</button> - <div style="font-size: 0.8vw;"> - Donated by DigitalOcean.com - </div> - </div> - </div> - <div class="mygauge-combo"> - <div class="mygauge"> - <div data-netdata="netdata.requests" - data-host="//newyork.netdata.rocks" - data-title="US - New York" - data-chart-library="easypiechart" - data-width="75%" - data-after="-300" - data-points="300" - data-colors="#BB5533" - ></div> - </div> - <div class="mygauge-button"> - <br/> <br/> - <button type="button" class="btn btn-default" data-toggle="button" aria-pressed="false" autocomplete="off" onclick="window.location='//newyork.netdata.rocks/default.html'" style="font-size: 1.0vw;">Enter New York!</button> - <div style="font-size: 0.8vw;"> - Donated by DigitalOcean.com - </div> - </div> - </div> - <div class="mygauge-combo"> - <div class="mygauge"> - <div data-netdata="netdata.requests" - data-host="//singapore.netdata.rocks" - data-title="Signapore" - data-chart-library="easypiechart" - data-width="75%" - data-after="-300" - data-points="300" - data-colors="#5588BB" - ></div> - </div> - <div class="mygauge-button"> - <br/> <br/> - <button type="button" class="btn btn-default" data-toggle="button" aria-pressed="false" autocomplete="off" onclick="window.location='//singapore.netdata.rocks/default.html'" style="font-size: 1.0vw;">Enter Singapore!</button> - <div style="font-size: 0.8vw;"> - Donated by DigitalOcean.com - </div> - </div> - </div> - <div class="mygauge-combo"> - <div class="mygauge"> - <div data-netdata="netdata.requests" - data-host="//bangalore.netdata.rocks" - data-title="India" - data-chart-library="easypiechart" - data-width="75%" - data-after="-300" - data-points="300" - data-colors="#BB55BB" - ></div> - </div> - <div class="mygauge-button"> - <br/> <br/> - <button type="button" class="btn btn-default" data-toggle="button" aria-pressed="false" autocomplete="off" onclick="window.location='//bangalore.netdata.rocks/default.html'" style="font-size: 1.0vw;">Enter India!</button> - <div style="font-size: 0.8vw;"> - Donated by DigitalOcean.com - </div> - </div> - </div> - </div> - </div> - - <div class="mytitle"> - this page is a custom <b>netdata</b> dashboard - </div> - <div class="mycontent"> - charts are coming from 8 servers, in parallel - <br/> - the servers are not aware of this multi-server dashboard, - <br/> - each server is not aware of the other servers, - <br/> - but on this dashboard <b>they are one</b>! - </div> - <div style="padding-top: 1vw; width: 100%; text-align: center; font-size: 1.5vw;"> - <i class="fa fa-comment" aria-hidden="true"></i> - hover on a chart below, or drag it to show the past - <b>the others will follow</b>! - <br/> - double click on a chart to reset them all - </div> - - <div class="mytitle"> - our <code>nginx</code> performance - </div> - <div class="mycontent"> - (we proxy netdata through nginx, on the demo sites) - </div> - - <!-- Nav tabs --> - <ul class="nav nav-tabs" role="tablist" style="padding-top: 1vw;"> - <li role="presentation" class="active"><a href="#nginx_requests" aria-controls="nginx_requests" role="tab" data-toggle="tab">Requests</a></li> - <li role="presentation"><a href="#nginx_connections" aria-controls="nginx_connections" role="tab" data-toggle="tab">Connections</a></li> - </ul> - - <!-- Tab panes --> - <div class="tab-content"> - <div role="tabpanel" class="tab-pane active" id="nginx_requests"> - <div class="mysparkline"> - <div class="mysparkline-overchart-label"> - <b>EU - London</b> web requests/s - </div> - <div class="mysparkline-overchart-value" id="nginx_local.requests.netdata" > - </div> - <div data-netdata="nginx_local.requests" - data-dimensions="requests" - data-host="//london.my-netdata.io" - data-chart-library="dygraph" - data-dygraph-theme="sparkline" - data-dygraph-type="area" - data-width="100%" - data-height="100%" - data-after="-300" - data-colors="#558855" - data-show-value-of-requests-at="nginx_local.requests.netdata" - ></div> - </div> - - <div class="mysparkline"> - <div class="mysparkline-overchart-label"> - <b>US - Atlanta</b> web requests/s - </div> - <div class="mysparkline-overchart-value" id="nginx_local.requests.netdata2" > - </div> - <div data-netdata="nginx_local.requests" - data-dimensions="requests" - data-host="//atlanta.my-netdata.io" - data-chart-library="dygraph" - data-dygraph-theme="sparkline" - data-dygraph-type="area" - data-width="100%" - data-height="100%" - data-after="-300" - data-colors="#AA5555" - data-show-value-of-requests-at="nginx_local.requests.netdata2" - ></div> - </div> - - <div class="mysparkline"> - <div class="mysparkline-overchart-label"> - <b>US - California</b> web requests/s - </div> - <div class="mysparkline-overchart-value" id="nginx_local.requests.netdata3" > - </div> - <div data-netdata="nginx_local.requests" - data-dimensions="requests" - data-host="//sanfrancisco.netdata.rocks" - data-chart-library="dygraph" - data-dygraph-theme="sparkline" - data-dygraph-type="area" - data-width="100%" - data-height="100%" - data-after="-300" - data-colors="#5555AA" - data-show-value-of-requests-at="nginx_local.requests.netdata3" - ></div> - </div> - - <div class="mysparkline"> - <div class="mysparkline-overchart-label"> - <b>Canada</b> web requests/s - </div> - <div class="mysparkline-overchart-value" id="nginx_local.requests.netdata4" > - </div> - <div data-netdata="nginx_local.requests" - data-dimensions="requests" - data-host="//toronto.netdata.rocks" - data-chart-library="dygraph" - data-dygraph-theme="sparkline" - data-dygraph-type="area" - data-width="100%" - data-height="100%" - data-after="-300" - data-colors="#885588" - data-show-value-of-requests-at="nginx_local.requests.netdata4" - ></div> - </div> - </div> - - <div role="tabpanel" class="tab-pane" id="nginx_connections"> - <div class="mysparkline"> - <div class="mysparkline-overchart-label"> - <b>EU - London</b> active connections - </div> - <div class="mysparkline-overchart-value" id="nginx_local.connections.netdata1" > - </div> - <div data-netdata="nginx_local.connections" - data-dimensions="active" - data-host="//london.my-netdata.io" - data-chart-library="dygraph" - data-dygraph-theme="sparkline" - data-dygraph-type="area" - data-width="100%" - data-height="100%" - data-after="-300" - data-colors="#558855" - data-show-value-of-active-at="nginx_local.connections.netdata1" - ></div> - </div> - - <div class="mysparkline"> - <div class="mysparkline-overchart-label"> - <b>US - Atlanta</b> active connections - </div> - <div class="mysparkline-overchart-value" id="nginx_local.connections.netdata2" > - </div> - <div data-netdata="nginx_local.connections" - data-dimensions="active" - data-host="//atlanta.my-netdata.io" - data-chart-library="dygraph" - data-dygraph-theme="sparkline" - data-dygraph-type="area" - data-width="100%" - data-height="100%" - data-after="-300" - data-colors="#AA5555" - data-show-value-of-active-at="nginx_local.connections.netdata2" - ></div> - </div> - - <div class="mysparkline"> - <div class="mysparkline-overchart-label"> - <b>US - California</b> active connections - </div> - <div class="mysparkline-overchart-value" id="nginx_local.connections.netdata3" > - </div> - <div data-netdata="nginx_local.connections" - data-dimensions="active" - data-host="//sanfrancisco.netdata.rocks" - data-chart-library="dygraph" - data-dygraph-theme="sparkline" - data-dygraph-type="area" - data-width="100%" - data-height="100%" - data-after="-300" - data-colors="#5555AA" - data-show-value-of-active-at="nginx_local.connections.netdata3" - ></div> - </div> - - <div class="mysparkline"> - <div class="mysparkline-overchart-label"> - <b>Canada</b> active connections - </div> - <div class="mysparkline-overchart-value" id="nginx_local.connections.netdata4" > - </div> - <div data-netdata="nginx_local.connections" - data-dimensions="active" - data-host="//toronto.netdata.rocks" - data-chart-library="dygraph" - data-dygraph-theme="sparkline" - data-dygraph-type="area" - data-width="100%" - data-height="100%" - data-after="-300" - data-colors="#885588" - data-show-value-of-active-at="nginx_local.connections.netdata4" - ></div> - </div> - </div> - </div> - - <div style="width: 100%; text-align: right; font-size: 1vw;"> - <i class="fa fa-comment" aria-hidden="true"></i> these charts are draggable and touchable, double click them to reset them - </div> - - - <div class="mytitle"> - bandwidth consumption on the demo sites - </div> - <div class="mycontent"> - Linux QoS is configured by <a href="https://github.com/firehol/netdata/wiki/You-should-install-QoS-on-all-your-servers">FireQOS</a> - </div> - - <!-- Nav tabs --> - <ul class="nav nav-tabs" role="tablist" style="padding-top: 1vw;"> - <li role="presentation" class="active"><a href="#outbout" aria-controls="outbout" role="tab" data-toggle="tab">Outbound</a></li> - <li role="presentation"><a href="#inbound" aria-controls="inbound" role="tab" data-toggle="tab">Inbound</a></li> - </ul> - - <!-- Tab panes --> - <div class="tab-content"> - <div role="tabpanel" class="tab-pane active" id="outbout"> - <div class="myfullchart"> - <div data-netdata="tc.world_out" - data-host="//london.my-netdata.io" - data-chart-library="dygraph" - data-title="EU - London, traffic we send per service" - data-width="100%" - data-height="100%" - data-after="-300" - ></div> - </div> - - <div class="myfullchart"> - <div data-netdata="tc.world_out" - data-host="//atlanta.my-netdata.io" - data-chart-library="dygraph" - data-title="US - Atlanta, traffic we send per service" - data-width="100%" - data-height="100%" - data-after="-300" - ></div> - - </div> - - <div class="myfullchart"> - <div data-netdata="tc.world_out" - data-host="//sanfrancisco.netdata.rocks" - data-chart-library="dygraph" - data-title="US - California, traffic we send per service" - data-width="100%" - data-height="100%" - data-after="-300" - ></div> - </div> - - <div class="myfullchart"> - <div data-netdata="tc.world_out" - data-host="//toronto.netdata.rocks" - data-chart-library="dygraph" - data-title="Canada, traffic we send per service" - data-width="100%" - data-height="100%" - data-after="-300" - ></div> - </div> - </div> - - <div role="tabpanel" class="tab-pane" id="inbound"> - <div class="myfullchart"> - <div data-netdata="tc.world_in" - data-host="//london.my-netdata.io" - data-chart-library="dygraph" - data-title="EU - London, traffic we receive per service" - data-width="100%" - data-height="100%" - data-after="-300" - ></div> - - </div> - - <div class="myfullchart"> - <div data-netdata="tc.world_in" - data-host="//atlanta.my-netdata.io" - data-chart-library="dygraph" - data-title="US - Atlanta, traffic we receive per service" - data-width="100%" - data-height="100%" - data-after="-300" - ></div> - - </div> - - <div class="myfullchart"> - <div data-netdata="tc.world_in" - data-host="//sanfrancisco.netdata.rocks" - data-chart-library="dygraph" - data-title="US - California, traffic we receive per service" - data-width="100%" - data-height="100%" - data-after="-300" - ></div> - </div> - - <div class="myfullchart"> - <div data-netdata="tc.world_in" - data-host="//toronto.netdata.rocks" - data-chart-library="dygraph" - data-title="Canada, traffic we receive per service" - data-width="100%" - data-height="100%" - data-after="-300" - ></div> - </div> - </div> - </div> - <div style="width: 100%; text-align: right; font-size: 1vw;"> - <i class="fa fa-comment" aria-hidden="true"></i> <i>these legends are interactive and the charts are resizable here ^^^</i> - </div> - - <div class="mytitle"> - DDoS protection performance on the demo sites - </div> - <div class="mycontent"> - iptables SYNPROXY configured by <a href="https://github.com/firehol/netdata/wiki/Monitoring-SYNPROXY">FireHOL</a> - </div> - - <div style="padding-top: 4vw; width: 100%; text-align: center; font-size: 1.5vw;"> - - <div class="mysparkline"> - <div class="mysparkline-overchart-label"> - <b>EU - London</b>, TCP SYN packets/s received - </div> - <div class="mysparkline-overchart-value" id="netfilter.synproxy_syn_received.netdata1" > - </div> - <div data-netdata="netfilter.synproxy_syn_received" - data-dimensions="received" - data-host="//london.my-netdata.io" - data-chart-library="dygraph" - data-dygraph-theme="sparkline" - data-dygraph-type="area" - data-width="100%" - data-height="100%" - data-after="-300" - data-colors="#558855" - data-show-value-of-received-at="netfilter.synproxy_syn_received.netdata1" - ></div> - </div> - - <div class="mysparkline"> - <div class="mysparkline-overchart-label"> - <b>US - Atlanta</b>, TCP SYN packets/s received - </div> - <div class="mysparkline-overchart-value" id="netfilter.synproxy_syn_received.netdata2" > - </div> - <div data-netdata="netfilter.synproxy_syn_received" - data-dimensions="received" - data-host="//atlanta.my-netdata.io" - data-chart-library="dygraph" - data-dygraph-theme="sparkline" - data-dygraph-type="area" - data-width="100%" - data-height="100%" - data-after="-300" - data-colors="#885555" - data-show-value-of-received-at="netfilter.synproxy_syn_received.netdata2" - ></div> - </div> - - <div class="mysparkline"> - <div class="mysparkline-overchart-label"> - <b>US - California</b>, TCP SYN packets/s received - </div> - <div class="mysparkline-overchart-value" id="netfilter.synproxy_syn_received.netdata3" > - </div> - <div data-netdata="netfilter.synproxy_syn_received" - data-dimensions="received" - data-host="//sanfrancisco.netdata.rocks" - data-chart-library="dygraph" - data-dygraph-theme="sparkline" - data-dygraph-type="area" - data-width="100%" - data-height="100%" - data-after="-300" - data-colors="#555588" - data-show-value-of-received-at="netfilter.synproxy_syn_received.netdata3" - ></div> - </div> - - <div class="mysparkline"> - <div class="mysparkline-overchart-label"> - <b>Canada</b>, TCP SYN packets/s received - </div> - <div class="mysparkline-overchart-value" id="netfilter.synproxy_syn_received.netdata4" > - </div> - <div data-netdata="netfilter.synproxy_syn_received" - data-dimensions="received" - data-host="//toronto.netdata.rocks" - data-chart-library="dygraph" - data-dygraph-theme="sparkline" - data-dygraph-type="area" - data-width="100%" - data-height="100%" - data-after="-300" - data-colors="#885588" - data-show-value-of-received-at="netfilter.synproxy_syn_received.netdata4" - ></div> - </div> - </div> - <div style="width: 100%; text-align: right; font-size: 1vw;"> - <i class="fa fa-comment" aria-hidden="true"></i> <i>did you notice the decimal numbers? - <br/>netdata interpolates collected values at second boundaries, with nanosecond detail!</i> - </div> - - - <div class="mytitle"> - CPU Utilization of the demo sites - </div> - - <div style="padding-top: 1vw;"> - <div class="myfullchart"> - <div data-netdata="system.cpu" - data-host="//london.my-netdata.io" - data-chart-library="dygraph" - data-title="EU - London, CPU Usage" - data-width="100%" - data-height="100%" - data-after="-300" - data-dygraph-valuerange="[0, 100]" - ></div> - </div> - - <div class="myfullchart"> - <div data-netdata="system.cpu" - data-host="//atlanta.my-netdata.io" - data-chart-library="dygraph" - data-title="US - Atlanta, CPU Usage" - data-width="100%" - data-height="100%" - data-after="-300" - data-dygraph-valuerange="[0, 100]" - ></div> - </div> - - <div class="myfullchart"> - <div data-netdata="system.cpu" - data-host="//sanfrancisco.netdata.rocks" - data-chart-library="dygraph" - data-title="US - California, CPU Usage" - data-width="100%" - data-height="100%" - data-after="-300" - data-dygraph-valuerange="[0, 100]" - ></div> - </div> - - <div class="myfullchart"> - <div data-netdata="system.cpu" - data-host="//toronto.netdata.rocks" - data-chart-library="dygraph" - data-title="Canada, CPU Usage" - data-width="100%" - data-height="100%" - data-after="-300" - data-dygraph-valuerange="[0, 100]" - ></div> - </div> - </div> - <div style="width: 100%; text-align: right; font-size: 1vw;"> - <i class="fa fa-comment" aria-hidden="true"></i> <i>what is using so much CPU? - <br/>The site <a href="//iplists.firehol.org/">iplists.firehol.org</a> is maintained by FireHOL - the CPU is used for comparing security IP Lists.</i> - </div> - - <div class="mytitle"> - Netdata performance - </div> - <div class="mycontent"> - netdata monitors <b>users</b>, <b>user groups</b>, <b>applications (process trees)</b> - <br/> - <b>containers</b> (<code>lxc</code>, <code>docker</code>, etc.) and SNMP devices. - </div> - - <!-- Nav tabs --> - <ul class="nav nav-tabs" role="tablist" style="padding-top: 1vw;"> - <li role="presentation" class="active"><a href="#netdata_cpu" aria-controls="netdata_cpu" role="tab" data-toggle="tab">CPU</a></li> - <li role="presentation"><a href="#netdata_avgtime" aria-controls="netdata_avgtime" role="tab" data-toggle="tab">Average Response Time</a></li> - </ul> - - <!-- Tab panes --> - <div class="tab-content"> - <div role="tabpanel" class="tab-pane active" id="netdata_cpu"> - <div class="mysparkline"> - <div class="mysparkline-overchart-label"> - <b>EU - London</b>, CPU % of a single core - </div> - <div class="mysparkline-overchart-value" id="users.cpu.netdata1" > - </div> - <div data-netdata="apps.cpu" - data-dimensions="netdata" - data-host="//london.my-netdata.io" - data-chart-library="dygraph" - data-dygraph-theme="sparkline" - data-dygraph-type="area" - data-width="100%" - data-height="100%" - data-after="-300" - data-colors="#558855" - data-show-value-of-netdata-at="users.cpu.netdata1" - ></div> - </div> - - <div class="mysparkline"> - <div class="mysparkline-overchart-label"> - <b>US - Atlanta</b>, CPU % of a single core - </div> - <div class="mysparkline-overchart-value" id="users.cpu.netdata2" > - </div> - <div data-netdata="apps.cpu" - data-dimensions="netdata" - data-host="//atlanta.my-netdata.io" - data-chart-library="dygraph" - data-dygraph-theme="sparkline" - data-dygraph-type="area" - data-width="100%" - data-height="100%" - data-after="-300" - data-colors="#885555" - data-show-value-of-netdata-at="users.cpu.netdata2" - ></div> - </div> - - <div class="mysparkline"> - <div class="mysparkline-overchart-label"> - <b>US - California</b>, CPU % of a single core - </div> - <div class="mysparkline-overchart-value" id="users.cpu.netdata3" > - </div> - <div data-netdata="apps.cpu" - data-dimensions="netdata" - data-host="//sanfrancisco.netdata.rocks" - data-chart-library="dygraph" - data-dygraph-theme="sparkline" - data-dygraph-type="area" - data-width="100%" - data-height="100%" - data-after="-300" - data-colors="#555588" - data-show-value-of-netdata-at="users.cpu.netdata3" - ></div> - </div> - - <div class="mysparkline"> - <div class="mysparkline-overchart-label"> - <b>Toronto</b>, CPU % of a single core - </div> - <div class="mysparkline-overchart-value" id="users.cpu.netdata4" > - </div> - <div data-netdata="apps.cpu" - data-dimensions="netdata" - data-host="//toronto.netdata.rocks" - data-chart-library="dygraph" - data-dygraph-theme="sparkline" - data-dygraph-type="area" - data-width="100%" - data-height="100%" - data-after="-300" - data-colors="#885588" - data-show-value-of-netdata-at="users.cpu.netdata4" - ></div> - </div> - - <div style="width: 100%; text-align: right; font-size: 1vw;"> - <i class="fa fa-comment" aria-hidden="true"></i> <i>this utilization is about the whole netdata process tree and the percentage is of <b>a single core</b>! - <br/>including <b>BASH</b> plugins (it monitors <code>mysql</code> on the demo sites), <b>node.js</b> plugins (it monitors <code>bind9</code> on the demo sites), etc. - <br/>and including the chart refreshes for the dashboards of all viewers.</i> - </div> - </div> - - <div role="tabpanel" class="tab-pane" id="netdata_avgtime"> - <div class="mysparkline"> - <div class="mysparkline-overchart-label"> - <b>EU - London</b>, API average response time in milliseconds - </div> - <div class="mysparkline-overchart-value" id="netdata.response_time1" > - </div> - <div data-netdata="netdata.response_time" - data-host="//london.my-netdata.io" - data-chart-library="dygraph" - data-dygraph-theme="sparkline" - data-dygraph-type="area" - data-width="100%" - data-height="100%" - data-after="-300" - data-colors="#558855 #356835" - data-show-value-of-average-at="netdata.response_time1" - ></div> - </div> - - <div class="mysparkline"> - <div class="mysparkline-overchart-label"> - <b>US - Atlanta</b>, API average response time in milliseconds - </div> - <div class="mysparkline-overchart-value" id="netdata.response_time2" > - </div> - <div data-netdata="netdata.response_time" - data-host="//atlanta.my-netdata.io" - data-chart-library="dygraph" - data-dygraph-theme="sparkline" - data-dygraph-type="area" - data-width="100%" - data-height="100%" - data-after="-300" - data-colors="#885555 #683535" - data-show-value-of-average-at="netdata.response_time2" - ></div> - </div> - - <div class="mysparkline"> - <div class="mysparkline-overchart-label"> - <b>US - California</b>, API average response time in milliseconds - </div> - <div class="mysparkline-overchart-value" id="netdata.response_time3" > - </div> - <div data-netdata="netdata.response_time" - data-host="//sanfrancisco.netdata.rocks" - data-chart-library="dygraph" - data-dygraph-theme="sparkline" - data-dygraph-type="area" - data-width="100%" - data-height="100%" - data-after="-300" - data-colors="#555588 #353568" - data-show-value-of-average-at="netdata.response_time3" - ></div> - </div> - - <div class="mysparkline"> - <div class="mysparkline-overchart-label"> - <b>Canada</b>, API average response time in milliseconds - </div> - <div class="mysparkline-overchart-value" id="netdata.response_time4" > - </div> - <div data-netdata="netdata.response_time" - data-host="//toronto.netdata.rocks" - data-chart-library="dygraph" - data-dygraph-theme="sparkline" - data-dygraph-type="area" - data-width="100%" - data-height="100%" - data-after="-300" - data-colors="#885588 #683568" - data-show-value-of-average-at="netdata.response_time4" - ></div> - </div> - - <div style="width: 100%; text-align: right; font-size: 1vw;"> - <i class="fa fa-comment" aria-hidden="true"></i> <i>netdata is really <b>fast</b> (the values are milliseconds!) - <br/> - These values include everything, from the reception of the first byte to the dispatch of the last, including gzip compression. - <br/> - Values above 2-3ms are usually chart refreshes of charts with several dimensions, charts with very long durations (zoomed out), or file transfers. - </i> - </div> - </div> - </div> - - <div style="padding-top: 6vw; width: 100%; text-align: center; font-size: 2vw;"> - want to know more? - <br/> - jump to <a href="https://github.com/firehol/netdata/">the netdata page at github</a> - <br/> - it needs just 3 mins to be installed on your servers! - <br/> - - </div> -</div> -</body> -<script> - // google analytics when this is used for the home page of the demo sites - // you don't need this if you customize this dashboard for your needs - setTimeout(function() { - (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ - (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), - m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) - })(window,document,'script','https://www.google-analytics.com/analytics.js','ga'); - - ga('create', 'UA-64295674-3', 'auto'); - ga('send', 'pageview'); - }, 2000); -</script> -</html> diff --git a/web/goto-host-from-alarm.html b/web/goto-host-from-alarm.html index fab3ea589..16ed2b92a 100644 --- a/web/goto-host-from-alarm.html +++ b/web/goto-host-from-alarm.html @@ -18,7 +18,7 @@ var netdataTheme = 'slate'; var netdataShowHelp = true; </script> -<script type="text/javascript" src="dashboard.js?v20161004-1"></script> +<script type="text/javascript" src="dashboard.js?v20170105-7"></script> <script> var urlOptions = { diff --git a/web/images/alert-128-orange.png b/web/images/alert-128-orange.png Binary files differindex 86fb89fef..c6182bfad 100644 --- a/web/images/alert-128-orange.png +++ b/web/images/alert-128-orange.png diff --git a/web/images/alert-128-red.png b/web/images/alert-128-red.png Binary files differindex a9b0167bd..90b9c73e6 100644 --- a/web/images/alert-128-red.png +++ b/web/images/alert-128-red.png diff --git a/web/images/check-mark-2-128-green.png b/web/images/check-mark-2-128-green.png Binary files differindex 0c85424cf..e04ddca12 100644 --- a/web/images/check-mark-2-128-green.png +++ b/web/images/check-mark-2-128-green.png diff --git a/web/images/post.png b/web/images/post.png Binary files differindex 239af429a..6bad54742 100644 --- a/web/images/post.png +++ b/web/images/post.png diff --git a/web/images/seo-performance-114.png b/web/images/seo-performance-114.png Binary files differindex 894270793..3f3862b3b 100644 --- a/web/images/seo-performance-114.png +++ b/web/images/seo-performance-114.png diff --git a/web/images/seo-performance-128.png b/web/images/seo-performance-128.png Binary files differindex c91a39b58..2a212a475 100644 --- a/web/images/seo-performance-128.png +++ b/web/images/seo-performance-128.png diff --git a/web/images/seo-performance-16.png b/web/images/seo-performance-16.png Binary files differindex 4bafb3c27..6d7f075ec 100644 --- a/web/images/seo-performance-16.png +++ b/web/images/seo-performance-16.png diff --git a/web/images/seo-performance-24.png b/web/images/seo-performance-24.png Binary files differindex 1a03f394c..32d077ef1 100644 --- a/web/images/seo-performance-24.png +++ b/web/images/seo-performance-24.png diff --git a/web/images/seo-performance-256.png b/web/images/seo-performance-256.png Binary files differindex 380c4317a..07abfa01c 100644 --- a/web/images/seo-performance-256.png +++ b/web/images/seo-performance-256.png diff --git a/web/images/seo-performance-32.png b/web/images/seo-performance-32.png Binary files differindex 407b34076..a39543cfb 100644 --- a/web/images/seo-performance-32.png +++ b/web/images/seo-performance-32.png diff --git a/web/images/seo-performance-48.png b/web/images/seo-performance-48.png Binary files differindex dd2149920..6dab89e92 100644 --- a/web/images/seo-performance-48.png +++ b/web/images/seo-performance-48.png diff --git a/web/images/seo-performance-512.png b/web/images/seo-performance-512.png Binary files differindex b51648027..1f8c16410 100644 --- a/web/images/seo-performance-512.png +++ b/web/images/seo-performance-512.png diff --git a/web/images/seo-performance-64.png b/web/images/seo-performance-64.png Binary files differindex 36f8af4ca..e79f3b35b 100644 --- a/web/images/seo-performance-64.png +++ b/web/images/seo-performance-64.png diff --git a/web/images/seo-performance-72.png b/web/images/seo-performance-72.png Binary files differindex e168fde06..a4c9efb30 100644 --- a/web/images/seo-performance-72.png +++ b/web/images/seo-performance-72.png diff --git a/web/index.html b/web/index.html index e305cf6fa..d8e128234 100644 --- a/web/index.html +++ b/web/index.html @@ -29,13 +29,21 @@ <link rel="icon" type="image/png" sizes="32x32" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAACNklEQVRYhcXXv2tUQRAH8M+FEIJISBHCIWIhIQSUILERi4AiiqCggiIiomAjlhaC4j+ghYWISgqNohZaCBZBC8Ei8QdEUCutFBsxCBqDYkgci/cunkfuJffjJQPD8mZm5/vd2WV2HzlJ0Bs8CvrywsgCHwy+BpGOg0sJfjj4nYKX9FdwKG9gwZlgtgK8pLOpPxfw1mCoCnClDgWtzQTvCEYWCV7SkWAlFBoEb8dlDKBF8t2bMWUSH/AHr3CiEfz5CPUusPJLkRCdk5ZqyeqUrQv4R7E5TwK7M3zTeIKduRAIitiWEfIY69GdCwGcRFuG/xqONRkzkaA7+J5x+MaDtWmHvJ4HgeEM8Nn0bridfv9HoOFyBAdwJCPkqqTzHWwUaz7wgeBHxupfBKuCj2W25mxBsCGYyAB/FxTT27HcPlyep64tCLbjKbqqhLzBlgKfF8pVE4FgRXABI+ioEnYfOyzcFWsCbg+OV+xlpU4ER4O+4HVwL51b3xYEXcGu4Ao+YQhr5gmdxHmsQyfG0b/YxbWmLfRWmnxa0s06VbTMCpnBS9zFzQKTwR5cXCzwHIE02Sl8wSZsRI/kgLVJqjSd+t9LVjiG1diPszhdK3A5gR48k5zYMTwscC59sfT799CYKvA8EttbSeXgTr3gJQKl91kR+yTlvyG5uUbLYh9gb+ovltkb6qYtNSRo3kOygsBSzGlKsubf43USWLYK5CLLXoFWyU/CtzLbVDpW2n+m40yN9ukqdvAX9ac/EIgOapcAAAAASUVORK5CYII="> - <meta property="og:locale" content="en_US" /> - <meta property="og:image" content="http://my-netdata.io/images/post.png"/> - <meta property="og:url" content="http://my-netdata.io/"/> - <meta property="og:type" content="website"/> - <meta property="og:site_name" content="netdata"/> - <meta property="og:title" content="netdata - real-time performance monitoring, done right!"/> - <meta property="og:description" content="Stunning real-time dashboards, blazingly fast and extremely interactive. Zero configuration, zero dependencies, zero maintenance." /> + <meta property="og:locale" content="en_US" /> + <meta property="og:url" content="https://my-netdata.io" /> + <meta property="og:type" content="website" /> + <meta property="og:site_name" content="netdata"/> + <meta property="og:title" content="Get control of your Linux Servers. Simple. Effective. Awesome." /> + <meta property="og:description" content="Unparalleled insights, in real-time, of everything happening on your Linux systems and applications, with stunning, interactive web dashboards and powerful performance and health alarms." /> + <meta property="og:image" content="https://cloud.githubusercontent.com/assets/2662304/20910305/65d10354-bb69-11e6-8128-c44b547517b4.png" /> + <meta property="og:image:type" content="image/png" /> + <meta property="fb:app_id" content="1200089276712916" /> + + <meta name="twitter:card" content="summary" /> + <meta name="twitter:site" content="@linuxnetdata" /> + <meta name="twitter:title" content="Get control of your Linux Servers. Simple. Effective. Awesome." /> + <meta name="twitter:description" content="Unparalleled insights, in real-time, of everything happening on your Linux systems and applications, with stunning, interactive web dashboards and powerful performance and health alarms." /> + <meta name="twitter:image" content="https://cloud.githubusercontent.com/assets/2662304/14092712/93b039ea-f551-11e5-822c-beadbf2b2a2e.gif" /> <style> /* prevent body from hiding under the navbar */ @@ -177,6 +185,10 @@ font-weight: 500; color: #767676; } + .dashboard-sidebar .nav > li > a > .fa { + width: 20px; + text-align: center; + } .dashboard-sidebar .nav > li > a:hover, .dashboard-sidebar .nav > li > a:focus { padding-left: 19px; @@ -364,7 +376,7 @@ width: 233px !important; } } - + @media (min-width: 1360px) { .container { padding-left: 3% !important; @@ -390,2097 +402,2255 @@ } </style> -</head> -<!-- check which theme to use --> -<script type="text/javascript"> - // enable alarms checking and notifications - var netdataShowAlarms = true; - - // enable registry updates - var netdataRegistry = true; - - // -------------------------------------------------------------------- - // urlOptions - - var urlOptions = { - hash: '#', - theme: null, - help: null, - pan_and_zoom: false, - after: 0, - before: 0, - nowelcome: false, - show_alarms: false, - chart: null, - family: null, - alarm: null, - alarm_unique_id: 0, - alarm_id: 0, - alarm_event_id: 0, - hasProperty: function(property) { - // console.log('checking property ' + property + ' of type ' + typeof(this[property])); - return typeof this[property] !== 'undefined'; - } - }; + <!-- check which theme to use --> + <script type="text/javascript"> + // enable alarms checking and notifications + var netdataShowAlarms = true; + + // enable registry updates + var netdataRegistry = true; + + // -------------------------------------------------------------------- + // urlOptions + + var urlOptions = { + hash: '#', + theme: null, + help: null, + update_always: false, + pan_and_zoom: false, + after: 0, + before: 0, + nowelcome: false, + show_alarms: false, + chart: null, + family: null, + alarm: null, + alarm_unique_id: 0, + alarm_id: 0, + alarm_event_id: 0, + + hasProperty: function(property) { + // console.log('checking property ' + property + ' of type ' + typeof(this[property])); + return typeof this[property] !== 'undefined'; + }, + + genHash: function() { + var hash = urlOptions.hash; + + if(urlOptions.pan_and_zoom === true) { + hash += ';after=' + urlOptions.after.toString() + + ';before=' + urlOptions.before.toString(); + } - function netdataPanAndZoomCallback(status, after, before) { - urlOptions.pan_and_zoom = status; - urlOptions.after = after; - urlOptions.before = before; - netdataHashUpdate(); - } + if(urlOptions.theme !== null) + hash += ';theme=' + urlOptions.theme.toString(); - function netdataHashUpdate() { - history.replaceState(null, '', netdataHash()); - } + if(urlOptions.help !== null) + hash += ';help=' + urlOptions.help.toString(); - function netdataHash() { - var hash = urlOptions.hash; + if(urlOptions.update_always === true) + hash += ';update_always=true'; - if(urlOptions.pan_and_zoom === true) { - hash += ';after=' + urlOptions.after.toString() + - ';before=' + urlOptions.before.toString(); - } + return hash; + }, - if(urlOptions.theme !== null) - hash += ';theme=' + urlOptions.theme.toString(); + parseHash: function() { + 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]] = decodeURIComponent(p[1]); + } + else { + if(variables[len].length > 0) + urlOptions.hash = variables[len]; + } + } - if(urlOptions.help !== null) - hash += ';help=' + urlOptions.help.toString(); + var booleans = [ 'nowelcome', 'show_alarms', 'pan_and_zoom', 'update_always' ]; + len = booleans.length; + while(len--) { + if(urlOptions[booleans[len]] === 'true' || urlOptions[booleans[len]] === true || urlOptions[booleans[len]] === '1' || urlOptions[booleans[len]] === 1) + urlOptions[booleans[len]] = true; + else + urlOptions[booleans[len]] = false; + } - return hash; - } + if(urlOptions.before > 0 && urlOptions.after > 0) { + urlOptions.pan_and_zoom = true; + urlOptions.nowelcome = true; + } + else + urlOptions.pan_and_zoom = false; - 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]] = decodeURIComponent(p[1]); - } - else { - if(variables[len].length > 0) - urlOptions.hash = variables[len]; - } - } + // console.log(urlOptions); + }, - var booleans = [ 'nowelcome', 'show_alarms', 'pan_and_zoom' ]; - len = booleans.length; - while(len--) { - if(urlOptions[booleans[len]] === 'true' || urlOptions[booleans[len]] === true || urlOptions[booleans[len]] === '1' || urlOptions[booleans[len]] === 1) - urlOptions[booleans[len]] = true; - else - urlOptions[booleans[len]] = false; - } + hashUpdate: function() { + history.replaceState(null, '', urlOptions.genHash()); + }, - if(urlOptions.before > 0 && urlOptions.after > 0) { - urlOptions.pan_and_zoom = true; - urlOptions.nowelcome = true; - } - else - urlOptions.pan_and_zoom = false; + netdataPanAndZoomCallback: function(status, after, before) { + urlOptions.pan_and_zoom = status; + urlOptions.after = after; + urlOptions.before = before; + urlOptions.hashUpdate(); + } - // console.log(urlOptions); - } + }; - netdataHashParse(); + urlOptions.parseHash(); - // -------------------------------------------------------------------- - // check options that should be processed before loading netdata.js - - function loadLocalStorage(name) { - var ret = null; + // -------------------------------------------------------------------- + // check options that should be processed before loading netdata.js - try { - if(typeof Storage !== "undefined" && typeof localStorage === 'object') - ret = localStorage.getItem(name); - } - catch(error) { - ; - } + function loadLocalStorage(name) { + var ret = null; - if(typeof ret === 'undefined' || ret === null) - return null; + try { + if(typeof Storage !== "undefined" && typeof localStorage === 'object') + ret = localStorage.getItem(name); + } + catch(error) { + ; + } - // console.log('loaded: ' + name.toString() + ' = ' + ret.toString()); + if(typeof ret === 'undefined' || ret === null) + return null; - return ret; - } + // console.log('loaded: ' + name.toString() + ' = ' + ret.toString()); - 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 true; + 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 true; + } } + catch(error) { + ; + } + + return false; + } + + function getTheme(def) { + var ret = loadLocalStorage('netdataTheme'); + if(typeof ret === 'undefined' || ret === null || ret === 'undefined') + return def; + else + return ret; } - catch(error) { - ; + + function setTheme(theme) { + if(theme === netdataTheme) return false; + return saveLocalStorage('netdataTheme', theme); } - return false; - } + var netdataTheme = getTheme('slate'); + var netdataShowHelp = true; - function getTheme(def) { - var ret = loadLocalStorage('netdataTheme'); - if(typeof ret === 'undefined' || ret === null || ret === 'undefined') - return def; + if(urlOptions.theme !== null) { + setTheme(urlOptions.theme); + netdataTheme = urlOptions.theme; + } else - return ret; - } + urlOptions.theme = netdataTheme; - function setTheme(theme) { - if(theme === netdataTheme) return false; - return saveLocalStorage('netdataTheme', theme); - } + if(urlOptions.help !== null) { + saveLocalStorage('options.show_help', urlOptions.help); + netdataShowHelp = urlOptions.help; + } + else { + urlOptions.help = loadLocalStorage('options.show_help'); + } - var netdataTheme = getTheme('slate'); - var netdataShowHelp = true; + // -------------------------------------------------------------------- + // registry call back to render my-netdata menu - if(urlOptions.theme !== null) { - setTheme(urlOptions.theme); - netdataTheme = urlOptions.theme; - } - else - urlOptions.theme = netdataTheme; + var netdataRegistryCallback = function(machines_array) { + var el = ''; + var a1 = ''; + var found = 0; - if(urlOptions.help !== null) { - saveLocalStorage('options.show_help', urlOptions.help); - netdataShowHelp = urlOptions.help; - } - else { - urlOptions.help = loadLocalStorage('options.show_help'); - } + if(machines_array === null) { + var ret = loadLocalStorage("registryCallback"); + if(typeof ret !== 'undefined' && ret !== null) { + machines_array = JSON.parse(ret); + console.log("failed to contact the registry - loaded registry data from browser local storage"); + } + } - // -------------------------------------------------------------------- - // registry call back to render my-netdata menu + if(machines_array) { + saveLocalStorage("registryCallback", JSON.stringify(machines_array)); - var netdataRegistryCallback = function(machines_array) { - var el = ''; - var a1 = ''; - var found = 0; + var machines = machines_array.sort(function (a, b) { + if (a.name > b.name) return -1; + if (a.name < b.name) return 1; + return 0; + }); - if(machines_array === null) { - var ret = loadLocalStorage("registryCallback"); - if(typeof ret !== 'undefined' && ret !== null) { - machines_array = JSON.parse(ret); - console.log("failed to contact the registry - loaded registry data from browser local storage"); + var len = machines.length; + while(len--) { + var u = machines[len]; + found++; + el += '<li id="registry_server_' + u.guid + '"><a class="registry_link" href="' + u.url + '" onClick="return gotoServerModalHandler(\'' + u.guid + '\');">' + u.name + '</a></li>'; + a1 += '<li id="registry_action_' + u.guid + '"><a href="#" onclick="deleteRegistryModalHandler(\'' + u.guid + '\',\'' + u.name + '\',\'' + u.url + '\'); return false;"><i class="fa fa-trash-o" aria-hidden="true" style="color: #999;"></i></a></li>'; + } } - } - if(machines_array) { - saveLocalStorage("registryCallback", JSON.stringify(machines_array)); + if(!found) { + if(machines) + el += '<li><a href="https://github.com/firehol/netdata/wiki/mynetdata-menu-item" style="color: #666;" target="_blank">your netdata server list is empty...</a></li>'; + else + el += '<li><a href="https://github.com/firehol/netdata/wiki/mynetdata-menu-item" style="color: #666;" target="_blank">failed to contact the registry...</a></li>'; + + a1 += '<li><a href="#" onClick="return false;"> </a></li>'; + + el += '<li role="separator" class="divider"></li>' + + '<li><a href="//london.netdata.rocks/default.html">UK - London (DigitalOcean.com)</a></li>' + + '<li><a href="//newyork.netdata.rocks/default.html">US - New York (DigitalOcean.com)</a></li>' + + '<li><a href="//sanfrancisco.netdata.rocks/default.html">US - San Francisco (DigitalOcean.com)</a></li>' + + '<li><a href="//atlanta.netdata.rocks/default.html">US - Atlanta (CDN77.com)</a></li>' + + '<li><a href="//frankfurt.netdata.rocks/default.html">Germany - Frankfurt (DigitalOcean.com)</a></li>' + + '<li><a href="//toronto.netdata.rocks/default.html">Canada - Toronto (DigitalOcean.com)</a></li>' + + '<li><a href="//singapore.netdata.rocks/default.html">Japan - Singapore (DigitalOcean.com)</a></li>' + + '<li><a href="//bangalore.netdata.rocks/default.html">India - Bangalore (DigitalOcean.com)</a></li>'; + a1 += '<li role="separator" class="divider"></li>' + + '<li><a href="#"> </a></li>' + + '<li><a href="#"> </a></li>'+ + '<li><a href="#"> </a></li>'+ + '<li><a href="#"> </a></li>'+ + '<li><a href="#"> </a></li>'+ + '<li><a href="#"> </a></li>'+ + '<li><a href="#"> </a></li>'+ + '<li><a href="#"> </a></li>'; + } - var machines = machines_array.sort(function (a, b) { - if (a.name > b.name) return -1; - if (a.name < b.name) return 1; - return 0; - }); + el += '<li role="separator" class="divider"></li>'; + a1 += '<li role="separator" class="divider"></li>'; - var len = machines.length; - while(len--) { - var u = machines[len]; - found++; - el += '<li id="registry_server_' + u.guid + '"><a class="registry_link" href="' + u.url + '" onClick="return gotoServerModalHandler(\'' + u.guid + '\');">' + u.name + '</a></li>'; - a1 += '<li id="registry_action_' + u.guid + '"><a href="#" onclick="deleteRegistryModalHandler(\'' + u.guid + '\',\'' + u.name + '\',\'' + u.url + '\'); return false;"><i class="fa fa-trash-o" aria-hidden="true" style="color: #999;"></i></a></li>'; + el += '<li><a href="https://github.com/firehol/netdata/wiki/mynetdata-menu-item" style="color: #999;" target="_blank">What is this?</a></li>'; + a1 += '<li><a href="#" style="color: #999;" onclick="switchRegistryModalHandler(); return false;"><i class="fa fa-sliders" aria-hidden="true" style="color: #999;"></i></a></li>' + + document.getElementById('mynetdata_servers').innerHTML = el; + document.getElementById('mynetdata_servers2').innerHTML = el; + document.getElementById('mynetdata_actions1').innerHTML = a1; + + gotoServerInit(); + }; + + var this_is_demo = null; // FIXME + function isdemo() { + if(this_is_demo !== null) return this_is_demo; + this_is_demo = false; + + try { + if(typeof document.location.hostname === 'string') { + if(document.location.hostname.endsWith('.my-netdata.io') || + document.location.hostname.endsWith('.mynetdata.io') || + document.location.hostname.endsWith('.netdata.rocks') || + document.location.hostname.endsWith('.firehol.org') || + document.location.hostname.endsWith('.netdata.online')) + this_is_demo = true; + } + } + catch(error) { + ; } - } - if(!found) { - if(machines) - el += '<li><a href="https://github.com/firehol/netdata/wiki/mynetdata-menu-item" style="color: #666;" target="_blank">your netdata server list is empty...</a></li>'; - else - el += '<li><a href="https://github.com/firehol/netdata/wiki/mynetdata-menu-item" style="color: #666;" target="_blank">failed to contact the registry...</a></li>'; - - a1 += '<li><a href="#" onClick="return false;"> </a></li>'; - - el += '<li role="separator" class="divider"></li>' + - '<li><a href="//london.netdata.rocks/default.html">UK - London (DigitalOcean.com)</a></li>' + - '<li><a href="//newyork.netdata.rocks/default.html">US - New York (DigitalOcean.com)</a></li>' + - '<li><a href="//sanfrancisco.netdata.rocks/default.html">US - San Francisco (DigitalOcean.com)</a></li>' + - '<li><a href="//atlanta.netdata.rocks/default.html">US - Atlanta (CDN77.com)</a></li>' + - '<li><a href="//frankfurt.netdata.rocks/default.html">Germany - Frankfurt (DigitalOcean.com)</a></li>' + - '<li><a href="//toronto.netdata.rocks/default.html">Canada - Toronto (DigitalOcean.com)</a></li>' + - '<li><a href="//singapore.netdata.rocks/default.html">Japan - Singapore (DigitalOcean.com)</a></li>' + - '<li><a href="//bangalore.netdata.rocks/default.html">India - Bangalore (DigitalOcean.com)</a></li>'; - a1 += '<li role="separator" class="divider"></li>' + - '<li><a href="#"> </a></li>' + - '<li><a href="#"> </a></li>'+ - '<li><a href="#"> </a></li>'+ - '<li><a href="#"> </a></li>'+ - '<li><a href="#"> </a></li>'+ - '<li><a href="#"> </a></li>'+ - '<li><a href="#"> </a></li>'+ - '<li><a href="#"> </a></li>'; + return this_is_demo; } - el += '<li role="separator" class="divider"></li>'; - a1 += '<li role="separator" class="divider"></li>'; - - el += '<li><a href="https://github.com/firehol/netdata/wiki/mynetdata-menu-item" style="color: #999;" target="_blank">What is this?</a></li>'; - a1 += '<li><a href="#" style="color: #999;" onclick="switchRegistryModalHandler(); return false;"><i class="fa fa-sliders" aria-hidden="true" style="color: #999;"></i></a></li>' + function netdataURL(url) { + if(typeof url === 'undefined') + url = document.location.toString(); - document.getElementById('mynetdata_servers').innerHTML = el; - document.getElementById('mynetdata_servers2').innerHTML = el; - document.getElementById('mynetdata_actions1').innerHTML = a1; + if(url.indexOf('#') !== -1) + url = url.substring(0, url.indexOf('#')); - gotoServerInit(); - }; + var hash = urlOptions.genHash(); - var this_is_demo = null; - function isdemo() { - if(this_is_demo !== null) return this_is_demo; - this_is_demo = false; + // console.log('netdataURL: ' + url + hash); - try { - if(typeof document.location.hostname === 'string') { - if(document.location.hostname.endsWith('.my-netdata.io') || - document.location.hostname.endsWith('.mynetdata.io') || - document.location.hostname.endsWith('.netdata.rocks') || - document.location.hostname.endsWith('.firehol.org') || - document.location.hostname.endsWith('.netdata.online')) - this_is_demo = true; - } - } - catch(error) { - ; + return url + hash; } - return this_is_demo; - } + function netdataReload(url) { + var t = netdataURL(url); + // console.log('netdataReload: ' + t); + document.location = t; - function netdataURL(url) { - if(typeof url === 'undefined') - url = document.location.toString(); + // since we play with hash + // this is needed to reload the page + location.reload(); + } - if(url.indexOf('#') !== -1) - url = url.substring(0, url.indexOf('#')); + var gotoServerValidateRemaining = 0; + var gotoServerMiddleClick = false; + var gotoServerStop = false; + function gotoServerValidateUrl(id, guid, url) { + var penaldy = 0; + var error = 'failed'; - var hash = netdataHash(); + if(document.location.toString().startsWith('http://') && url.toString().startsWith('https://')) + // we penalize https only if the current url is http + // to allow the user walk through all its servers. + penaldy = 500; - // console.log('netdataURL: ' + url + hash); + else if(document.location.toString().startsWith('https://') && url.toString().startsWith('http://')) + error = 'can\'t check'; - return url + hash; - } + var finalURL = netdataURL(url); - function netdataReload(url) { - var t = netdataURL(url); - // console.log('netdataReload: ' + t); - document.location = t; + setTimeout(function() { + 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>'; - // since we play with hash - // this is needed to reload the page - location.reload(); - } + NETDATA.registry.hello(url, function(data) { + if(typeof data !== 'undefined' && data !== null && typeof data.machine_guid === 'string' && data.machine_guid === guid) { + // console.log('OK ' + id + ' URL: ' + url); + document.getElementById(guid + '-' + id + '-status').innerHTML = "OK"; - var gotoServerValidateRemaining = 0; - var gotoServerMiddleClick = false; - var gotoServerStop = false; - function gotoServerValidateUrl(id, guid, url) { - var penaldy = 0; - var error = 'failed'; + if(!gotoServerStop) { + gotoServerStop = true; - if(document.location.toString().startsWith('http://') && url.toString().startsWith('https://')) - // we penalize https only if the current url is http - // to allow the user walk through all its servers. - penaldy = 500; + if(gotoServerMiddleClick) { + window.open(finalURL, '_blank'); + gotoServerMiddleClick = false; + 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.getElementById('gotoServerResponse').innerHTML += 'found it! It is at:<br/><small>' + url + '</small>'; + document.location = finalURL; + } + } + } + else { + if(typeof data !== 'undefined' && data !== null && typeof data.machine_guid === 'string' && data.machine_guid !== guid) + error = 'wrong machine'; - else if(document.location.toString().startsWith('https://') && url.toString().startsWith('http://')) - error = 'can\'t check'; - - var finalURL = netdataURL(url); + document.getElementById(guid + '-' + id + '-status').innerHTML = error; + gotoServerValidateRemaining--; + if(gotoServerValidateRemaining <= 0) { + gotoServerMiddleClick = false; + document.getElementById('gotoServerResponse').innerHTML = '<b>Sorry! I cannot find any operational URL for this server</b>'; + } + } + }); + }, (id * 50) + penaldy); + } - setTimeout(function() { - 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>'; + function gotoServerModalHandler(guid) { + // console.log('goto server: ' + guid); - NETDATA.registry.hello(url, function(data) { - if(typeof data !== 'undefined' && data !== null && typeof data.machine_guid === 'string' && data.machine_guid === guid) { - // console.log('OK ' + id + ' URL: ' + url); - document.getElementById(guid + '-' + id + '-status').innerHTML = "OK"; + gotoServerStop = false; + var checked = {}; + var len = NETDATA.registry.machines[guid].alternate_urls.length; + var count = 0; - if(!gotoServerStop) { - gotoServerStop = true; + document.getElementById('gotoServerResponse').innerHTML = ''; + document.getElementById('gotoServerList').innerHTML = ''; + document.getElementById('gotoServerName').innerHTML = NETDATA.registry.machines[guid].name; + $('#gotoServerModal').modal('show'); - if(gotoServerMiddleClick) { - window.open(finalURL, '_blank'); - gotoServerMiddleClick = false; - 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.getElementById('gotoServerResponse').innerHTML += 'found it! It is at:<br/><small>' + url + '</small>'; - document.location = finalURL; + gotoServerValidateRemaining = len; + while(len--) { + var url = NETDATA.registry.machines[guid].alternate_urls[len]; + checked[url] = true; + gotoServerValidateUrl(count++, guid, url); + } + + setTimeout(function() { + if(gotoServerStop === false) { + document.getElementById('gotoServerResponse').innerHTML = '<b>Added all the known URLs for this machine.</b>'; + NETDATA.registry.search(guid, function(data) { + // console.log(data); + len = data.urls.length; + while(len--) { + var url = data.urls[len][1]; + // console.log(url); + if(typeof checked[url] === 'undefined') { + gotoServerValidateRemaining++; + checked[url] = true; + gotoServerValidateUrl(count++, guid, url); + } } - } + }); + } + }, 2000); + return false; + } + + function gotoServerInit() { + $(".registry_link").on('click', function(e) { + if(e.which === 2) { + e.preventDefault(); + gotoServerMiddleClick = true; } else { - if(typeof data !== 'undefined' && data !== null && typeof data.machine_guid === 'string' && data.machine_guid !== guid) - error = 'wrong machine'; - - document.getElementById(guid + '-' + id + '-status').innerHTML = error; - gotoServerValidateRemaining--; - if(gotoServerValidateRemaining <= 0) { - gotoServerMiddleClick = false; - document.getElementById('gotoServerResponse').innerHTML = '<b>Sorry! I cannot find any operational URL for this server</b>'; - } + gotoServerMiddleClick = false; } + + return true; }); - }, (id * 50) + penaldy); - } + } - function gotoServerModalHandler(guid) { - // console.log('goto server: ' + guid); + function switchRegistryModalHandler() { + document.getElementById('switchRegistryPersonGUID').value = NETDATA.registry.person_guid; + document.getElementById('switchRegistryURL').innerHTML = NETDATA.registry.server; + document.getElementById('switchRegistryResponse').innerHTML = ''; + $('#switchRegistryModal').modal('show'); + } - gotoServerStop = false; - var checked = {}; - var len = NETDATA.registry.machines[guid].alternate_urls.length; - var count = 0; + function notifyForSwitchRegistry() { + var n = document.getElementById('switchRegistryPersonGUID').value; - document.getElementById('gotoServerResponse').innerHTML = ''; - document.getElementById('gotoServerList').innerHTML = ''; - document.getElementById('gotoServerName').innerHTML = NETDATA.registry.machines[guid].name; - $('#gotoServerModal').modal('show'); + if(n !== '' && n.length === 36) { + NETDATA.registry.switch(n, function(result) { + if(result !== null) { + $('#switchRegistryModal').modal('hide'); + NETDATA.registry.init(); + } + else { + document.getElementById('switchRegistryResponse').innerHTML = "<b>Sorry! The registry rejected your request.</b>"; + } + }); + } + else + document.getElementById('switchRegistryResponse').innerHTML = "<b>The ID you have entered is not a GUID.</b>"; + } - gotoServerValidateRemaining = len; - while(len--) { - var url = NETDATA.registry.machines[guid].alternate_urls[len]; - checked[url] = true; - gotoServerValidateUrl(count++, guid, url); + var deleteRegistryUrl = null; + function deleteRegistryModalHandler(guid, name, url) { + deleteRegistryUrl = url; + document.getElementById('deleteRegistryServerName').innerHTML = name; + document.getElementById('deleteRegistryServerName2').innerHTML = name; + document.getElementById('deleteRegistryServerURL').innerHTML = url; + document.getElementById('deleteRegistryResponse').innerHTML = ''; + $('#deleteRegistryModal').modal('show'); } - setTimeout(function() { - if(gotoServerStop === false) { - document.getElementById('gotoServerResponse').innerHTML = '<b>Added all the known URLs for this machine.</b>'; - NETDATA.registry.search(guid, function(data) { - // console.log(data); - len = data.urls.length; - while(len--) { - var url = data.urls[len][1]; - // console.log(url); - if(typeof checked[url] === 'undefined') { - gotoServerValidateRemaining++; - checked[url] = true; - gotoServerValidateUrl(count++, guid, url); - } + function notifyForDeleteRegistry() { + if(deleteRegistryUrl) { + NETDATA.registry.delete(deleteRegistryUrl, function(result) { + if(result !== null) { + deleteRegistryUrl = null; + $('#deleteRegistryModal').modal('hide'); + NETDATA.registry.init(); + } + else { + document.getElementById('deleteRegistryResponse').innerHTML = "<b>Sorry! this command was rejected by the registry server.</b>"; } }); } - }, 2000); - return false; - } + } - function gotoServerInit() { - $(".registry_link").on('click', function(e) { - if(e.which === 2) { - e.preventDefault(); - gotoServerMiddleClick = true; - } - else { - gotoServerMiddleClick = false; - } + var options = { + sparklines_registry: {}, + menus: {}, + submenu_names: {}, + data: null, + hostname: 'netdata_server', // will be overwritten by the netdata server + categories: new Array(), + categories_idx: {}, + families: new Array(), + families_idx: {}, + + chartsPerRow: 0, + chartsMinWidth: 1450, + chartsHeight: 180, + sparklinesHeight: 60, + }; - return true; - }); - } + // generate a sparkline + // used in the documentation + function sparkline(chart, dimension, units) { + var key = chart + '.' + dimension; - function switchRegistryModalHandler() { - document.getElementById('switchRegistryPersonGUID').value = NETDATA.registry.person_guid; - document.getElementById('switchRegistryURL').innerHTML = NETDATA.registry.server; - document.getElementById('switchRegistryResponse').innerHTML = ''; - $('#switchRegistryModal').modal('show'); - } + if(typeof units === 'undefined') + units = ''; - function notifyForSwitchRegistry() { - var n = document.getElementById('switchRegistryPersonGUID').value; + if(typeof options.sparklines_registry[key] === 'undefined') + options.sparklines_registry[key] = { count: 1 }; + else + options.sparklines_registry[key].count++; - if(n !== '' && n.length === 36) { - NETDATA.registry.switch(n, function(result) { - if(result !== null) { - $('#switchRegistryModal').modal('hide'); - NETDATA.registry.init(); - } - else { - document.getElementById('switchRegistryResponse').innerHTML = "<b>Sorry! The registry rejected your request.</b>"; - } - }); - } - else - document.getElementById('switchRegistryResponse').innerHTML = "<b>The ID you have entered is not a GUID.</b>"; - } + key = key + '.' + options.sparklines_registry[key].count; - var deleteRegistryUrl = null; - function deleteRegistryModalHandler(guid, name, url) { - deleteRegistryUrl = url; - document.getElementById('deleteRegistryServerName').innerHTML = name; - document.getElementById('deleteRegistryServerName2').innerHTML = name; - document.getElementById('deleteRegistryServerURL').innerHTML = url; - document.getElementById('deleteRegistryResponse').innerHTML = ''; - $('#deleteRegistryModal').modal('show'); - } + var h = '<div data-netdata="' + chart + '" data-after="-120" data-width="25%" data-height="15px" data-chart-library="dygraph" data-dygraph-theme="sparkline" data-dimensions="' + dimension + '" data-show-value-of-' + dimension + '-at="' + key + '"></div> (<span id="' + key + '" style="display: inline-block; min-width: 50px; text-align: right;">X</span>' + units + ')'; - function notifyForDeleteRegistry() { - if(deleteRegistryUrl) { - NETDATA.registry.delete(deleteRegistryUrl, function(result) { - if(result !== null) { - deleteRegistryUrl = null; - $('#deleteRegistryModal').modal('hide'); - NETDATA.registry.init(); - } - else { - document.getElementById('deleteRegistryResponse').innerHTML = "<b>Sorry! this command was rejected by the registry server.</b>"; - } - }); + return h; } - } - var options = { - sparklines_registry: {}, - menus: {}, - submenu_names: {}, - data: null, - hostname: 'netdata_server', // will be overwritten by the netdata server - categories: new Array(), - categories_idx: {}, - families: new Array(), - families_idx: {}, - - chartsPerRow: 0, - chartsMinWidth: 1450, - chartsHeight: 180, - sparklinesHeight: 60, - }; - - // generate a sparkline - // used in the documentation - function sparkline(chart, dimension, units) { - var key = chart + '.' + dimension; - - if(typeof units === 'undefined') - units = ''; - - if(typeof options.sparklines_registry[key] === 'undefined') - options.sparklines_registry[key] = { count: 1 }; - else - options.sparklines_registry[key].count++; + function chartsPerRow(total) { + if(options.chartsPerRow === 0) { + width = Math.floor(total / options.chartsMinWidth); + if(width === 0) width = 1; + return width; + } + else return options.chartsPerRow; + } - key = key + '.' + options.sparklines_registry[key].count; + function prioritySort(a, b) { + if(a.priority < b.priority) return -1; + if(a.priority > b.priority) return 1; + if(a.name < b.name) return -1; + return 1; + } - var h = '<div data-netdata="' + chart + '" data-after="-120" data-width="25%" data-height="15px" data-chart-library="dygraph" data-dygraph-theme="sparkline" data-dimensions="' + dimension + '" data-show-value-of-' + dimension + '-at="' + key + '"></div> (<span id="' + key + '" style="display: inline-block; min-width: 50px; text-align: right;">X</span>' + units + ')'; + function sortObjectByPriority(object) { + var idx = {}; + var sorted = new Array(); - return h; - } + for(var i in object) { + if(typeof idx[i] === 'undefined') { + idx[i] = object[i]; + sorted.push(i); + } + } - function chartsPerRow(total) { - if(options.chartsPerRow === 0) { - width = Math.floor(total / options.chartsMinWidth); - if(width === 0) width = 1; - return width; + sorted.sort(function(a, b) { + if(idx[a].priority < idx[b].priority) return -1; + if(idx[a].priority > idx[b].priority) return 1; + if(a < b) return -1; + return 1; + }); + + return sorted; } - else return options.chartsPerRow; - } - function prioritySort(a, b) { - if(a.priority < b.priority) return -1; - if(a.priority > b.priority) return 1; - if(a.name < b.name) return -1; - return 1; - } - function sortObjectByPriority(object) { - var idx = {}; - var sorted = new Array(); + // ---------------------------------------------------------------------------- + // scroll to a section, without changing the browser history - for(var i in object) { - if(typeof idx[i] === 'undefined') { - idx[i] = object[i]; - sorted.push(i); + function scrollToId(hash) { + if(hash && hash != '' && document.getElementById(hash) !== null) { + var offset = $('#' + hash).offset(); + if(typeof offset !== 'undefined') + $('html, body').animate({ scrollTop: offset.top }, 0); } + + // we must return false to prevent the default action + return false; } - sorted.sort(function(a, b) { - if(idx[a].priority < idx[b].priority) return -1; - if(idx[a].priority > idx[b].priority) return 1; - if(a < b) return -1; - return 1; - }); + // ---------------------------------------------------------------------------- + + var netdataDashboard = { + menu: {}, + submenu: {}, + context: {}, + + gaugeChart: function(title, width, dimensions, colors) { + if(typeof colors === 'undefined') + colors = ''; + + if(typeof dimensions === 'undefined') + dimensions = ''; + + return '<div data-netdata="CHART_UNIQUE_ID"' + + ' data-dimensions="' + dimensions + '"' + + ' data-chart-library="gauge"' + + ' data-gauge-adjust="width"' + + ' data-title="' + title + '"' + + ' data-width="' + width + '"' + + ' data-before="0"' + + ' data-after="-CHART_DURATION"' + + ' data-points="CHART_DURATION"' + + ' data-colors="' + colors + '"' + + ' role="application"></div>'; + }, + + anyAttribute: function(obj, attr, key, def) { + if(typeof obj[key] !== 'undefined') { + if(typeof obj[key][attr] !== 'undefined') + return obj[key][attr]; + } + return def; + }, - return sorted; - } + menuTitle: function(chart) { + if(typeof chart.menu_pattern !== 'undefined') { + return (netdataDashboard.anyAttribute(netdataDashboard.menu, 'title', chart.menu_pattern, chart.menu_pattern).toString() + + ' ' + chart.type.slice(-(chart.type.length - chart.menu_pattern.length - 1)).toString()).replace(/_/g, ' '); + } + return (netdataDashboard.anyAttribute(netdataDashboard.menu, 'title', chart.menu, chart.menu)).toString().replace(/_/g, ' '); + }, - // ---------------------------------------------------------------------------- - // scroll to a section, without changing the browser history + menuIcon: function(chart) { + if(typeof chart.menu_pattern !== 'undefined') + return netdataDashboard.anyAttribute(netdataDashboard.menu, 'icon', chart.menu_pattern, '<i class="fa fa-puzzle-piece" aria-hidden="true"></i>').toString(); - function scrollToId(hash) { - if(hash && hash != '') { - var offset = $('#' + hash).offset(); - if(typeof offset !== 'undefined') - $('html, body').animate({ scrollTop: offset.top }, 0); - } + return netdataDashboard.anyAttribute(netdataDashboard.menu, 'icon', chart.menu, '<i class="fa fa-puzzle-piece" aria-hidden="true"></i>'); + }, - // we must return false to prevent the default action - return false; - } + menuInfo: function(menu) { + return netdataDashboard.anyAttribute(netdataDashboard.menu, 'info', menu, null); + }, - // ---------------------------------------------------------------------------- - - var netdataDashboard = { - menu: {}, - submenu: {}, - context: {}, - - gaugeChart: function(title, width, dimensions, colors) { - if(typeof colors === 'undefined') - colors = ''; - - if(typeof dimensions === 'undefined') - dimensions = ''; - - return '<div data-netdata="CHART_UNIQUE_ID"' - + ' data-dimensions="' + dimensions + '"' - + ' data-chart-library="gauge"' - + ' data-gauge-adjust="width"' - + ' data-title="' + title + '"' - + ' data-width="' + width + '"' - + ' data-before="0"' - + ' data-after="-CHART_DURATION"' - + ' data-points="CHART_DURATION"' - + ' data-colors="' + colors + '"' - + ' role="application"></div>'; - }, - - anyAttribute: function(obj, attr, key, def) { - if(typeof obj[key] !== 'undefined') { - if(typeof obj[key][attr] !== 'undefined') - return obj[key][attr]; - } - return def; - }, + menuHeight: function(menu, relative) { + return netdataDashboard.anyAttribute(netdataDashboard.menu, 'height', menu, 1.0) * relative; + }, - menuTitle: function(chart) { - if(typeof chart.menu_pattern !== 'undefined') { - return (netdataDashboard.anyAttribute(netdataDashboard.menu, 'title', chart.menu_pattern, chart.menu_pattern).toString() - + ' ' + chart.type.slice(-(chart.type.length - chart.menu_pattern.length - 1)).toString()).replace(/_/g, ' '); - } + submenuTitle: function(menu, submenu) { + var key = menu + '.' + submenu; + var title = netdataDashboard.anyAttribute(netdataDashboard.submenu, 'title', key, submenu).toString().replace(/_/g, ' ');; + if(title.length > 28) { + var a = title.substring(0, 13); + var b = title.substring(title.length - 12, title.length); + return a + '...' + b; + } + return title; + }, + + submenuInfo: function(menu, submenu) { + var key = menu + '.' + submenu; + return netdataDashboard.anyAttribute(netdataDashboard.submenu, 'info', key, null); + }, + + submenuHeight: function(menu, submenu, relative) { + var key = menu + '.' + submenu; + return netdataDashboard.anyAttribute(netdataDashboard.submenu, 'height', key, 1.0) * relative; + }, + + contextInfo: function(id) { + if(typeof netdataDashboard.context[id] !== 'undefined' && typeof netdataDashboard.context[id].info !== 'undefined') + return '<div class="chart-message netdata-chart-alignment" role="document">' + netdataDashboard.context[id].info + '</div>'; + else + return ''; + }, - return (netdataDashboard.anyAttribute(netdataDashboard.menu, 'title', chart.menu, chart.menu)).toString().replace(/_/g, ' '); - }, + contextValueRange: function(id) { + if(typeof netdataDashboard.context[id] !== 'undefined' && typeof netdataDashboard.context[id].valueRange !== 'undefined') + return netdataDashboard.context[id].valueRange; + else + return '[null, null]'; + }, - menuIcon: function(chart) { - if(typeof chart.menu_pattern !== 'undefined') - return netdataDashboard.anyAttribute(netdataDashboard.menu, 'icon', chart.menu_pattern, '<i class="fa fa-puzzle-piece" aria-hidden="true"></i>').toString(); + contextHeight: function(id, def) { + if(typeof netdataDashboard.context[id] !== 'undefined' && typeof netdataDashboard.context[id].height !== 'undefined') + return def * netdataDashboard.context[id].height; + else + return def; + } + }; - return netdataDashboard.anyAttribute(netdataDashboard.menu, 'icon', chart.menu, '<i class="fa fa-puzzle-piece" aria-hidden="true"></i>'); - }, + // ---------------------------------------------------------------------------- - menuInfo: function(menu) { - return netdataDashboard.anyAttribute(netdataDashboard.menu, 'info', menu, null); - }, + // enrich the data structure returned by netdata + // to reflect our menu system and content + function enrichChartData(chart) { + var tmp = chart.type.split('_')[0]; - menuHeight: function(menu, relative) { - return netdataDashboard.anyAttribute(netdataDashboard.menu, 'height', menu, 1.0) * relative; - }, + switch(tmp) { + case 'ap': + case 'net': + case 'disk': + chart.menu = tmp; + break; - submenuTitle: function(menu, submenu) { - var key = menu + '.' + submenu; - var title = netdataDashboard.anyAttribute(netdataDashboard.submenu, 'title', key, submenu).toString().replace(/_/g, ' ');; - if(title.length > 28) { - var a = title.substring(0, 13); - var b = title.substring(title.length - 12, title.length); - return a + '...' + b; - } - return title; - }, - - submenuInfo: function(menu, submenu) { - var key = menu + '.' + submenu; - return netdataDashboard.anyAttribute(netdataDashboard.submenu, 'info', key, null); - }, - - submenuHeight: function(menu, submenu, relative) { - var key = menu + '.' + submenu; - return netdataDashboard.anyAttribute(netdataDashboard.submenu, 'height', key, 1.0) * relative; - }, - - contextInfo: function(id) { - if(typeof netdataDashboard.context[id] !== 'undefined' && typeof netdataDashboard.context[id].info !== 'undefined') - return '<div class="chart-message netdata-chart-alignment" role="document">' + netdataDashboard.context[id].info + '</div>'; - else - return ''; - }, + case 'cgroup': + chart.menu = chart.type; + if(chart.id.match(/.*[\._\/-:]qemu[\._\/-:]*/) || chart.id.match(/.*[\._\/-:]kvm[\._\/-:]*/)) + chart.menu_pattern = 'cgqemu'; + else + chart.menu_pattern = 'cgroup'; + break; + + case 'apache': + case 'exim': + case 'dovecot': + case 'hddtemp': + case 'ipfs': + case 'memcached': + case 'mysql': + case 'named': + case 'nginx': + case 'nut': + case 'phpfpm': + case 'postfix': + case 'postgres': + case 'redis': + case 'retroshare': + case 'smawebbox': + case 'snmp': + case 'squid': + case 'tomcat': + chart.menu = chart.type; + chart.menu_pattern = tmp; + break; + + case 'tc': + chart.menu = tmp; + + // find a name for this device from fireqos info + // we strip '_(in|out)' or '(in|out)_' + if(typeof options.submenu_names[chart.family] === 'undefined' || options.submenu_names[chart.family] === chart.family) { + var n = chart.name.split('.')[1]; + if(n.endsWith('_in')) + options.submenu_names[chart.family] = n.slice(0, n.lastIndexOf('_in')); + else if(n.endsWith('_out')) + options.submenu_names[chart.family] = n.slice(0, n.lastIndexOf('_out')); + else if(n.startsWith('in_')) + options.submenu_names[chart.family] = n.slice(3, n.length); + else if(n.startsWith('out_')) + options.submenu_names[chart.family] = n.slice(4, n.length); + } - contextHeight: function(id, def) { - if(typeof netdataDashboard.context[id] !== 'undefined' && typeof netdataDashboard.context[id].height !== 'undefined') - return def * netdataDashboard.context[id].height; - else - return def; - } - }; - - // ---------------------------------------------------------------------------- - - // enrich the data structure returned by netdata - // to reflect our menu system and content - function enrichChartData(chart) { - var tmp = chart.type.split('_')[0]; - - switch(tmp) { - case 'ap': - case 'net': - case 'disk': - chart.menu = tmp; - break; - - case 'cgroup': - chart.menu = chart.type; - if(chart.id.match(/.*[\._\/-:]qemu[\._\/-:]*/) || chart.id.match(/.*[\._\/-:]kvm[\._\/-:]*/)) - chart.menu_pattern = 'cgqemu'; - else - chart.menu_pattern = 'cgroup'; - break; - - case 'apache': - case 'exim': - case 'memcached': - case 'mysql': - case 'named': - case 'nginx': - case 'nut': - case 'phpfpm': - case 'postfix': - case 'redis': - case 'retroshare': - case 'ipfs': - case 'smawebbox': - case 'squid': - case 'snmp': - case 'tomcat': - chart.menu = chart.type; - chart.menu_pattern = tmp; - break; - - case 'tc': - chart.menu = tmp; - - // find a name for this device from fireqos info - // we strip '_(in|out)' or '(in|out)_' - if(typeof options.submenu_names[chart.family] === 'undefined' || options.submenu_names[chart.family] === chart.family) { - var n = chart.name.split('.')[1]; - if(n.endsWith('_in')) - options.submenu_names[chart.family] = n.slice(0, n.lastIndexOf('_in')); - else if(n.endsWith('_out')) - options.submenu_names[chart.family] = n.slice(0, n.lastIndexOf('_out')); - else if(n.startsWith('in_')) - options.submenu_names[chart.family] = n.slice(3, n.length); - else if(n.startsWith('out_')) - options.submenu_names[chart.family] = n.slice(4, n.length); - } + // increase the priority of IFB devices + // to have inbound appear before outbound + if(chart.id.match(/.*-ifb$/)) + chart.priority--; - // increase the priority of IFB devices - // to have inbound appear before outbound - if(chart.id.match(/.*-ifb$/)) - chart.priority--; + break; - break; + default: + chart.menu = chart.type; + break; + } - default: - chart.menu = chart.type; - break; + chart.submenu = chart.family; } - chart.submenu = chart.family; - } + // ---------------------------------------------------------------------------- + + function headMain(charts, duration) { + var head = ''; + + if(typeof charts['system.swap'] !== 'undefined') + head += '<div style="margin-right: 10px;" data-netdata="system.swap"' + + ' data-dimensions="used"' + + ' data-append-options="percentage"' + + ' data-chart-library="easypiechart"' + + ' data-title="Used Swap"' + + ' data-units="%"' + + ' data-easypiechart-max-value="100"' + + ' data-width="8%"' + + ' data-before="0"' + + ' data-after="-' + duration.toString() + '"' + + ' data-points="' + duration.toString() + '"' + + ' data-colors="#DD4400"' + + ' role="application"></div>'; + + if(typeof charts['system.io'] !== 'undefined') { + head += '<div style="margin-right: 10px;" data-netdata="system.io"' + + ' data-dimensions="in"' + + ' data-chart-library="easypiechart"' + + ' data-title="Disk Read"' + + ' data-width="10%"' + + ' data-before="0"' + + ' data-after="-' + duration.toString() + '"' + + ' data-points="' + duration.toString() + '"' + + ' role="application"></div>'; + + head += '<div style="margin-right: 10px;" data-netdata="system.io"' + + ' data-dimensions="out"' + + ' data-chart-library="easypiechart"' + + ' data-title="Disk Write"' + + ' data-width="10%"' + + ' data-before="0"' + + ' data-after="-' + duration.toString() + '"' + + ' data-points="' + duration.toString() + '"' + + ' role="application"></div>'; + } - // ---------------------------------------------------------------------------- - - function headMain(charts, duration) { - var head = ''; - - if(typeof charts['system.swap'] !== 'undefined') - head += '<div style="margin-right: 10px;" data-netdata="system.swap"' - + ' data-dimensions="free"' - + ' data-append-options="percentage"' - + ' data-chart-library="easypiechart"' - + ' data-title="Free Swap"' - + ' data-units="%"' - + ' data-easypiechart-max-value="100"' - + ' data-width="8%"' - + ' data-before="0"' - + ' data-after="-' + duration.toString() + '"' - + ' data-points="' + duration.toString() + '"' - + ' data-colors="#DD4400"' - + ' role="application"></div>'; - - if(typeof charts['system.io'] !== 'undefined') { - head += '<div style="margin-right: 10px;" data-netdata="system.io"' - + ' data-dimensions="in"' - + ' data-chart-library="easypiechart"' - + ' data-title="Disk Read"' - + ' data-width="10%"' - + ' data-before="0"' - + ' data-after="-' + duration.toString() + '"' - + ' data-points="' + duration.toString() + '"' - + ' role="application"></div>'; - - head += '<div style="margin-right: 10px;" data-netdata="system.io"' - + ' data-dimensions="out"' - + ' data-chart-library="easypiechart"' - + ' data-title="Disk Write"' - + ' data-width="10%"' - + ' data-before="0"' - + ' data-after="-' + duration.toString() + '"' - + ' data-points="' + duration.toString() + '"' - + ' role="application"></div>'; - } + if(typeof charts['system.cpu'] !== 'undefined') + head += '<div data-netdata="system.cpu"' + + ' data-chart-library="gauge"' + + ' data-title="CPU"' + + ' data-units="%"' + + ' data-gauge-max-value="100"' + + ' data-width="18%"' + + ' data-after="-' + duration.toString() + '"' + + ' data-points="' + duration.toString() + '"' + + ' data-colors="' + NETDATA.colors[12] + '"' + + ' role="application"></div>'; + + if(typeof charts['system.ipv4'] !== 'undefined') { + head += '<div style="margin-right: 10px;" data-netdata="system.ipv4"' + + ' data-dimensions="received"' + + ' data-chart-library="easypiechart"' + + ' data-title="IPv4 Inbound"' + + ' data-width="10%"' + + ' data-before="0"' + + ' data-after="-' + duration.toString() + '"' + + ' data-points="' + duration.toString() + '"' + + ' role="application"></div>'; + + head += '<div style="margin-right: 10px;" data-netdata="system.ipv4"' + + ' data-dimensions="sent"' + + ' data-chart-library="easypiechart"' + + ' data-title="IPv4 Outbound"' + + ' data-width="10%"' + + ' data-before="0"' + + ' data-after="-' + duration.toString() + '"' + + ' data-points="' + duration.toString() + '"' + + ' role="application"></div>'; + } + else if(typeof charts['system.ipv6'] !== 'undefined') { + head += '<div style="margin-right: 10px;" data-netdata="system.ipv6"' + + ' data-dimensions="received"' + + ' data-chart-library="easypiechart"' + + ' data-title="IPv6 Inbound"' + + ' data-units="kbps"' + + ' data-width="10%"' + + ' data-before="0"' + + ' data-after="-' + duration.toString() + '"' + + ' data-points="' + duration.toString() + '"' + + ' role="application"></div>'; + + head += '<div style="margin-right: 10px;" data-netdata="system.ipv6"' + + ' data-dimensions="sent"' + + ' data-chart-library="easypiechart"' + + ' data-title="IPv6 Outbound"' + + ' data-units="kbps"' + + ' data-width="10%"' + + ' data-before="0"' + + ' data-after="-' + duration.toString() + '"' + + ' data-points="' + duration.toString() + '"' + + ' role="application"></div>'; + } - if(typeof charts['system.cpu'] !== 'undefined') - head += '<div data-netdata="system.cpu"' - + ' data-chart-library="gauge"' - + ' data-title="CPU"' - + ' data-units="%"' - + ' data-gauge-max-value="100"' - + ' data-width="18%"' - + ' data-after="-' + duration.toString() + '"' - + ' data-points="' + duration.toString() + '"' - + ' data-colors="' + NETDATA.colors[12] + '"' - + ' role="application"></div>'; - - if(typeof charts['system.ipv4'] !== 'undefined') { - head += '<div style="margin-right: 10px;" data-netdata="system.ipv4"' - + ' data-dimensions="received"' - + ' data-chart-library="easypiechart"' - + ' data-title="IPv4 Inbound"' - + ' data-width="10%"' - + ' data-before="0"' - + ' data-after="-' + duration.toString() + '"' - + ' data-points="' + duration.toString() + '"' - + ' role="application"></div>'; - - head += '<div style="margin-right: 10px;" data-netdata="system.ipv4"' - + ' data-dimensions="sent"' - + ' data-chart-library="easypiechart"' - + ' data-title="IPv4 Outbound"' - + ' data-width="10%"' - + ' data-before="0"' - + ' data-after="-' + duration.toString() + '"' - + ' data-points="' + duration.toString() + '"' - + ' role="application"></div>'; - } - else if(typeof charts['system.ipv6'] !== 'undefined') { - head += '<div style="margin-right: 10px;" data-netdata="system.ipv6"' - + ' data-dimensions="received"' - + ' data-chart-library="easypiechart"' - + ' data-title="IPv6 Inbound"' - + ' data-units="kbps"' - + ' data-width="10%"' - + ' data-before="0"' - + ' data-after="-' + duration.toString() + '"' - + ' data-points="' + duration.toString() + '"' - + ' role="application"></div>'; - - head += '<div style="margin-right: 10px;" data-netdata="system.ipv6"' - + ' data-dimensions="sent"' - + ' data-chart-library="easypiechart"' - + ' data-title="IPv6 Outbound"' - + ' data-units="kbps"' - + ' data-width="10%"' - + ' data-before="0"' - + ' data-after="-' + duration.toString() + '"' - + ' data-points="' + duration.toString() + '"' - + ' role="application"></div>'; + if(typeof charts['system.ram'] !== 'undefined') + head += '<div style="margin-right: 10px;" data-netdata="system.ram"' + + ' data-dimensions="used|buffers|active|wired"' // active and wired are FreeBSD stats + + ' data-append-options="percentage"' + + ' data-chart-library="easypiechart"' + + ' data-title="Used RAM"' + + ' data-units="%"' + + ' data-easypiechart-max-value="100"' + + ' data-width="8%"' + + ' data-after="-' + duration.toString() + '"' + + ' data-points="' + duration.toString() + '"' + + ' data-colors="' + NETDATA.colors[7] + '"' + + ' role="application"></div>'; + + return head; } - if(typeof charts['system.ram'] !== 'undefined') - head += '<div style="margin-right: 10px;" data-netdata="system.ram"' - + ' data-dimensions="cached|free"' - + ' data-append-options="percentage"' - + ' data-chart-library="easypiechart"' - + ' data-title="Available RAM"' - + ' data-units="%"' - + ' data-easypiechart-max-value="100"' - + ' data-width="8%"' - + ' data-after="-' + duration.toString() + '"' - + ' data-points="' + duration.toString() + '"' - + ' data-colors="' + NETDATA.colors[7] + '"' - + ' role="application"></div>'; - - return head; - } - - function generateHeadCharts(type, chart, duration) { - var head = ''; - var hcharts = netdataDashboard.anyAttribute(netdataDashboard.context, type, chart.context, []); - if(hcharts.length > 0) { - var hi = 0, hlen = hcharts.length; - while(hi < hlen) { - if(typeof hcharts[hi] === 'function') - head += hcharts[hi](chart.id).replace('CHART_DURATION', duration.toString()).replace('CHART_UNIQUE_ID', chart.id); - else - head += hcharts[hi].replace('CHART_DURATION', duration.toString()).replace('CHART_UNIQUE_ID', chart.id); - hi++; + function generateHeadCharts(type, chart, duration) { + var head = ''; + var hcharts = netdataDashboard.anyAttribute(netdataDashboard.context, type, chart.context, []); + if(hcharts.length > 0) { + var hi = 0, hlen = hcharts.length; + while(hi < hlen) { + if(typeof hcharts[hi] === 'function') + head += hcharts[hi](chart.id).replace('CHART_DURATION', duration.toString()).replace('CHART_UNIQUE_ID', chart.id); + else + head += hcharts[hi].replace('CHART_DURATION', duration.toString()).replace('CHART_UNIQUE_ID', chart.id); + hi++; + } } + return head; } - return head; - } - function renderPage(menus, data) { - var div = document.getElementById('charts_div'); - var pcent_width = Math.floor(100 / chartsPerRow($(div).width())); - - // find the proper duration for per-second updates - var duration = Math.round(($(div).width() * pcent_width / 100 * data.update_every / 3) / 60) * 60; - var html = ''; - var sidebar = '<ul class="nav dashboard-sidenav" data-spy="affix" id="sidebar_ul">'; - var mainhead = headMain(data.charts, duration); - - // sort the menus - var main = sortObjectByPriority(menus); - var i = 0, len = main.length; - while(i < len) { - var menu = main[i++]; - - // generate an entry at the main menu - - var menuid = NETDATA.name2id('menu_' + menu); - sidebar += '<li class=""><a href="#' + menuid + '" onClick="return scrollToId(\'' + menuid + '\');">' + menus[menu].icon + ' ' + menus[menu].title + '</a><ul class="nav">'; - html += '<div role="section"><div role="sectionhead"><h1 id="' + menuid + '" role="heading">' + menus[menu].title + '</h1></div><div role="document">'; - - if(menus[menu].info !== null) - html += menus[menu].info; - - // console.log(' >> ' + menu + ' (' + menus[menu].priority + '): ' + menus[menu].title); - - var shtml = ''; - var mhead = '<div class="netdata-chart-row">' + mainhead; - mainhead = ''; - - // sort the submenus of this menu - var sub = sortObjectByPriority(menus[menu].submenus); - var si = 0, slen = sub.length; - while(si < slen) { - var submenu = sub[si++]; - - // generate an entry at the submenu - var submenuid = NETDATA.name2id('menu_' + menu + '_submenu_' + submenu); - sidebar += '<li class><a href="#' + submenuid + '" onClick="return scrollToId(\'' + submenuid + '\');">' + menus[menu].submenus[submenu].title + '</a></li>'; - shtml += '<div class="netdata-group-container" id="' + submenuid + '" style="display: inline-block; width: ' + pcent_width.toString() + '%"><h2 id="' + submenuid + '" class="netdata-chart-alignment" role="heading">' + menus[menu].submenus[submenu].title + '</h2>'; - - if(menus[menu].submenus[submenu].info !== null) - shtml += '<div class="chart-message netdata-chart-alignment" role="document">' + menus[menu].submenus[submenu].info + '</div>'; - - var head = '<div class="netdata-chart-row">'; - var chtml = ''; - - // console.log(' \------- ' + submenu + ' (' + menus[menu].submenus[submenu].priority + '): ' + menus[menu].submenus[submenu].title); - - // sort the charts in this submenu of this menu - menus[menu].submenus[submenu].charts.sort(prioritySort); - var ci = 0, clen = menus[menu].submenus[submenu].charts.length; - while(ci < clen) { - var chart = menus[menu].submenus[submenu].charts[ci++]; - - // generate the submenu heading charts - mhead += generateHeadCharts('mainheads', chart, duration); - head += generateHeadCharts('heads', chart, duration); - - // generate the chart - chtml += netdataDashboard.contextInfo(chart.context) + '<div id="chart_' + NETDATA.name2id(chart.id) + '" data-netdata="' + chart.id + '"' - + ' data-width="' + pcent_width.toString() + '%"' - + ' data-height="' + netdataDashboard.contextHeight(chart.context, options.chartsHeight).toString() + 'px"' - + ' data-before="0"' - + ' data-after="-' + duration.toString() + '"' - + ' data-id="' + NETDATA.name2id(options.hostname + '/' + chart.id) + '"' - + ' data-colors="' + netdataDashboard.anyAttribute(netdataDashboard.context, 'colors', chart.context, '') + '"' - + ' role="application"></div>'; - - // console.log(' \------- ' + chart.id + ' (' + chart.priority + '): ' + chart.context + ' height: ' + menus[menu].submenus[submenu].height); + function renderPage(menus, data) { + var div = document.getElementById('charts_div'); + var pcent_width = Math.floor(100 / chartsPerRow($(div).width())); + + // find the proper duration for per-second updates + var duration = Math.round(($(div).width() * pcent_width / 100 * data.update_every / 3) / 60) * 60; + var html = ''; + var sidebar = '<ul class="nav dashboard-sidenav" data-spy="affix" id="sidebar_ul">'; + var mainhead = headMain(data.charts, duration); + + // sort the menus + var main = sortObjectByPriority(menus); + var i = 0, len = main.length; + while(i < len) { + var menu = main[i++]; + + // generate an entry at the main menu + + var menuid = NETDATA.name2id('menu_' + menu); + sidebar += '<li class=""><a href="#' + menuid + '" onClick="return scrollToId(\'' + menuid + '\');">' + menus[menu].icon + ' ' + menus[menu].title + '</a><ul class="nav">'; + html += '<div role="section"><div role="sectionhead"><h1 id="' + menuid + '" role="heading">' + menus[menu].title + '</h1></div><div role="document">'; + + if(menus[menu].info !== null) + html += menus[menu].info; + + // console.log(' >> ' + menu + ' (' + menus[menu].priority + '): ' + menus[menu].title); + + var shtml = ''; + var mhead = '<div class="netdata-chart-row">' + mainhead; + mainhead = ''; + + // sort the submenus of this menu + var sub = sortObjectByPriority(menus[menu].submenus); + var si = 0, slen = sub.length; + while(si < slen) { + var submenu = sub[si++]; + + // generate an entry at the submenu + var submenuid = NETDATA.name2id('menu_' + menu + '_submenu_' + submenu); + sidebar += '<li class><a href="#' + submenuid + '" onClick="return scrollToId(\'' + submenuid + '\');">' + menus[menu].submenus[submenu].title + '</a></li>'; + shtml += '<div class="netdata-group-container" id="' + submenuid + '" style="display: inline-block; width: ' + pcent_width.toString() + '%"><h2 id="' + submenuid + '" class="netdata-chart-alignment" role="heading">' + menus[menu].submenus[submenu].title + '</h2>'; + + if(menus[menu].submenus[submenu].info !== null) + shtml += '<div class="chart-message netdata-chart-alignment" role="document">' + menus[menu].submenus[submenu].info + '</div>'; + + var head = '<div class="netdata-chart-row">'; + var chtml = ''; + + // console.log(' \------- ' + submenu + ' (' + menus[menu].submenus[submenu].priority + '): ' + menus[menu].submenus[submenu].title); + + // sort the charts in this submenu of this menu + menus[menu].submenus[submenu].charts.sort(prioritySort); + var ci = 0, clen = menus[menu].submenus[submenu].charts.length; + while(ci < clen) { + var chart = menus[menu].submenus[submenu].charts[ci++]; + + // generate the submenu heading charts + mhead += generateHeadCharts('mainheads', chart, duration); + head += generateHeadCharts('heads', chart, duration); + + // generate the chart + chtml += netdataDashboard.contextInfo(chart.context) + '<div id="chart_' + NETDATA.name2id(chart.id) + '" data-netdata="' + chart.id + '"' + + ' data-width="' + pcent_width.toString() + '%"' + + ' data-height="' + netdataDashboard.contextHeight(chart.context, options.chartsHeight).toString() + 'px"' + + ' data-dygraph-valuerange="' + netdataDashboard.contextValueRange(chart.context) + '"' + + ' data-before="0"' + + ' data-after="-' + duration.toString() + '"' + + ' data-id="' + NETDATA.name2id(options.hostname + '/' + chart.id) + '"' + + ' data-colors="' + netdataDashboard.anyAttribute(netdataDashboard.context, 'colors', chart.context, '') + '"' + + ' role="application"></div>'; + + // console.log(' \------- ' + chart.id + ' (' + chart.priority + '): ' + chart.context + ' height: ' + menus[menu].submenus[submenu].height); + } + + head += '</div>'; + shtml += head + chtml + '</div>'; } - head += '</div>'; - shtml += head + chtml + '</div>'; + mhead += '</div>'; + sidebar += '</ul></li>'; + html += mhead + shtml + '</div></div><hr role="separator"/>'; } - mhead += '</div>'; - sidebar += '</ul></li>'; - html += mhead + shtml + '</div></div><hr role="separator"/>'; + sidebar += '<li class="" style="padding-top:15px;"><a href="https://github.com/firehol/netdata/wiki/Add-more-charts-to-netdata" target="_blank"><i class="fa fa-plus" aria-hidden="true"></i> add more charts</a></li>'; + sidebar += '<li class=""><a href="https://github.com/firehol/netdata/wiki/Add-more-alarms-to-netdata" target="_blank"><i class="fa fa-plus" aria-hidden="true"></i> add more alarms</a></li>'; + sidebar += '<li class="" style="margin:20px;color:#666;"><small>netdata on <b>' + data.hostname.toString() + '</b>, collects every ' + ((data.update_every == 1)?'second':data.update_every.toString() + ' seconds') + ' <b>' + data.dimensions_count.toLocaleString() + '</b> metrics, presented as <b>' + data.charts_count.toLocaleString() + '</b> charts and monitored by <b>' + data.alarms_count.toLocaleString() + '</b> alarms, using ' + Math.round(data.rrd_memory_bytes / 1024 / 1024).toLocaleString() + ' MB of memory for ' + Math.round(data.history / (3600/data.update_every)).toLocaleString() + ' ' + ((data.history == (3600/data.update_every))?'hour':'hours').toString() + ' of real-time history.</small></li>'; + sidebar += '</ul>'; + div.innerHTML = html; + document.getElementById('sidebar').innerHTML = sidebar; + finalizePage(); } - sidebar += '</ul>'; - div.innerHTML = html; - document.getElementById('sidebar').innerHTML = sidebar; - finalizePage(); - } + function renderChartsAndMenu(data) { + var menus = options.menus; + var charts = data.charts; + + for(var c in charts) { + enrichChartData(charts[c]); + + // create the menu + if(typeof menus[charts[c].menu] === 'undefined') { + menus[charts[c].menu] = { + priority: charts[c].priority, + submenus: {}, + title: netdataDashboard.menuTitle(charts[c]), + icon: netdataDashboard.menuIcon(charts[c]), + info: netdataDashboard.menuInfo(charts[c].menu), + height: netdataDashboard.menuHeight(charts[c].menu, options.chartsHeight) + }; + } - function renderChartsAndMenu(data) { - var menus = options.menus; - var charts = data.charts; - - for(var c in charts) { - enrichChartData(charts[c]); - - // create the menu - if(typeof menus[charts[c].menu] === 'undefined') { - menus[charts[c].menu] = { - priority: charts[c].priority, - submenus: {}, - title: netdataDashboard.menuTitle(charts[c]), - icon: netdataDashboard.menuIcon(charts[c]), - info: netdataDashboard.menuInfo(charts[c].menu), - height: netdataDashboard.menuHeight(charts[c].menu, options.chartsHeight) - }; + if(charts[c].priority < menus[charts[c].menu].priority) + menus[charts[c].menu].priority = charts[c].priority; + + // create the submenu + if(typeof menus[charts[c].menu].submenus[charts[c].submenu] === 'undefined') { + menus[charts[c].menu].submenus[charts[c].submenu] = { + priority: charts[c].priority, + charts: new Array(), + title: null, + info: netdataDashboard.submenuInfo(charts[c].menu, charts[c].submenu), + height: netdataDashboard.submenuHeight(charts[c].menu, charts[c].submenu, menus[charts[c].menu].height) + }; + } + + if(charts[c].priority < menus[charts[c].menu].submenus[charts[c].submenu].priority) + menus[charts[c].menu].submenus[charts[c].submenu].priority = charts[c].priority; + + // index the chart in the menu/submenu + menus[charts[c].menu].submenus[charts[c].submenu].charts.push(charts[c]); } - if(charts[c].priority < menus[charts[c].menu].priority) - menus[charts[c].menu].priority = charts[c].priority; - - // create the submenu - if(typeof menus[charts[c].menu].submenus[charts[c].submenu] === 'undefined') { - menus[charts[c].menu].submenus[charts[c].submenu] = { - priority: charts[c].priority, - charts: new Array(), - title: null, - info: netdataDashboard.submenuInfo(charts[c].menu, charts[c].submenu), - height: netdataDashboard.submenuHeight(charts[c].menu, charts[c].submenu, menus[charts[c].menu].height) - }; + // propagate the descriptive subname given to QoS + // to all the other submenus with the same name + for(var m in menus) { + for(var s in menus[m].submenus) { + // set the family using a name + if(typeof options.submenu_names[s] !== 'undefined') { + menus[m].submenus[s].title = s + ' (' + options.submenu_names[s] + ')'; + } + else { + menus[m].submenus[s].title = netdataDashboard.submenuTitle(m, s); + } + } } - if(charts[c].priority < menus[charts[c].menu].submenus[charts[c].submenu].priority) - menus[charts[c].menu].submenus[charts[c].submenu].priority = charts[c].priority; + renderPage(menus, data); + } - // index the chart in the menu/submenu - menus[charts[c].menu].submenus[charts[c].submenu].charts.push(charts[c]); + // ---------------------------------------------------------------------------- + + function loadJs(url, callback) { + $.ajax({ + url: url, + cache: true, + dataType: "script", + xhrFields: { withCredentials: true } // required for the cookie + }) + .fail(function() { + alert('Cannot load required JS library: ' + url); + }) + .always(function() { + if(typeof callback === 'function') + callback(); + }) } - // propagate the descriptive subname given to QoS - // to all the other submenus with the same name - for(var m in menus) { - for(var s in menus[m].submenus) { - // set the family using a name - if(typeof options.submenu_names[s] !== 'undefined') { - menus[m].submenus[s].title = s + ' (' + options.submenu_names[s] + ')'; - } - else { - menus[m].submenus[s].title = netdataDashboard.submenuTitle(m, s); - } + var bootstrapTableLoaded = false; + function loadBootstrapTable(callback) { + if(bootstrapTableLoaded === false) { + bootstrapTableLoaded === true; + loadJs(NETDATA.serverDefault + 'lib/bootstrap-table-1.11.0.min.js', function() { + loadJs(NETDATA.serverDefault + 'lib/bootstrap-table-export-1.11.0.min.js', function() { + loadJs(NETDATA.serverDefault + 'lib/tableExport-1.6.0.min.js', callback); + }) + }); } + else callback(); } - renderPage(menus, data); - } - - // ---------------------------------------------------------------------------- - - function loadJs(url, callback) { - $.ajax({ - url: url, - cache: true, - dataType: "script", - xhrFields: { withCredentials: true } // required for the cookie - }) - .fail(function() { - alert('Cannot load required JS library: ' + url); - }) - .always(function() { - if(typeof callback === 'function') - callback(); - }) - } + 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/firehol/netdata/wiki/Generating-Badges" target="_blank">netdata badges</a> refresh automatically. Their color indicates the state of the alarm: <span style="color: #e05d44"><b> red </b></span> is critical, <span style="color:#fe7d37"><b> orange </b></span> is warning, <span style="color: #4c1"><b> bright green </b></span> is ok, <span style="color: #9f9f9f"><b> light grey </b></span> is undefined (i.e. no data or no status), <span style="color: #000"><b> black </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/firehol/netdata/blob/master/conf.d/health_alarm_notify.conf">this configuration file</a> for more information.'; - var bootstrapTableLoaded = false; - function loadBootstrapTable(callback) { - if(bootstrapTableLoaded === false) { - bootstrapTableLoaded === true; - loadJs(NETDATA.serverDefault + 'lib/bootstrap-table-1.11.0.min.js', function() { - loadJs(NETDATA.serverDefault + 'lib/bootstrap-table-export-1.11.0.min.js', function() { - loadJs(NETDATA.serverDefault + 'lib/tableExport-1.6.0.min.js', callback); - }) - }); - } - else callback(); - } + NETDATA.alarms.get('all', function(data) { + options.alarm_families = new Array(); - 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/firehol/netdata/wiki/Generating-Badges" target="_blank">netdata badges</a> refresh automatically. Their color indicates the state of the alarm: <span style="color: #e05d44"><b> red </b></span> is critical, <span style="color:#fe7d37"><b> orange </b></span> is warning, <span style="color: #4c1"><b> bright green </b></span> is ok, <span style="color: #9f9f9f"><b> light grey </b></span> is undefined (i.e. no data or no status), <span style="color: #000"><b> black </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/firehol/netdata/blob/master/conf.d/health_alarm_notify.conf">this configuration file</a> for more information.'; + alarmsCallback(data); - NETDATA.alarms.get('all', function(data) { - options.alarm_families = new Array(); + if(data === null) { + document.getElementById('alarms_active').innerHTML = + document.getElementById('alarms_all').innerHTML = + document.getElementById('alarms_log').innerHTML = + 'failed to load alarm data!'; + return; + } - alarmsCallback(data); + function alarmid4human(id) { + if(id === 0) + return '-'; - if(data === null) { - document.getElementById('alarms_active').innerHTML = - document.getElementById('alarms_all').innerHTML = - document.getElementById('alarms_log').innerHTML = - 'failed to load alarm data!'; - return; - } - - function timestamp4human(timestamp, space) { - if(typeof space === 'undefined') - space = ' '; + return id.toString(); + } - var t = new Date(timestamp * 1000); - var now = new Date(); + function timestamp4human(timestamp, space) { + if(timestamp === 0) + return '-'; - if(t.toDateString() == now.toDateString()) - return t.toLocaleTimeString(); + if(typeof space === 'undefined') + space = ' '; - return t.toLocaleDateString() + space + t.toLocaleTimeString(); - } + var t = new Date(timestamp * 1000); + var now = new Date(); - function seconds4human(seconds, options) { - var default_options = { - now: 'now', - space: ' ', - negative_suffix: 'ago', - hour: 'hour', - hours: 'hours', - minute: 'minute', - minutes: 'minutes', - second: 'second', - seconds: 'seconds', - and: 'and' - }; + if(t.toDateString() == now.toDateString()) + return t.toLocaleTimeString(); - if(typeof options !== 'object') - options = default_options; - else { - var x; - for(x in default_options) { - if(typeof options[x] !== 'string') - options[x] = default_options[x]; - } + return t.toLocaleDateString() + space + t.toLocaleTimeString(); } - if(typeof seconds === 'string') - seconds = parseInt(seconds); + function seconds4human(seconds, options) { + var default_options = { + now: 'now', + space: ' ', + negative_suffix: 'ago', + hour: 'hour', + hours: 'hours', + minute: 'minute', + minutes: 'minutes', + second: 'second', + seconds: 'seconds', + and: 'and' + }; - if(seconds === 0) - return options.now; + if(typeof options !== 'object') + options = default_options; + else { + var x; + for(x in default_options) { + if(typeof options[x] !== 'string') + options[x] = default_options[x]; + } + } - var suffix = ''; - if(seconds < 0) { - seconds = -seconds; - if(options.negative_suffix !== '') suffix = options.space + options.negative_suffix; - } + if(typeof seconds === 'string') + seconds = parseInt(seconds); - var hours = Math.floor(seconds / 3600); - seconds -= (hours * 3600); + if(seconds === 0) + return options.now; - var minutes = Math.floor(seconds / 60); - seconds -= (minutes * 60); + var suffix = ''; + if(seconds < 0) { + seconds = -seconds; + if(options.negative_suffix !== '') suffix = options.space + options.negative_suffix; + } - var txt = ''; - - if(hours > 1) txt += hours.toString() + options.space + options.hours; - else if(hours === 1) txt += hours.toString() + options.space + options.hour; + var hours = Math.floor(seconds / 3600); + seconds -= (hours * 3600); - if(hours > 0 && minutes > 0 && seconds == 0) - txt += options.space + options.and + options.space; - else if(hours > 0 && minutes > 0 && seconds > 0) - txt += ',' + options.space; + var minutes = Math.floor(seconds / 60); + seconds -= (minutes * 60); - if(minutes > 1) txt += minutes.toString() + options.space + options.minutes; - else if(minutes === 1) txt += minutes.toString() + options.space + options.minute; + var txt = ''; - if((minutes > 0 || minutes > 0) && seconds > 0) - txt += options.space + options.and + options.space; + if(hours > 1) txt += hours.toString() + options.space + options.hours; + else if(hours === 1) txt += hours.toString() + options.space + options.hour; - if(seconds > 1) txt += Math.floor(seconds).toString() + options.space + options.seconds; - else if(seconds === 1) txt += Math.floor(seconds).toString() + options.space + options.second; + if(hours > 0 && minutes > 0 && seconds == 0) + txt += options.space + options.and + options.space; + else if(hours > 0 && minutes > 0 && seconds > 0) + txt += ',' + options.space; - return txt + suffix; - } + if(minutes > 1) txt += minutes.toString() + options.space + options.minutes; + else if(minutes === 1) txt += minutes.toString() + options.space + options.minute; - function alarm_lookup_explain(alarm, chart) { - var dimensions = ' of all values '; + if((minutes > 0 || minutes > 0) && seconds > 0) + txt += options.space + options.and + options.space; - if(chart.dimensions.length > 1) - dimensions = ' of the sum of all dimensions '; + if(seconds > 1) txt += Math.floor(seconds).toString() + options.space + options.seconds; + else if(seconds === 1) txt += Math.floor(seconds).toString() + options.space + options.second; - if(typeof alarm.lookup_dimensions !== 'undefined') { - var d = alarm.lookup_dimensions.replace('|', ','); - var x = d.split(','); - if(x.length > 1) - dimensions = 'of the sum of dimensions <code>' + alarm.lookup_dimensions + '</code> '; - else - dimensions = 'of all values of dimension <code>' + alarm.lookup_dimensions + '</code> '; + return txt + suffix; } - return '<code>' + alarm.lookup_method + '</code> ' - + dimensions + ', of chart <code>' + alarm.chart + '</code>' - + ', starting <code>' + seconds4human(alarm.lookup_after + alarm.lookup_before) + '</code> and up to <code>' + seconds4human(alarm.lookup_before) + '</code>' - + ((alarm.lookup_options)?(', with options <code>' + alarm.lookup_options.replace(' ', ', ') + '</code>'):'') - + '.'; - } + function alarm_lookup_explain(alarm, chart) { + var dimensions = ' of all values '; - function alarm_to_html(alarm, full) { - var chart = options.data.charts[alarm.chart]; + if(chart.dimensions.length > 1) + dimensions = ' of the sum of all dimensions '; - var html = '<tr><td class="text-center" style="vertical-align:middle" width="40%"><b>' + alarm.chart + '</b><br/> <br/><embed src="' + NETDATA.alarms.server + '/api/v1/badge.svg?chart=' + alarm.chart + '&alarm=' + alarm.name + '&refresh=auto" type="image/svg+xml" height="20"/><br/> <br/><span style="font-size: 18px">' + alarm.info + '</span><br/> <br/>role: <b>' + alarm.recipient + '</b><br/> <br/><b><i class="fa fa-line-chart" aria-hidden="true"></i></b><small> <a href="#" onClick="NETDATA.alarms.scrollToChart(\'' + alarm.chart + '\'); $(\'#alarmsModal\').modal(\'hide\'); return false;">jump to chart</a></small></td>' - + '<td><table class="table">' - + ((typeof alarm.warn !== 'undefined')?('<tr><td width="10%" style="text-align:right">warning when</td><td><span style="font-family: monospace; color:#fe7d37; font-weight: bold;">' + alarm.warn + '</span></td></tr>'):'') - + ((typeof alarm.crit !== 'undefined')?('<tr><td width="10%" style="text-align:right">critical when</td><td><span style="font-family: monospace; color: #e05d44; font-weight: bold;">' + alarm.crit + '</span></td></tr>'):''); + if(typeof alarm.lookup_dimensions !== 'undefined') { + var d = alarm.lookup_dimensions.replace('|', ','); + var x = d.split(','); + if(x.length > 1) + dimensions = 'of the sum of dimensions <code>' + alarm.lookup_dimensions + '</code> '; + else + dimensions = 'of all values of dimension <code>' + alarm.lookup_dimensions + '</code> '; + } - if(full === true) { - html += ((typeof alarm.lookup_after !== 'undefined')?('<tr><td width="10%" style="text-align:right">db lookup</td><td>' + alarm_lookup_explain(alarm, chart) + '</td></tr>'):'') - + ((typeof alarm.calc !== 'undefined')?('<tr><td width="10%" style="text-align:right">calculation</td><td><span style="font-family: monospace;">' + alarm.calc + '</span></td></tr>'):'') - + ((chart.green !== null)?('<tr><td width="10%" style="text-align:right">green threshold</td><td><code>' + chart.green + ' ' + chart.units + '</code></td></tr>'):'') - + ((chart.red !== null)?('<tr><td width="10%" style="text-align:right">red threshold</td><td><code>' + chart.red + ' ' + chart.units + '</code></td></tr>'):''); + return '<code>' + alarm.lookup_method + '</code> ' + + dimensions + ', of chart <code>' + alarm.chart + '</code>' + + ', starting <code>' + seconds4human(alarm.lookup_after + alarm.lookup_before) + '</code> and up to <code>' + seconds4human(alarm.lookup_before) + '</code>' + + ((alarm.lookup_options)?(', with options <code>' + alarm.lookup_options.replace(' ', ', ') + '</code>'):'') + + '.'; } - 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) { - delay += '<small><br/>hysteresis ' + seconds4human(alarm.delay_up_duration, { negative_suffix: '' }); + function alarm_to_html(alarm, full) { + var chart = options.data.charts[alarm.chart]; + var has_alarm = ((typeof alarm.warn !== 'undefined' || typeof alarm.crit !== 'undefined')?true:false); + + var role_href = ((has_alarm === true)?('<br/> <br/>role: <b>' + alarm.recipient + '</b><br/> <br/><b><i class="fa fa-line-chart" aria-hidden="true"></i></b><small> <a href="#" onClick="NETDATA.alarms.scrollToChart(\'' + alarm.chart + '\'); $(\'#alarmsModal\').modal(\'hide\'); return false;">jump to chart</a></small>'):(' ')); + + var html = '<tr><td class="text-center" style="vertical-align:middle" width="40%"><b>' + alarm.chart + '</b><br/> <br/><embed src="' + NETDATA.alarms.server + '/api/v1/badge.svg?chart=' + alarm.chart + '&alarm=' + alarm.name + '&refresh=auto" type="image/svg+xml" height="20"/><br/> <br/><span style="font-size: 18px">' + alarm.info + '</span>' + role_href + '</td>' + + '<td><table class="table">' + + ((typeof alarm.warn !== 'undefined')?('<tr><td width="10%" style="text-align:right">warning when</td><td><span style="font-family: monospace; color:#fe7d37; font-weight: bold;">' + alarm.warn + '</span></td></tr>'):'') + + ((typeof alarm.crit !== 'undefined')?('<tr><td width="10%" style="text-align:right">critical when</td><td><span style="font-family: monospace; color: #e05d44; font-weight: bold;">' + alarm.crit + '</span></td></tr>'):''); + + if(full === true) { + html += ((typeof alarm.lookup_after !== 'undefined')?('<tr><td width="10%" style="text-align:right">db lookup</td><td>' + alarm_lookup_explain(alarm, chart) + '</td></tr>'):'') + + ((typeof alarm.calc !== 'undefined')?('<tr><td width="10%" style="text-align:right">calculation</td><td><span style="font-family: monospace;">' + alarm.calc + '</span></td></tr>'):'') + + ((chart.green !== null)?('<tr><td width="10%" style="text-align:right">green threshold</td><td><code>' + chart.green + ' ' + chart.units + '</code></td></tr>'):'') + + ((chart.red !== null)?('<tr><td width="10%" style="text-align:right">red threshold</td><td><code>' + chart.red + ' ' + chart.units + '</code></td></tr>'):''); } - else { - delay = '<small><br/>hysteresis '; - if(alarm.delay_up_duration > 0) { - delay += 'on escalation <code>' + seconds4human(alarm.delay_up_duration, { negative_suffix: '' }) + '</code>, '; + + 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) { + delay += '<small><br/>hysteresis ' + seconds4human(alarm.delay_up_duration, { negative_suffix: '' }); } - if(alarm.delay_down_duration > 0) { - delay += 'on recovery <code>' + seconds4human(alarm.delay_down_duration, { negative_suffix: '' }) + '</code>, '; + else { + delay = '<small><br/>hysteresis '; + if(alarm.delay_up_duration > 0) { + delay += 'on escalation <code>' + seconds4human(alarm.delay_up_duration, { negative_suffix: '' }) + '</code>, '; + } + if(alarm.delay_down_duration > 0) { + delay += 'on recovery <code>' + seconds4human(alarm.delay_down_duration, { negative_suffix: '' }) + '</code>, '; + } } + if(alarm.delay_multiplier != 1.0) { + delay += 'multiplied by <code>' + alarm.delay_multiplier.toString() + '</code>'; + delay += ', up to <code>' + seconds4human(alarm.delay_max_duration, { negative_suffix: '' }) + '</code>'; + } + delay += '</small>'; } - if(alarm.delay_multiplier != 1.0) { - delay += 'multiplied by <code>' + alarm.delay_multiplier.toString() + '</code>'; - delay += ', up to <code>' + seconds4human(alarm.delay_max_duration, { negative_suffix: '' }) + '</code>'; - } - delay += '</small>'; - } - - html += '<tr><td width="10%" style="text-align:right">check every</td><td>' + seconds4human(alarm.update_every, { negative_suffix: '' }) + '</td></tr>' - + '<tr><td width="10%" style="text-align:right">execute</td><td><span style="font-family: monospace;">' + alarm.exec + '</span>' + delay + '</td></tr>' - + '<tr><td width="10%" style="text-align:right">source</td><td><span style="font-family: monospace;">' + alarm.source + '</span></td></tr>' - + '</table></td></tr>'; - return html; - } + html += '<tr><td width="10%" style="text-align:right">check every</td><td>' + seconds4human(alarm.update_every, { negative_suffix: '' }) + '</td></tr>' + + ((has_alarm === true)?('<tr><td width="10%" style="text-align:right">execute</td><td><span style="font-family: monospace;">' + alarm.exec + '</span>' + delay + '</td></tr>'):'') + + '<tr><td width="10%" style="text-align:right">source</td><td><span style="font-family: monospace;">' + alarm.source + '</span></td></tr>' + + '</table></td></tr>'; - function alarm_family_show(id) { - var html = '<table class="table">'; - var family = options.alarm_families[id]; - var len = family.arr.length; - while(len--) { - var alarm = family.arr[len]; - html += alarm_to_html(alarm, true); + return html; } - html += '</table>'; - - $('#alarm_all_' + id.toString()).html(html); - } - // find the proper family of each alarm - var now = new Date().getTime(); - var x; - var count_active = 0; - var count_all = 0; - var families = {}; - var families_sort = new Array(); - for(x in data.alarms) { - var alarm = data.alarms[x]; - var family = alarm.family; - - // find the chart - var chart = options.data.charts[alarm.chart]; - if(typeof chart === 'undefined') - chart = options.data.charts_by_name[alarm.chart]; + function alarm_family_show(id) { + var html = '<table class="table">'; + var family = options.alarm_families[id]; + var len = family.arr.length; + while(len--) { + var alarm = family.arr[len]; + html += alarm_to_html(alarm, true); + } + html += '</table>'; - // not found - this should never happen! - if(typeof chart === 'undefined') { - console.log('WARNING: alarm ' + x + ' is linked to chart ' + alarm.chart + ', which is not found in the list of chart got from the server.'); - chart = { priority: 9999999 }; + $('#alarm_all_' + id.toString()).html(html); } - else if(typeof chart.menu !== 'undefined' && typeof chart.submenu !== 'undefined') - // the family based on the chart - family = chart.menu + ' - ' + chart.submenu; - - if(typeof families[family] === 'undefined') { - families[family] = { - name: family, - arr: new Array(), - priority: chart.priority - }; - families_sort.push(families[family]); - } + // find the proper family of each alarm + var now = Date.now(); + var x; + var count_active = 0; + var count_all = 0; + var families = {}; + var families_sort = new Array(); + for(x in data.alarms) { + var alarm = data.alarms[x]; + var family = alarm.family; + + // find the chart + var chart = options.data.charts[alarm.chart]; + if(typeof chart === 'undefined') + chart = options.data.charts_by_name[alarm.chart]; + + // not found - this should never happen! + if(typeof chart === 'undefined') { + console.log('WARNING: alarm ' + x + ' is linked to chart ' + alarm.chart + ', which is not found in the list of chart got from the server.'); + chart = { priority: 9999999 }; + } + else if(typeof chart.menu !== 'undefined' && typeof chart.submenu !== 'undefined') + // the family based on the chart + family = chart.menu + ' - ' + chart.submenu; + + if(typeof families[family] === 'undefined') { + families[family] = { + name: family, + arr: new Array(), + priority: chart.priority + }; + + families_sort.push(families[family]); + } - if(chart.priority < families[family].priority) - families[family].priority = chart.priority; + if(chart.priority < families[family].priority) + families[family].priority = chart.priority; - families[family].arr.unshift(alarm); - } + families[family].arr.unshift(alarm); + } - // sort the families, like the dashboard menu does - var families_sorted = families_sort.sort(function (a, b) { - if (a.priority > b.priority) return -1; - if (a.priority < b.priority) return 1; - return 0; - }); + // sort the families, like the dashboard menu does + var families_sorted = families_sort.sort(function (a, b) { + if (a.priority > b.priority) return -1; + if (a.priority < b.priority) return 1; + return 0; + }); - var fc = 0; - var len = families_sorted.length; - while(len--) { - var family = families_sorted[len].name; - var active_family_added = false; - var expanded = 'true'; - var collapsed = ''; - var cin = 'in'; - - if(fc !== 0) { - all += "</table></div></div></div>"; - expanded = 'false'; - collapsed = 'class="collapsed"' - cin = ''; - } + var fc = 0; + var len = families_sorted.length; + while(len--) { + var family = families_sorted[len].name; + var active_family_added = false; + var expanded = 'true'; + var collapsed = ''; + var cin = 'in'; + + if(fc !== 0) { + all += "</table></div></div></div>"; + expanded = 'false'; + collapsed = 'class="collapsed"' + cin = ''; + } - all += '<div class="panel panel-default"><div class="panel-heading" role="tab" id="alarm_all_heading_' + fc.toString() + '"><h4 class="panel-title"><a ' + collapsed + ' role="button" data-toggle="collapse" data-parent="#alarms_all_accordion" href="#alarm_all_' + fc.toString() + '" aria-expanded="' + expanded + '" aria-controls="alarm_all_' + fc.toString() + '">' + family.toString() + '</a></h4></div><div id="alarm_all_' + fc.toString() + '" class="panel-collapse collapse ' + cin + '" role="tabpanel" aria-labelledby="alarm_all_heading_' + fc.toString() + '" data-alarm-id="' + fc.toString() + '"><div class="panel-body" id="alarm_all_body_' + fc.toString() + '">'; + all += '<div class="panel panel-default"><div class="panel-heading" role="tab" id="alarm_all_heading_' + fc.toString() + '"><h4 class="panel-title"><a ' + collapsed + ' role="button" data-toggle="collapse" data-parent="#alarms_all_accordion" href="#alarm_all_' + fc.toString() + '" aria-expanded="' + expanded + '" aria-controls="alarm_all_' + fc.toString() + '">' + family.toString() + '</a></h4></div><div id="alarm_all_' + fc.toString() + '" class="panel-collapse collapse ' + cin + '" role="tabpanel" aria-labelledby="alarm_all_heading_' + fc.toString() + '" data-alarm-id="' + fc.toString() + '"><div class="panel-body" id="alarm_all_body_' + fc.toString() + '">'; - options.alarm_families[fc] = families[family]; + options.alarm_families[fc] = families[family]; - fc++; + fc++; - var arr = families[family].arr; - var c = arr.length; - while(c--) { - var alarm = arr[c]; - if(alarm.status === 'WARNING' || alarm.status === 'CRITICAL') { - if(!active_family_added) { - active_family_added = true; - active += '<tr><th class="text-center" colspan="2"><h4>' + family + '</h4></th></tr>'; + var arr = families[family].arr; + var c = arr.length; + while(c--) { + var alarm = arr[c]; + if(alarm.status === 'WARNING' || alarm.status === 'CRITICAL') { + if(!active_family_added) { + active_family_added = true; + active += '<tr><th class="text-center" colspan="2"><h4>' + family + '</h4></th></tr>'; + } + count_active++; + active += alarm_to_html(alarm, true); } - count_active++; - active += alarm_to_html(alarm, true); - } - count_all++; + count_all++; + } } - } - active += "</table>"; - if(families_sorted.length > 0) all += "</div></div></div>"; - all += "</div>"; + active += "</table>"; + if(families_sorted.length > 0) all += "</div></div></div>"; + all += "</div>"; - if(!count_active) - active += "<h4>Everything is normal. No raised alarms.</h4>"; - else - active += footer; + if(!count_active) + active += "<h4>Everything is normal. No raised alarms.</h4>"; + else + active += footer; - if(!count_all) - all += "<h4>No alarms are running in this system.</h4>"; - else - all += footer; + if(!count_all) + all += "<h4>No alarms are running in this system.</h4>"; + else + all += footer; - document.getElementById('alarms_active').innerHTML = active; - document.getElementById('alarms_all').innerHTML = all; + document.getElementById('alarms_active').innerHTML = active; + document.getElementById('alarms_all').innerHTML = all; - if(families_sorted.length > 0) alarm_family_show(0); + if(families_sorted.length > 0) alarm_family_show(0); - // register bootstrap events - $('#alarms_all_accordion').on('show.bs.collapse', function (d) { - var target = $(d.target); - var id = $(target).data('alarm-id'); - alarm_family_show(id); - }); - $('#alarms_all_accordion').on('hidden.bs.collapse', function (d) { - var target = $(d.target); - var id = $(target).data('alarm-id'); - $('#alarm_all_' + id.toString()).html(''); - }); + // register bootstrap events + $('#alarms_all_accordion').on('show.bs.collapse', function (d) { + var target = $(d.target); + var id = $(target).data('alarm-id'); + alarm_family_show(id); + }); + $('#alarms_all_accordion').on('hidden.bs.collapse', function (d) { + var target = $(d.target); + var id = $(target).data('alarm-id'); + $('#alarm_all_' + id.toString()).html(''); + }); - document.getElementById('alarms_log').innerHTML = '<h3>Alarm Log</h3><table id="alarms_log_table"></table>'; - - loadBootstrapTable(function () { - $('#alarms_log_table').bootstrapTable({ - url: NETDATA.alarms.server + '/api/v1/alarm_log?all', - cache: false, - pagination: true, - pageSize: 10, - showPaginationSwitch: false, - search: true, - searchTimeOut: 300, - searchAlign: 'left', - showColumns: true, - showExport: true, - exportDataType: 'basic', - exportOptions: { - fileName: 'netdata_alarm_log' - }, - rowStyle: function(row, index) { - switch(row.status) { - case 'CRITICAL' : return { classes: 'danger' }; break; - case 'WARNING' : return { classes: 'warning' }; break; - case 'UNDEFINED': return { classes: 'info' }; break; - case 'CLEAR' : return { classes: 'success' }; break; - } - return {}; - }, - showFooter: false, - showHeader: true, - showRefresh: true, - showToggle: false, - sortable: true, - silentSort: false, - columns: [ - { - field: 'when', - title: 'Event Date', - valign: 'middle', - titleTooltip: 'The date and time the even took place', - switchable: false, - sortable: true, - formatter: function(value, row, index) { return timestamp4human(value, ' '); }, - }, - { - field: 'hostname', - title: 'Host', - valign: 'middle', - titleTooltip: 'The host that generated this event', - visible: false, - sortable: true + document.getElementById('alarms_log').innerHTML = '<h3>Alarm Log</h3><table id="alarms_log_table"></table>'; + + loadBootstrapTable(function () { + $('#alarms_log_table').bootstrapTable({ + url: NETDATA.alarms.server + '/api/v1/alarm_log?all', + cache: false, + pagination: true, + pageSize: 10, + showPaginationSwitch: false, + search: true, + searchTimeOut: 300, + searchAlign: 'left', + showColumns: true, + showExport: true, + exportDataType: 'basic', + exportOptions: { + fileName: 'netdata_alarm_log' }, - { - field: 'unique_id', - title: 'Unique ID', - titleTooltip: 'The host unique ID for this event', - valign: 'middle', - visible: false, - sortable: true - }, - { - field: 'alarm_id', - title: 'Alarm ID', - titleTooltip: 'The ID of the alarm that generated this event', - valign: 'middle', - visible: false, - sortable: true - }, - { - field: 'alarm_event_id', - title: 'Alarm Event ID', - titleTooltip: 'The incremental ID of this event for the given alarm', - valign: 'middle', - visible: false, - sortable: true - }, - { - field: 'chart', - title: 'Chart', - titleTooltip: 'The chart the alarm is attached to', - valign: 'middle', - switchable: false, - sortable: true - }, - { - field: 'family', - title: 'Family', - titleTooltip: 'The family of the chart the alarm is attached to', - valign: 'middle', - visible: false, - sortable: true + rowStyle: function(row, index) { + switch(row.status) { + case 'CRITICAL' : return { classes: 'danger' }; break; + case 'WARNING' : return { classes: 'warning' }; break; + case 'UNDEFINED': return { classes: 'info' }; break; + case 'CLEAR' : return { classes: 'success' }; break; + } + return {}; }, - { - field: 'name', - title: 'Alarm', - titleTooltip: 'The alarm name that generated this event', - formatter: function(value, row, index) { - return value.toString().replace(/_/g, ' '); + showFooter: false, + showHeader: true, + showRefresh: true, + showToggle: false, + sortable: true, + silentSort: false, + columns: [ + { + field: 'when', + title: 'Event Date', + valign: 'middle', + titleTooltip: 'The date and time the even took place', + formatter: function(value, row, index) { return timestamp4human(value, ' '); }, + align: 'center', + valign: 'middle', + switchable: false, + sortable: true }, - valign: 'middle', - switchable: false, - sortable: true - }, - { - field: 'old_value', - title: 'Old Value', - titleTooltip: 'The value of the alarm, just before this event', - valign: 'middle', - visible: false, - sortable: true - }, - { - field: 'value', - title: 'Value', - titleTooltip: 'The value of the alarm, that triggered this event', - formatter: function(value, row, index) { - return ((value !== null)?Math.floor(value):'NaN').toString(); + { + field: 'hostname', + title: 'Host', + valign: 'middle', + titleTooltip: 'The host that generated this event', + align: 'center', + valign: 'middle', + visible: false, + sortable: true }, - align: 'right', - valign: 'middle', - sortable: true - }, - { - field: 'units', - title: 'Units', - titleTooltip: 'The units of the value of the alarm', - valign: 'middle', - sortable: true - }, - { - field: 'old_status', - title: 'Old Status', - titleTooltip: 'The status of the alarm, just before this event', - valign: 'middle', - visible: false, - sortable: true - }, - { - field: 'status', - title: 'Status', - titleTooltip: 'The status of the alarm, that was set due to this event', - valign: 'middle', - switchable: false, - sortable: true - }, - { - field: 'duration', - title: 'Last Duration', - titleTooltip: 'The duration the alarm was at its previous state, just before this event', - formatter: function(value, row, index) { return seconds4human(value, { negative_suffix: '', space: ' ', now: 'no time' }); }, - valign: 'middle', - visible: false, - sortable: true - }, - { - field: 'non_clear_duration', - title: 'Raised Duration', - titleTooltip: 'The duration the alarm was raised, just before this event', - formatter: function(value, row, index) { return seconds4human(value, { negative_suffix: '', space: ' ', now: 'no time' }); }, - valign: 'middle', - visible: false, - sortable: true - }, - { - field: 'recipient', - title: 'Recipient', - titleTooltip: 'The recipient of this event', - valign: 'middle', - visible: false, - sortable: true - }, - { - field: 'processed', - title: 'Processed Status', - titleTooltip: 'True when this event is processed', - valign: 'middle', - visible: false, - sortable: true - }, - { - field: 'updated', - title: 'Updated Status', - titleTooltip: 'True when this event has been updated by another event', - valign: 'middle', - visible: false, - sortable: true - }, - { - field: 'updated_by_id', - title: 'Updated By ID', - titleTooltip: 'The unique ID of the event that obsoleted this one', - valign: 'middle', - visible: false, - sortable: true - }, - { - field: 'updates_id', - title: 'Updates ID', - titleTooltip: 'The unique ID of the event obsoleted because of this event', - valign: 'middle', - visible: false, - sortable: true - }, - { - field: 'exec', - title: 'Script', - titleTooltip: 'The script to handle the event notification', - valign: 'middle', - visible: false, - sortable: true - }, - { - field: 'exec_run', - title: 'Script Run At', - titleTooltip: 'The date and time the script has been ran', - formatter: function(value, row, index) { return timestamp4human(value, ' '); }, - valign: 'middle', - visible: false, - sortable: true - }, - { - field: 'exec_code', - title: 'Script Return Value', - titleTooltip: 'The return code of the script', - valign: 'middle', - visible: false, - sortable: true - }, - { - field: 'delay', - title: 'Script Delay', - titleTooltip: 'The hysteresis of the notification', - formatter: function(value, row, index) { return seconds4human(value, { negative_suffix: '', space: ' ', now: 'no time' }); }, - valign: 'middle', - visible: false, - sortable: true - }, - { - field: 'delay_up_to_timestamp', - title: 'Script Delay Run At', - titleTooltip: 'The date and time the script should be run, after hysteresis', - formatter: function(value, row, index) { return timestamp4human(value, ' '); }, - valign: 'middle', - visible: false, - sortable: true - }, - { - field: 'info', - title: 'Description', - titleTooltip: 'A short description of the alarm', - valign: 'middle', - visible: false, - sortable: true - }, - { - field: 'source', - title: 'Alarm Source', - titleTooltip: 'The source of configuration of the alarm', - valign: 'middle', - visible: false, - sortable: true - } - ] + { + field: 'unique_id', + title: 'Unique ID', + titleTooltip: 'The host unique ID for this event', + formatter: function(value, row, index) { return alarmid4human(value); }, + align: 'center', + valign: 'middle', + visible: false, + sortable: true + }, + { + field: 'alarm_id', + title: 'Alarm ID', + titleTooltip: 'The ID of the alarm that generated this event', + formatter: function(value, row, index) { return alarmid4human(value); }, + align: 'center', + valign: 'middle', + visible: false, + sortable: true + }, + { + field: 'alarm_event_id', + title: 'Alarm Event ID', + titleTooltip: 'The incremental ID of this event for the given alarm', + formatter: function(value, row, index) { return alarmid4human(value); }, + align: 'center', + valign: 'middle', + visible: false, + sortable: true + }, + { + field: 'chart', + title: 'Chart', + titleTooltip: 'The chart the alarm is attached to', + align: 'center', + valign: 'middle', + switchable: false, + sortable: true + }, + { + field: 'family', + title: 'Family', + titleTooltip: 'The family of the chart the alarm is attached to', + align: 'center', + valign: 'middle', + visible: false, + sortable: true + }, + { + field: 'name', + title: 'Alarm', + titleTooltip: 'The alarm name that generated this event', + formatter: function(value, row, index) { + return value.toString().replace(/_/g, ' '); + }, + align: 'center', + valign: 'middle', + switchable: false, + sortable: true + }, + { + field: 'old_value', + title: 'Old Value', + titleTooltip: 'The value of the alarm, just before this event', + formatter: function(value, row, index) { + return ((value !== null)?Math.round(value * 100) / 100:'NaN').toString(); + }, + align: 'center', + valign: 'middle', + visible: false, + sortable: true + }, + { + field: 'value', + title: 'Value', + titleTooltip: 'The value of the alarm, that triggered this event', + formatter: function(value, row, index) { + return ((value !== null)?Math.round(value * 100) / 100:'NaN').toString(); + }, + align: 'right', + valign: 'middle', + sortable: true + }, + { + field: 'units', + title: 'Units', + titleTooltip: 'The units of the value of the alarm', + align: 'left', + valign: 'middle', + sortable: true + }, + { + field: 'old_status', + title: 'Old Status', + titleTooltip: 'The status of the alarm, just before this event', + align: 'center', + valign: 'middle', + visible: false, + sortable: true + }, + { + field: 'status', + title: 'Status', + titleTooltip: 'The status of the alarm, that was set due to this event', + align: 'center', + valign: 'middle', + switchable: false, + sortable: true + }, + { + field: 'duration', + title: 'Last Duration', + titleTooltip: 'The duration the alarm was at its previous state, just before this event', + formatter: function(value, row, index) { return seconds4human(value, { negative_suffix: '', space: ' ', now: 'no time' }); }, + align: 'center', + valign: 'middle', + visible: false, + sortable: true + }, + { + field: 'non_clear_duration', + title: 'Raised Duration', + titleTooltip: 'The duration the alarm was raised, just before this event', + formatter: function(value, row, index) { return seconds4human(value, { negative_suffix: '', space: ' ', now: 'no time' }); }, + align: 'center', + valign: 'middle', + visible: false, + sortable: true + }, + { + field: 'recipient', + title: 'Recipient', + titleTooltip: 'The recipient of this event', + align: 'center', + valign: 'middle', + visible: false, + sortable: true + }, + { + field: 'processed', + title: 'Processed Status', + titleTooltip: 'True when this event is processed', + formatter: function(value, row, index) { + if(value === true) + return 'DONE'; + else + return 'PENDING'; + }, + align: 'center', + valign: 'middle', + visible: false, + sortable: true + }, + { + field: 'updated', + title: 'Updated Status', + titleTooltip: 'True when this event has been updated by another event', + formatter: function(value, row, index) { + if(value === true) + return 'UPDATED'; + else + return 'CURRENT'; + }, + align: 'center', + valign: 'middle', + visible: false, + sortable: true + }, + { + field: 'updated_by_id', + title: 'Updated By ID', + titleTooltip: 'The unique ID of the event that obsoleted this one', + formatter: function(value, row, index) { return alarmid4human(value); }, + align: 'center', + valign: 'middle', + visible: false, + sortable: true + }, + { + field: 'updates_id', + title: 'Updates ID', + titleTooltip: 'The unique ID of the event obsoleted because of this event', + formatter: function(value, row, index) { return alarmid4human(value); }, + align: 'center', + valign: 'middle', + visible: false, + sortable: true + }, + { + field: 'exec', + title: 'Script', + titleTooltip: 'The script to handle the event notification', + align: 'center', + valign: 'middle', + visible: false, + sortable: true + }, + { + field: 'exec_run', + title: 'Script Run At', + titleTooltip: 'The date and time the script has been ran', + formatter: function(value, row, index) { return timestamp4human(value, ' '); }, + align: 'center', + valign: 'middle', + visible: false, + sortable: true + }, + { + field: 'exec_code', + title: 'Script Return Value', + titleTooltip: 'The return code of the script', + formatter: function(value, row, index) { + if(value === 0) + return 'OK (returned 0)'; + else + return 'FAILED (with code ' + value.toString() + ')'; + }, + align: 'center', + valign: 'middle', + visible: false, + sortable: true + }, + { + field: 'delay', + title: 'Script Delay', + titleTooltip: 'The hysteresis of the notification', + formatter: function(value, row, index) { return seconds4human(value, { negative_suffix: '', space: ' ', now: 'no time' }); }, + align: 'center', + valign: 'middle', + visible: false, + sortable: true + }, + { + field: 'delay_up_to_timestamp', + title: 'Script Delay Run At', + titleTooltip: 'The date and time the script should be run, after hysteresis', + formatter: function(value, row, index) { return timestamp4human(value, ' '); }, + align: 'center', + valign: 'middle', + visible: false, + sortable: true + }, + { + field: 'info', + title: 'Description', + titleTooltip: 'A short description of the alarm', + align: 'center', + valign: 'middle', + visible: false, + sortable: true + }, + { + field: 'source', + title: 'Alarm Source', + titleTooltip: 'The source of configuration of the alarm', + align: 'center', + valign: 'middle', + visible: false, + sortable: true + } + ] + }); + // console.log($('#alarms_log_table').bootstrapTable('getOptions')); }); - // console.log($('#alarms_log_table').bootstrapTable('getOptions')); }); - }); - } + } - function alarmsCallback(data) { - var count = 0; - for(x in data.alarms) { - var alarm = data.alarms[x]; - if(alarm.status === 'WARNING' || alarm.status === 'CRITICAL') - count++; + function alarmsCallback(data) { + var count = 0; + for(x in data.alarms) { + var alarm = data.alarms[x]; + if(alarm.status === 'WARNING' || alarm.status === 'CRITICAL') + count++; + } + + if(count > 0) + document.getElementById('alarms_count_badge').innerHTML = count.toString(); + else + document.getElementById('alarms_count_badge').innerHTML = ''; } - if(count > 0) - document.getElementById('alarms_count_badge').innerHTML = count.toString(); - else - document.getElementById('alarms_count_badge').innerHTML = ''; - } + function initializeDynamicDashboard(netdata_url) { + if(typeof netdata_url === 'undefined' || netdata_url === null) + netdata_url = NETDATA.serverDefault; + + // initialize clickable alarms + NETDATA.alarms.chart_div_offset = 100; + NETDATA.alarms.chart_div_id_prefix = 'chart_'; + NETDATA.alarms.chart_div_animation_duration = 0; + + NETDATA.pause(function() { + NETDATA.alarms.callback = alarmsCallback; + + // download all the charts the server knows + NETDATA.chartRegistry.downloadAll(netdata_url, function(data) { + if(data !== null) { + options.hostname = data.hostname; + options.data = data; + + // update the dashboard hostname + document.getElementById('hostname').innerHTML = options.hostname; + document.getElementById('hostname').href = NETDATA.serverDefault; + + // update the dashboard title + document.title = options.hostname + ' netdata dashboard'; + + // close the splash screen + $("#loadOverlay").css("display","none"); + + // create a chart_by_name index + data.charts_by_name = {}; + var charts = data.charts; + var x; + for(x in charts) { + var chart = charts[x]; + data.charts_by_name[chart.name] = chart; + } - function initializeDynamicDashboard(netdata_url) { - if(typeof netdata_url === 'undefined' || netdata_url === null) - netdata_url = NETDATA.serverDefault; - - // initialize clickable alarms - NETDATA.alarms.chart_div_offset = 100; - NETDATA.alarms.chart_div_id_prefix = 'chart_'; - NETDATA.alarms.chart_div_animation_duration = 0; - - NETDATA.pause(function() { - NETDATA.alarms.callback = alarmsCallback; - - // download all the charts the server knows - NETDATA.chartRegistry.downloadAll(netdata_url, function(data) { - if(data !== null) { - options.hostname = data.hostname; - options.data = data; - - // update the dashboard hostname - document.getElementById('hostname').innerHTML = options.hostname; - document.getElementById('hostname').href = NETDATA.serverDefault; - - // update the dashboard title - document.title = options.hostname + ' netdata dashboard'; - - // close the splash screen - $("#loadOverlay").css("display","none"); - - // create a chart_by_name index - data.charts_by_name = {}; - var charts = data.charts; - var x; - for(x in charts) { - var chart = charts[x]; - data.charts_by_name[chart.name] = chart; + // render all charts + renderChartsAndMenu(data); } - - // render all charts - renderChartsAndMenu(data); - } + }); }); - }); - } + } - // ---------------------------------------------------------------------------- + // ---------------------------------------------------------------------------- - function versionLog(msg) { - document.getElementById('versionCheckLog').innerHTML = msg; - } + function versionLog(msg) { + document.getElementById('versionCheckLog').innerHTML = msg; + } - function getNetdataVersion(callback) { - versionLog('Downloading installed version info from netdata...'); - - $.ajax({ - url: 'version.txt', - async: true, - cache: false, - xhrFields: { withCredentials: true } // required for the cookie - }) - .done(function(data) { - data = data.replace(/(\r\n|\n|\r| |\t)/gm,""); - if(data.length !== 40) { - versionLog('Received version string is invalid.'); + function getNetdataVersion(callback) { + versionLog('Downloading installed version info from netdata...'); + + $.ajax({ + url: 'version.txt', + async: true, + cache: false, + xhrFields: { withCredentials: true } // required for the cookie + }) + .done(function(data) { + data = data.replace(/(\r\n|\n|\r| |\t)/gm,""); + if(data.length !== 40) { + versionLog('Received version string is invalid.'); + callback(null); + } + else { + versionLog('Installed version of netdata is ' + data); + document.getElementById('netdataVersion').innerHTML = data; + callback(data); + } + }) + .fail(function() { + versionLog('Failed to download installed version info from netdata!'); callback(null); - } - else { - versionLog('Installed version of netdata is ' + data); - document.getElementById('netdataVersion').innerHTML = data; - callback(data); - } - }) - .fail(function() { - versionLog('Failed to download installed version info from netdata!'); - callback(null); - }); - } - - function getGithubLatestCommit(callback) { - versionLog('Downloading latest version info from github...'); - - $.ajax({ - url: 'https://api.github.com/repos/firehol/netdata/commits', - async: true, - cache: false - }) - .done(function(data) { - versionLog('Latest version info from github is ' + data[0].sha); - callback(data[0].sha); - }) - .fail(function() { - versionLog('Failed to download installed version info from github!'); - callback(null); - }); - } - - function checkForUpdate(callback) { - getNetdataVersion(function(sha1) { - if(sha1 === null) callback(null, null); + }); + } - getGithubLatestCommit(function(sha2) { - callback(sha1, sha2); + function getGithubLatestCommit(callback) { + versionLog('Downloading latest version info from github...'); + + $.ajax({ + url: 'https://api.github.com/repos/firehol/netdata/commits', + async: true, + cache: false + }) + .done(function(data) { + versionLog('Latest version info from github is ' + data[0].sha); + callback(data[0].sha); + }) + .fail(function() { + versionLog('Failed to download installed version info from github!'); + callback(null); }); - }); + } - return null; - } + function checkForUpdate(callback) { + getNetdataVersion(function(sha1) { + if(sha1 === null) callback(null, null); - function notifyForUpdate(force) { - versionLog('<p>checking for updates...</p>'); + getGithubLatestCommit(function(sha2) { + callback(sha1, sha2); + }); + }); - var now = new Date().getTime(); + return null; + } - if(typeof force === 'undefined' || force !== true) { - var last = loadLocalStorage('last_update_check'); + function notifyForUpdate(force) { + versionLog('<p>checking for updates...</p>'); - if(typeof last === 'string') - last = parseInt(last); - else - last = 0; + var now = Date.now(); - if(now - last < 3600000 * 8) { - // no need to check it - too soon - return; - } - } + if(typeof force === 'undefined' || force !== true) { + var last = loadLocalStorage('last_update_check'); - checkForUpdate(function(sha1, sha2) { - var save = false; + if(typeof last === 'string') + last = parseInt(last); + else + last = 0; - if(sha1 === null) { - save = false; - versionLog('<p><big>Failed to get your netdata version!</big></p><p>You can always get the latest version of netdata from <a href="https://github.com/firehol/netdata" target="_blank">its github page</a>.</p>'); - } - else if(sha2 === null) { - save = false; - versionLog('<p><big>Failed to get the latest version from github.</big></p><p>You can always get the latest version of netdata from <a href="https://github.com/firehol/netdata" target="_blank">its github page</a>.</p>'); - } - else if(sha1 === sha2) { - save = true; - versionLog('<p><big>You already have the latest version of netdata!</big></p><p>No update yet?<br/>Probably, we need some motivation to keep going on!</p><p>If you haven\'t already, <a href="https://github.com/firehol/netdata" target="_blank">give netdata a <b>Star</b> at its github page</a>.</p>'); + if(now - last < 3600000 * 8) { + // no need to check it - too soon + return; + } } - else { - save = true; - var compare = 'https://github.com/firehol/netdata/compare/' + sha1.toString() + '...' + sha2.toString(); - versionLog('<p><big><strong>New version of netdata available!</strong></big></p><p>Latest version: ' + sha2.toString() + '</p><p><a href="' + compare + '" target="_blank">Click here for the changes log</a> since your installed version, and<br/><a href="https://github.com/firehol/netdata/wiki/Updating-Netdata" target="_blank">click here for directions on updating</a> your netdata installation.</p><p>We suggest to review the changes log for new features you may be interested, or important bug fixes you may need.<br/>Keeping your netdata updated, is generally a good idea.</p>'); + checkForUpdate(function(sha1, sha2) { + var save = false; - document.getElementById('update_badge').innerHTML = '!'; - } - - if(save) - saveLocalStorage('last_update_check', now.toString()); - }); - } + if(sha1 === null) { + save = false; + versionLog('<p><big>Failed to get your netdata version!</big></p><p>You can always get the latest version of netdata from <a href="https://github.com/firehol/netdata" target="_blank">its github page</a>.</p>'); + } + else if(sha2 === null) { + save = false; + versionLog('<p><big>Failed to get the latest version from github.</big></p><p>You can always get the latest version of netdata from <a href="https://github.com/firehol/netdata" target="_blank">its github page</a>.</p>'); + } + else if(sha1 === sha2) { + save = true; + versionLog('<p><big>You already have the latest version of netdata!</big></p><p>No update yet?<br/>Probably, we need some motivation to keep going on!</p><p>If you haven\'t already, <a href="https://github.com/firehol/netdata" target="_blank">give netdata a <b>Star</b> at its github page</a>.</p>'); + } + else { + save = true; + var compare = 'https://github.com/firehol/netdata/compare/' + sha1.toString() + '...' + sha2.toString(); - // ---------------------------------------------------------------------------- - - function finalizePage() { - // resize all charts - without starting the background thread - // this has to be done while NETDATA is paused - // if we ommit this, the affix menu will be wrong, since all - // the Dom elements are initially zero-sized - NETDATA.parseDom(); - - if(urlOptions.pan_and_zoom === true) - NETDATA.globalPanAndZoom.setMaster(NETDATA.options.targets[0], urlOptions.after, urlOptions.before); - - // ------------------------------------------------------------------------ - // https://github.com/viralpatel/jquery.shorten/blob/master/src/jquery.shorten.js - $.fn.shorten = function(settings) { - "use strict"; - - var config = { - showChars: 750, - minHideChars: 10, - ellipsesText: "...", - moreText: '<i class="fa fa-expand" aria-hidden="true"></i> show more information', - lessText: '<i class="fa fa-compress" aria-hidden="true"></i> show less information', - onLess: function() { NETDATA.onscroll(); }, - onMore: function() { NETDATA.onscroll(); }, - errMsg: null, - force: false - }; + versionLog('<p><big><strong>New version of netdata available!</strong></big></p><p>Latest version: ' + sha2.toString() + '</p><p><a href="' + compare + '" target="_blank">Click here for the changes log</a> since your installed version, and<br/><a href="https://github.com/firehol/netdata/wiki/Updating-Netdata" target="_blank">click here for directions on updating</a> your netdata installation.</p><p>We suggest to review the changes log for new features you may be interested, or important bug fixes you may need.<br/>Keeping your netdata updated, is generally a good idea.</p>'); - if (settings) { - $.extend(config, settings); - } + document.getElementById('update_badge').innerHTML = '!'; + } - if ($(this).data('jquery.shorten') && !config.force) { - return false; - } - $(this).data('jquery.shorten', true); + if(save) + saveLocalStorage('last_update_check', now.toString()); + }); + } - $(document).off("click", '.morelink'); + // ---------------------------------------------------------------------------- + + function finalizePage() { + // resize all charts - without starting the background thread + // this has to be done while NETDATA is paused + // if we ommit this, the affix menu will be wrong, since all + // the Dom elements are initially zero-sized + NETDATA.parseDom(); + + if(urlOptions.pan_and_zoom === true) + NETDATA.globalPanAndZoom.setMaster(NETDATA.options.targets[0], urlOptions.after, urlOptions.before); + + // ------------------------------------------------------------------------ + // https://github.com/viralpatel/jquery.shorten/blob/master/src/jquery.shorten.js + $.fn.shorten = function(settings) { + "use strict"; + + var config = { + showChars: 750, + minHideChars: 10, + ellipsesText: "...", + moreText: '<i class="fa fa-expand" aria-hidden="true"></i> show more information', + lessText: '<i class="fa fa-compress" aria-hidden="true"></i> show less information', + onLess: function() { NETDATA.onscroll(); }, + onMore: function() { NETDATA.onscroll(); }, + errMsg: null, + force: false + }; - $(document).on({ - click: function() { + if (settings) { + $.extend(config, settings); + } - var $this = $(this); - if ($this.hasClass('less')) { - $this.removeClass('less'); - $this.html(config.moreText); - $this.parent().prev().animate({'height':'0'+'%'}, 0, function () { $this.parent().prev().prev().show(); }).hide(0, function() { - config.onLess(); - }); - - } else { - $this.addClass('less'); - $this.html(config.lessText); - $this.parent().prev().animate({'height':'100'+'%'}, 0, function () { $this.parent().prev().prev().hide(); }).show(0, function() { - config.onMore(); - }); - } + if ($(this).data('jquery.shorten') && !config.force) { return false; } - }, '.morelink'); + $(this).data('jquery.shorten', true); + + $(document).off("click", '.morelink'); + + $(document).on({ + click: function() { + + var $this = $(this); + if ($this.hasClass('less')) { + $this.removeClass('less'); + $this.html(config.moreText); + $this.parent().prev().animate({'height':'0'+'%'}, 0, function () { $this.parent().prev().prev().show(); }).hide(0, function() { + config.onLess(); + }); + + } else { + $this.addClass('less'); + $this.html(config.lessText); + $this.parent().prev().animate({'height':'100'+'%'}, 0, function () { $this.parent().prev().prev().hide(); }).show(0, function() { + config.onMore(); + }); + } + return false; + } + }, '.morelink'); + + return this.each(function() { + var $this = $(this); - return this.each(function() { - var $this = $(this); + var content = $this.html(); + var contentlen = $this.text().length; + if (contentlen > config.showChars + config.minHideChars) { + var c = content.substr(0, config.showChars); + if (c.indexOf('<') >= 0) // If there's HTML don't want to cut it + { + var inTag = false; // I'm in a tag? + var bag = ''; // Put the characters to be shown here + var countChars = 0; // Current bag size + var openTags = []; // Stack for opened tags, so I can close them later + var tagName = null; - var content = $this.html(); - var contentlen = $this.text().length; - if (contentlen > config.showChars + config.minHideChars) { - var c = content.substr(0, config.showChars); - if (c.indexOf('<') >= 0) // If there's HTML don't want to cut it - { - var inTag = false; // I'm in a tag? - var bag = ''; // Put the characters to be shown here - var countChars = 0; // Current bag size - var openTags = []; // Stack for opened tags, so I can close them later - var tagName = null; + for (var i = 0, r = 0; r <= config.showChars; i++) { + if (content[i] == '<' && !inTag) { + inTag = true; - for (var i = 0, r = 0; r <= config.showChars; i++) { - if (content[i] == '<' && !inTag) { - inTag = true; + // This could be "tag" or "/tag" + tagName = content.substring(i + 1, content.indexOf('>', i)); - // This could be "tag" or "/tag" - tagName = content.substring(i + 1, content.indexOf('>', i)); + // If its a closing tag + if (tagName[0] == '/') { - // If its a closing tag - if (tagName[0] == '/') { + if (tagName != '/' + openTags[0]) { + config.errMsg = 'ERROR en HTML: the top of the stack should be the tag that closes'; + } else { + openTags.shift(); // Pops the last tag from the open tag stack (the tag is closed in the retult HTML!) + } - if (tagName != '/' + openTags[0]) { - config.errMsg = 'ERROR en HTML: the top of the stack should be the tag that closes'; } else { - openTags.shift(); // Pops the last tag from the open tag stack (the tag is closed in the retult HTML!) - } - - } else { - // There are some nasty tags that don't have a close tag like <br/> - if (tagName.toLowerCase() != 'br') { - openTags.unshift(tagName); // Add to start the name of the tag that opens + // There are some nasty tags that don't have a close tag like <br/> + if (tagName.toLowerCase() != 'br') { + openTags.unshift(tagName); // Add to start the name of the tag that opens + } } } - } - if (inTag && content[i] == '>') { - inTag = false; - } + if (inTag && content[i] == '>') { + inTag = false; + } - if (inTag) { bag += content.charAt(i); } // Add tag name chars to the result - else { - r++; - if (countChars <= config.showChars) { - bag += content.charAt(i); // Fix to ie 7 not allowing you to reference string characters using the [] - countChars++; - } else // Now I have the characters needed - { - if (openTags.length > 0) // I have unclosed tags + if (inTag) { bag += content.charAt(i); } // Add tag name chars to the result + else { + r++; + if (countChars <= config.showChars) { + bag += content.charAt(i); // Fix to ie 7 not allowing you to reference string characters using the [] + countChars++; + } else // Now I have the characters needed { - //console.log('They were open tags'); - //console.log(openTags); - for (j = 0; j < openTags.length; j++) { - //console.log('Cierro tag ' + openTags[j]); - bag += '</' + openTags[j] + '>'; // Close all tags that were opened - - // You could shift the tag from the stack to check if you end with an empty stack, that means you have closed all open tags + if (openTags.length > 0) // I have unclosed tags + { + //console.log('They were open tags'); + //console.log(openTags); + for (j = 0; j < openTags.length; j++) { + //console.log('Cierro tag ' + openTags[j]); + bag += '</' + openTags[j] + '>'; // Close all tags that were opened + + // You could shift the tag from the stack to check if you end with an empty stack, that means you have closed all open tags + } + break; } - break; } } } + c = $('<div/>').html(bag + '<span class="ellip">' + config.ellipsesText + '</span>').html(); + }else{ + c+=config.ellipsesText; } - c = $('<div/>').html(bag + '<span class="ellip">' + config.ellipsesText + '</span>').html(); - }else{ - c+=config.ellipsesText; + + var html = '<div class="shortcontent">' + c + + '</div><div class="allcontent">' + content + + '</div><span><a href="javascript://nop/" class="morelink">' + config.moreText + '</a></span>'; + + $this.html(html); + $this.find(".allcontent").hide(); // Hide all text + $('.shortcontent p:last', $this).css('margin-bottom', 0); //Remove bottom margin on last paragraph as it's likely shortened } + }); - var html = '<div class="shortcontent">' + c + - '</div><div class="allcontent">' + content + - '</div><span><a href="javascript://nop/" class="morelink">' + config.moreText + '</a></span>'; + }; + $(".chart-message").shorten(); + // ------------------------------------------------------------------------ + + // callback for us to track PanAndZoom operations + NETDATA.globalPanAndZoom.callback = urlOptions.netdataPanAndZoomCallback; + + // let it run (update the charts) + NETDATA.unpause(); - $this.html(html); - $this.find(".allcontent").hide(); // Hide all text - $('.shortcontent p:last', $this).css('margin-bottom', 0); //Remove bottom margin on last paragraph as it's likely shortened + // check if we have to jump to a specific section + scrollToId(urlOptions.hash.replace('#','')); + + if(urlOptions.chart !== null) { + NETDATA.alarms.scrollToChart(urlOptions.chart); + //urlOptions.hash = '#' + NETDATA.name2id('menu_' + charts[c].menu + '_submenu_' + charts[c].submenu); + //urlOptions.hash = '#chart_' + NETDATA.name2id(urlOptions.chart); + //console.log('hash = ' + urlOptions.hash); + } + + /* activate bootstrap sidebar (affix) */ + $('#sidebar').affix({ + offset: { + top: (isdemo())?150:0, + bottom: 0 } }); - }; - $(".chart-message").shorten(); - // ------------------------------------------------------------------------ + /* fix scrolling of very long affix lists + http://stackoverflow.com/questions/21691585/bootstrap-3-1-0-affix-too-long + */ + $('#sidebar').on('affixed.bs.affix', function() { + $(this).removeAttr('style'); + }); - // callback for us to track PanAndZoom operations - NETDATA.globalPanAndZoom.callback = netdataPanAndZoomCallback; + /* activate bootstrap scrollspy (needed for sidebar) */ + $(document.body).scrollspy({ + target: '#sidebar', + offset: $(window).height() / 5 // controls the diff of the <hX> element to the top, to select it + }); - // let it run (update the charts) - NETDATA.unpause(); + // change the URL based on the current position of the screen + $('#sidebar').on('activate.bs.scrollspy', function (e) { + // console.log(e); + var el = $(e.target); + //if(el.find('ul').size() == 0) { + var hash = el.find('a').attr('href'); + if(typeof hash === 'string' && hash.substring(0, 1) === '#' && urlOptions.hash.startsWith(hash + '_submenu_') === false) { + urlOptions.hash = hash; + //console.log(urlOptions.hash); + urlOptions.hashUpdate(); + } + //else console.log('hash: not accepting ' + hash); + //} + //else console.log('el.find(): not found'); + }); - // check if we have to jump to a specific section - scrollToId(urlOptions.hash.replace('#','')); + document.getElementById('footer').style.display = 'block'; - if(urlOptions.chart !== null) { - NETDATA.alarms.scrollToChart(urlOptions.chart); - //urlOptions.hash = '#' + NETDATA.name2id('menu_' + charts[c].menu + '_submenu_' + charts[c].submenu); - //urlOptions.hash = '#chart_' + NETDATA.name2id(urlOptions.chart); - //console.log('hash = ' + urlOptions.hash); - } + var update_options_modal = function() { + // console.log('update_options_modal'); - /* activate bootstrap sidebar (affix) */ - $('#sidebar').affix({ - offset: { - top: (isdemo())?150:0, - bottom: 0 - } - }); - - /* fix scrolling of very long affix lists - http://stackoverflow.com/questions/21691585/bootstrap-3-1-0-affix-too-long - */ - $('#sidebar').on('affixed.bs.affix', function() { - $(this).removeAttr('style'); - }); - - /* activate bootstrap scrollspy (needed for sidebar) */ - $(document.body).scrollspy({ - target: '#sidebar', - offset: $(window).height() / 5 // controls the diff of the <hX> element to the top, to select it - }); - - // change the URL based on the current position of the screen - $('#sidebar').on('activate.bs.scrollspy', function (e) { - // console.log(e); - var el = $(e.target); - //if(el.find('ul').size() == 0) { - var hash = el.find('a').attr('href'); - if(typeof hash === 'string' && hash.substring(0, 1) === '#' && urlOptions.hash.startsWith(hash + '_submenu_') === false) { - urlOptions.hash = hash; - //console.log(urlOptions.hash); - netdataHashUpdate(); - } - //else console.log('hash: not accepting ' + hash); - //} - //else console.log('el.find(): not found'); - }); + var sync_option = function(option) { + var self = $('#' + option); - document.getElementById('footer').style.display = 'block'; + if(self.prop('checked') !== NETDATA.getOption(option)) { + // console.log('switching ' + option.toString()); + self.bootstrapToggle(NETDATA.getOption(option)?'on':'off'); + } + } - var update_options_modal = function() { - // console.log('update_options_modal'); + var theme_sync_option = function(option) { + var self = $('#' + option); - var sync_option = function(option) { - var self = $('#' + option); + self.bootstrapToggle(netdataTheme === 'slate'?'on':'off'); + } - if(self.prop('checked') !== NETDATA.getOption(option)) { - // console.log('switching ' + option.toString()); - self.bootstrapToggle(NETDATA.getOption(option)?'on':'off'); + sync_option('eliminate_zero_dimensions'); + sync_option('destroy_on_hide'); + sync_option('async_on_scroll'); + sync_option('parallel_refresher'); + sync_option('concurrent_refreshes'); + sync_option('sync_selection'); + sync_option('sync_pan_and_zoom'); + sync_option('stop_updates_when_focus_is_lost'); + sync_option('smooth_plot'); + sync_option('pan_and_zoom_data_padding'); + sync_option('show_help'); + theme_sync_option('netdata_theme_control'); + + if(NETDATA.getOption('parallel_refresher') === false) { + $('#concurrent_refreshes_row').hide(); } - } + else { + $('#concurrent_refreshes_row').show(); + } + }; + NETDATA.setOption('setOptionCallback', update_options_modal); + + // handle options changes + $('#eliminate_zero_dimensions').change(function() { NETDATA.setOption('eliminate_zero_dimensions', $(this).prop('checked')); }); + $('#destroy_on_hide').change(function() { NETDATA.setOption('destroy_on_hide', $(this).prop('checked')); }); + $('#async_on_scroll').change(function() { NETDATA.setOption('async_on_scroll', $(this).prop('checked')); }); + $('#parallel_refresher').change(function() { NETDATA.setOption('parallel_refresher', $(this).prop('checked')); }); + $('#concurrent_refreshes').change(function() { NETDATA.setOption('concurrent_refreshes', $(this).prop('checked')); }); + $('#sync_selection').change(function() { NETDATA.setOption('sync_selection', $(this).prop('checked')); }); + $('#sync_pan_and_zoom').change(function() { NETDATA.setOption('sync_pan_and_zoom', $(this).prop('checked')); }); + $('#stop_updates_when_focus_is_lost').change(function() { + urlOptions.update_always = !$(this).prop('checked'); + urlOptions.hashUpdate(); + + NETDATA.setOption('stop_updates_when_focus_is_lost', !urlOptions.update_always); + }); + $('#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() { + urlOptions.help = $(this).prop('checked'); + urlOptions.hashUpdate(); - var theme_sync_option = function(option) { - var self = $('#' + option); + NETDATA.setOption('show_help', urlOptions.help); + netdataReload(); + }); - self.bootstrapToggle(netdataTheme === 'slate'?'on':'off'); - } + // this has to be the last + // it reloads the page + $('#netdata_theme_control').change(function() { + urlOptions.theme = $(this).prop('checked')?'slate':'white'; + urlOptions.hashUpdate(); - sync_option('eliminate_zero_dimensions'); - sync_option('destroy_on_hide'); - sync_option('async_on_scroll'); - sync_option('parallel_refresher'); - sync_option('concurrent_refreshes'); - sync_option('sync_selection'); - sync_option('sync_pan_and_zoom'); - sync_option('stop_updates_when_focus_is_lost'); - sync_option('smooth_plot'); - sync_option('pan_and_zoom_data_padding'); - sync_option('show_help'); - theme_sync_option('netdata_theme_control'); - - if(NETDATA.getOption('parallel_refresher') === false) { - $('#concurrent_refreshes_row').hide(); - } - else { - $('#concurrent_refreshes_row').show(); - } - }; - NETDATA.setOption('setOptionCallback', update_options_modal); - - // handle options changes - $('#eliminate_zero_dimensions').change(function() { NETDATA.setOption('eliminate_zero_dimensions', $(this).prop('checked')); }); - $('#destroy_on_hide').change(function() { NETDATA.setOption('destroy_on_hide', $(this).prop('checked')); }); - $('#async_on_scroll').change(function() { NETDATA.setOption('async_on_scroll', $(this).prop('checked')); }); - $('#parallel_refresher').change(function() { NETDATA.setOption('parallel_refresher', $(this).prop('checked')); }); - $('#concurrent_refreshes').change(function() { NETDATA.setOption('concurrent_refreshes', $(this).prop('checked')); }); - $('#sync_selection').change(function() { NETDATA.setOption('sync_selection', $(this).prop('checked')); }); - $('#sync_pan_and_zoom').change(function() { NETDATA.setOption('sync_pan_and_zoom', $(this).prop('checked')); }); - $('#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() { - 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() { - urlOptions.theme = $(this).prop('checked')?'slate':'white'; - if(setTheme(urlOptions.theme)) - netdataReload(); - }); + if(setTheme(urlOptions.theme)) + netdataReload(); + }); - $('#updateModal').on('shown.bs.modal', function() { - notifyForUpdate(true); - }); + $('#updateModal').on('show.bs.modal', function() { + versionLog('checking, please wait...'); + }); - $('#alarmsModal').on('shown.bs.modal', function() { - NETDATA.pause(alarmsUpdateModal); - }); + $('#updateModal').on('shown.bs.modal', function() { + notifyForUpdate(true); + }); - $('#alarmsModal').on('hidden.bs.modal', function() { - NETDATA.unpause(); - document.getElementById('alarms_active').innerHTML = - document.getElementById('alarms_all').innerHTML = - document.getElementById('alarms_log').innerHTML = - 'loading...'; - }); - - $('#deleteRegistryModal').on('hidden.bs.modal', function() { - deleteRegistryGuid = null; - }); - - if(isdemo()) { - if(urlOptions.nowelcome !== true) { + $('#alarmsModal').on('shown.bs.modal', function() { + NETDATA.pause(alarmsUpdateModal); + }); + + $('#alarmsModal').on('hidden.bs.modal', function() { + NETDATA.unpause(); + document.getElementById('alarms_active').innerHTML = + document.getElementById('alarms_all').innerHTML = + document.getElementById('alarms_log').innerHTML = + 'loading...'; + }); + + $('#deleteRegistryModal').on('hidden.bs.modal', function() { + deleteRegistryGuid = null; + }); + + if(isdemo()) { + // do not to give errors on netdata demo servers for 60 seconds + NETDATA.options.current.retries_on_data_failures = 60; + + if(urlOptions.nowelcome !== true) { + setTimeout(function() { + $('#welcomeModal').modal(); + }, 1000); + } + + // google analytics when this is used for the home page of the demo sites + // this does not run on user's installations setTimeout(function() { - $('#welcomeModal').modal(); - }, 1000); + (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ + (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), + m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) + })(window,document,'script','https://www.google-analytics.com/analytics.js','ga'); + + ga('create', 'UA-64295674-3', 'auto'); + ga('send', 'pageview'); + }, 2000); } + else notifyForUpdate(); + + if(urlOptions.show_alarms === true) + setTimeout(function() { $('#alarmsModal').modal('show'); }, 1000); + + var sidebar = document.getElementById('sidebar'); + Ps.initialize(sidebar, { + wheelSpeed: 0.5, + wheelPropagation: true, + swipePropagation: true, + minScrollbarLength: null, + maxScrollbarLength: null, + useBothWheelAxes: false, + suppressScrollX: true, + suppressScrollY: false, + scrollXMarginOffset: 0, + scrollYMarginOffset: 0, + theme: 'default' + }); + Ps.update(sidebar); + + var registry = document.getElementById('myNetdataDropdownUL'); + Ps.initialize(registry, { + wheelSpeed: 1, + wheelPropagation: false, + swipePropagation: false, + minScrollbarLength: null, + maxScrollbarLength: null, + useBothWheelAxes: false, + suppressScrollX: true, + suppressScrollY: false, + scrollXMarginOffset: 0, + scrollYMarginOffset: 0, + theme: 'default' + }); + Ps.update(registry); - // google analytics when this is used for the home page of the demo sites - // this does not run on user's installations - setTimeout(function() { - (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ - (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), - m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) - })(window,document,'script','https://www.google-analytics.com/analytics.js','ga'); + NETDATA.onresizeCallback = function() { + Ps.update(sidebar); + Ps.update(registry); + }; - ga('create', 'UA-64295674-3', 'auto'); - ga('send', 'pageview'); - }, 2000); + $('#myNetdataDropdownParent') + .on('show.bs.dropdown', function () { + NETDATA.pause(function() {}); + }) + .on('shown.bs.dropdown', function () { + Ps.update(registry); + }) + .on('hidden.bs.dropdown', function () { + NETDATA.unpause(); + }); + + // var netdataEnded = performance.now(); + // console.log('start up time: ' + (netdataEnded - netdataStarted).toString() + ' ms'); } - else notifyForUpdate(); - if(urlOptions.show_alarms === true) - setTimeout(function() { $('#alarmsModal').modal('show'); }, 1000); - } + function resetDashboardOptions() { + var help = NETDATA.options.current.show_help; - function resetDashboardOptions() { - var help = NETDATA.options.current.show_help; + NETDATA.resetOptions(); + if(setTheme('slate')) + netdataReload(); - NETDATA.resetOptions(); - if(setTheme('slate')) - netdataReload(); + if(help !== NETDATA.options.current.show_help) + netdataReload(); + } - if(help !== NETDATA.options.current.show_help) - netdataReload(); - } + // callback to add the dashboard info to the + // parallel javascript downloader in netdata + var netdataPrepCallback = function() { + NETDATA.requiredCSS.push({ + url: NETDATA.serverDefault + 'css/bootstrap-toggle-2.2.2.min.css', + isAlreadyLoaded: function() { return false; } + }); - // callback to add the dashboard info to the - // parallel javascript downloader in netdata - var netdataPrepCallback = function() { - NETDATA.requiredCSS.push({ - url: NETDATA.serverDefault + 'css/bootstrap-toggle-2.2.2.min.css', - isAlreadyLoaded: function() { return false; } - }); - - NETDATA.requiredJs.push({ - url: NETDATA.serverDefault + 'lib/bootstrap-toggle-2.2.2.min.js', - isAlreadyLoaded: function() { return false; } - }); - - NETDATA.requiredJs.push({ - url: NETDATA.serverDefault + 'dashboard_info.js?v20161002-1', - async: false, - isAlreadyLoaded: function() { return false; } - }); - - if(isdemo()) { - document.getElementById('masthead').style.display = 'block'; - } - } + NETDATA.requiredJs.push({ + url: NETDATA.serverDefault + 'lib/bootstrap-toggle-2.2.2.min.js', + isAlreadyLoaded: function() { return false; } + }); - // our entry point - var netdataCallback = initializeDynamicDashboard; + NETDATA.requiredJs.push({ + url: NETDATA.serverDefault + 'dashboard_info.js?v20170115-1', + async: false, + isAlreadyLoaded: function() { return false; } + }); -</script> + if(isdemo()) { + document.getElementById('masthead').style.display = 'block'; + } + else { + if(urlOptions.update_always === true) + NETDATA.setOption('stop_updates_when_focus_is_lost', !urlOptions.update_always); + } + }; + + // our entry point + // var netdataStarted = performance.now(); + var netdataCallback = initializeDynamicDashboard; + </script> +</head> <body data-spy="scroll" data-target="#sidebar"> <div id="loadOverlay" class="loadOverlay" style="background-color: #888; color: #888;"> @@ -2494,9 +2664,9 @@ <div class="container"> <nav id="mynetdata_nav" class="collapse navbar-collapse navbar-left hidden-sm hidden-xs" role="navigation" style="padding-right: 20px;"> <ul class="nav navbar-nav"> - <li class="dropdown"> - <a href="#" class="dropdown-toggle" data-toggle="dropdown" id="current_view">my-netdata <strong class="caret"></strong></a> - <ul class="dropdown-menu scrollable-menu inpagemenu multi-column columns-2" role="menu"> + <li class="dropdown" id="myNetdataDropdownParent"> + <a href="#" class="dropdown-toggle" data-toggle="dropdown">my-netdata <strong class="caret"></strong></a> + <ul class="dropdown-menu scrollable-menu inpagemenu multi-column columns-2" role="menu" id="myNetdataDropdownUL"> <div class="row"> <div class="col-sm-6" style="width: 85%; padding-right: 0;"> <ul id="mynetdata_servers" class="multi-column-dropdown"> @@ -2524,31 +2694,21 @@ </div> <nav class="collapse navbar-collapse navbar-right" role="navigation"> <ul class="nav navbar-nav"> - <li><a href="#" class="btn" data-toggle="modal" data-target="#alarmsModal" title="alarms"><i class="fa fa-bell"></i></span> <span class="hidden-sm">Alarms </span><span id="alarms_count_badge" class="badge"></a></li> + <li><a href="#" class="btn" data-toggle="modal" data-target="#alarmsModal" title="alarms"><i class="fa fa-bell"></i> <span class="hidden-sm">Alarms </span><span id="alarms_count_badge" class="badge"></span></a></li> <li><a href="#" class="btn" data-toggle="modal" data-target="#optionsModal" title="dashboard settings"><i class="fa fa-sliders"></i> <span class="hidden-sm">Settings</span></a></li> - <li><a href="https://github.com/firehol/netdata/wiki" class="btn" target="_blank" title="netdata community"><i class="fa fa-github"></i> <span class="hidden-sm hidden-md">Community</span></a></li> - <li class="hidden-sm" id="updateButton"><a href="#" class="btn" data-toggle="modal" data-target="#updateModal" title="check for update"><i class="fa fa-cloud-download"></i><span id="update_badge" class="badge"></span> <span class="hidden-sm hidden-md">Update</span></a></li> + <li class="hidden-sm" id="updateButton"><a href="#" class="btn" data-toggle="modal" data-target="#updateModal" title="check for update"><i class="fa fa-cloud-download"></i> <span class="hidden-sm hidden-md">Update </span><span id="update_badge" class="badge"></span></a></li> + <li><a href="https://github.com/firehol/netdata/wiki" class="btn" target="_blank" title="netdata on github"><i class="fa fa-github"></i></a></li> + <li><a href="https://twitter.com/linuxnetdata" class="btn" target="_blank" title="netdata on twitter"><i class="fa fa-twitter" aria-hidden="true"></i></a></li> + <li><a href="https://www.facebook.com/linuxnetdata/" class="btn" target="_blank" title="netdata on facebook"><i class="fa fa-facebook-official" aria-hidden="true"></i></a></li> <li class="hidden-sm"><a href="#" class="btn" data-toggle="modal" data-target="#helpModal" title="dashboard help"><i class="fa fa-question-circle"></i> <span class="hidden-sm hidden-md">Help</span></a></li> - <!-- - <li class="dropdown hidden-md hidden-lg hidden-xs"> - <a href="#" class="dropdown-toggle" data-toggle="dropdown" id="current_view">Menu <strong class="caret"></strong></a> - <ul class="dropdown-menu scrollable-menu inpagemenu" role="menu"> - <li><a href="#" class="btn" data-toggle="modal" data-target="#alarmsModal"><i class="fa fa-bell"></i> alarms</a></li> - <li><a href="#" class="btn" data-toggle="modal" data-target="#optionsModal"><i class="fa fa-sliders"></i> settings</a></li> - <li><a href="https://github.com/firehol/netdata/wiki" class="btn" target="_blank"><i class="fa fa-github"></i> community</a></li> - <li><a href="#" class="btn" data-toggle="modal" data-target="#helpModal"><i class="fa fa-question-circle"></i> help</a></li> - </ul> - </li> - --> <li class="dropdown hidden-sm hidden-md hidden-lg"> - <a href="#" class="dropdown-toggle" data-toggle="dropdown" id="current_view">my-netdata <strong class="caret"></strong></a> + <a href="#" class="dropdown-toggle" data-toggle="dropdown">my-netdata <strong class="caret"></strong></a> <ul id="mynetdata_servers2" class="dropdown-menu scrollable-menu inpagemenu" role="menu"> <li><a href="#" onclick="return false;" style="color: #999;">loading...</a></li> </ul> </li> </ul> </nav> - </nav> </div> </nav> @@ -2584,7 +2744,7 @@ <div class="charts-body" role="main"> <div id="charts_div"></div> </div> - <div class="sidebar-body hidden-xs hidden-sm" role="complementary"> + <div class="sidebar-body hidden-xs hidden-sm" id="sidebar-body" role="complementary"> <nav class="dashboard-sidebar hidden-print hidden-xs hidden-sm" id="sidebar" role="menu"></nav> </div> </div> @@ -2595,7 +2755,7 @@ <div class="col-md-10" role="main"> <div class="p"> <big><a href="https://github.com/firehol/netdata/wiki" target="_blank">netdata</a></big><br/> - <i class="fa fa-copyright"></i> Copyright 2016, Costa Tsaousis.<br/> + <i class="fa fa-copyright"></i> Copyright 2016-2017, <a href="mailto:costa@tsaousis.gr">Costa Tsaousis</a>.<br/> Released under <a href="http://www.gnu.org/licenses/gpl-3.0.en.html" target="_blank">GPL v3 or later</a>.<br/> </div> <div class="p"> @@ -2620,8 +2780,8 @@ <i class="fa fa-circle"></i> <a href="http://www.bootstraptoggle.com/" target="_blank">Bootstrap Toggle</a>, <i class="fa fa-copyright"></i> Copyright (c) 2011-2014 Min Hur, The New York Times Company, <a href="https://github.com/minhur/bootstrap-toggle/blob/master/LICENSE" target="_blank">MIT License</a> - <i class="fa fa-circle"></i> <a href="https://jamesflorentino.github.io/nanoScrollerJS/" target="_blank">NanoScroller</a>, - <i class="fa fa-copyright"></i> Copyright 2012, James Florentino, <a href="https://github.com/jamesflorentino/nanoScrollerJS/blob/master/LICENSE" target="_blank">MIT License</a> + <i class="fa fa-circle"></i> <a href="https://github.com/noraesae/perfect-scrollbar" target="_blank">perfect-scrollbar</a>, + <i class="fa fa-copyright"></i> Copyright 2016, Hyunje Alex Jun and other contributors, <a href="https://github.com/noraesae/perfect-scrollbar/blob/master/LICENSE" target="_blank">MIT License</a> <i class="fa fa-circle"></i> <a href="https://fortawesome.github.io/Font-Awesome/" target="_blank">FontAwesome</a>, <i class="fa fa-copyright"></i> Created by Dave Gandy, Font: <a href="http://scripts.sil.org/OFL" target="_blank">SIL OFL 1.1 License</a>, CSS: <a href="http://opensource.org/licenses/mit-license.html" target="_blank">MIT License</a> @@ -2645,28 +2805,43 @@ <div class="modal-content"> <div class="modal-header"> <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button> - <h4 class="modal-title" id="welcomeModalLabel">Welcome!</h4> + <h4 class="modal-title" id="welcomeModalLabel">Welcome to the world of netdata</h4> </div> <div class="modal-body"> - <div class="p"> - <b><a href="https://github.com/firehol/netdata/wiki" target="_blank">netdata</a></b> is the fastest way to visualize metrics. It is a resource efficient, highly optimized system for collecting and visualizing any type of real-time time series data, from CPU usage, disk activity, SQL queries, API calls, web site visitors, etc. - </div> - <div class="p"> - <b><a href="https://github.com/firehol/netdata/wiki" target="_blank">netdata</a></b> tries to visualize the truth of <b>now</b>, in its <b>greatest detail</b>, so that you can get insights of what is happening now and what just happened, on your systems and applications. - </div> - <div class="p"> - To make a chart in <b><a href="https://github.com/firehol/netdata/wiki" target="_blank">netdata</a></b>, you just need a <b>number</b>. Just a number you can read somehow. <b><a href="https://github.com/firehol/netdata/wiki" target="_blank">netdata</a></b> will turn this number to a real time, interactive, web chart. For collecting these numbers, it supports <a href="https://github.com/firehol/netdata/wiki/External-Plugins" target="_blank">external plugins</a>, even <a href="https://github.com/firehol/netdata/wiki/General-Info---charts.d" target="_blank">shell</a> or <a href="https://github.com/firehol/netdata/wiki/General-Info---charts.d" target="_blank">node.js</a> plugins. Any computer program, in any language, that can print a few lines of text on its standard output, can be a netdata data collector. - </div> - <div class="p"> - <b><a href="https://github.com/firehol/netdata/wiki" target="_blank">netdata</a></b> can embed charts everywhere, like this one <div data-netdata="system.cpu" data-dimensions="system" data-after="-120" data-width="25%" data-height="15px" data-chart-library="dygraph" data-dygraph-theme="sparkline" data-show-value-of-system-at="system.cpu.system.modal.1"></div> (my CPU system usage which is <span id="system.cpu.system.modal.1" style="display: inline-block; width: 40px; text-align: right;"></span>%), - or this one <div data-netdata="ipv4.tcppackets" data-dimensions="received" data-after="-120" data-width="25%" data-height="15px" data-chart-library="dygraph" data-dygraph-theme="sparkline" data-show-value-of-received-at="ipv4.tcppackets.received.modal.1"></div> (my IPv4 received TCP packets, which are <span id="ipv4.tcppackets.received.modal.1" style="display: inline-block; width: 60px; text-align: right;"></span>/second). - </div> - <div class="p"> - You can have <b><a href="https://github.com/firehol/netdata/wiki" target="_blank">netdata</a></b> charts on your site too. Just give it a <code>div</code> and a real time chart, zoomable and draggable will appear (try it even on these tiny ones - <b>drag</b> them to pan horizontally, <b>shift + drag</b> to zoom in, on <b>chrome shift + mouse wheel</b> to zoom in/out, <b>double click</b> on them to reset them - don't be afraid of <b><a href="https://github.com/firehol/netdata/wiki" target="_blank">netdata</a></b> performance - <a href="https://github.com/firehol/netdata/wiki/Performance" target="_blank">a raspberry pi 2 can sustain 300 charts updates per second</a>!). - </div> - <div class="p"> - For more information please refer to the <b><a href="https://github.com/firehol/netdata/wiki" target="_blank">netdata wiki</a></b>. + <div class="p"> + <div style="width: 100%; text-align: center; padding-top: 10px; padding-bottom: 10px; font-size: 18px;"> + if there is a metric for something, we want it visualised<br/> + and we want this visualisation to be <strong>real-time</strong>, <strong>efficient</strong> and <strong>awesome</strong> </div> + </div> + <div class="p"> + <b><a href="https://github.com/firehol/netdata/wiki" target="_blank">netdata</a></b> + is a new way to monitor your systems and applications, to get <strong>real-time insights</strong> + of what is really happening and what affects performance. + It is carefully optimised to be a real-time system, without interfering in any way, + to the core function of your systems. + </div> + <div class="p"> + <b><a href="https://github.com/firehol/netdata/wiki" target="_blank">netdata</a></b> + has been designed to monitor <strong>massive amounts of metrics, per server, per second</strong>. + When installed, it might come up with 1k to 3k metrics, but we have been testing it with 100k + metrics, all collected per second, and still the cpu utilisation remained negligible. + <br/> + We have also tried to give each metric, a meaning, a context. + We have grouped and categorized all metrics into meaningful charts, providing a + better understanding of the underlying technologies and mechanisms. + </div> + <div class="p"> + <b><a href="https://github.com/firehol/netdata/wiki" target="_blank">netdata</a></b> is free, + open-source software. If you decide to use it, + <strong><a href="https://github.com/firehol/netdata/wiki/a-github-star-is-important" target="_blank">it is important to give netdata a star at GitHub</a></strong>. + </div> + <div class="p"> + Enjoy real-time performance monitoring! + </div> + <div class="p"> + Costa Tsaousis + </div> </div> <div class="modal-footer"> <button type="button" class="btn btn-default" data-dismiss="modal">Close</button> @@ -2727,7 +2902,7 @@ </div> <hr/> <div class="p"> - <h4>SHIFT + Mouse Wheel <small>(does not work on firefox and IE/Edge)</small></h4> + <h4>SHIFT + Mouse Wheel</h4> While pressing the shift key and the mouse pointer is over the contents of a chart, scroll the mouse wheel to zoom in or out. This kind of zooming is aligned to center below the mouse pointer. The other charts will follow too. <br/> Once a chart is zoomed, auto refreshing stops for all charts. To enable it again, <b>double click</b> a zoomed chart. @@ -2888,23 +3063,13 @@ <td class="option-info"><strong>Which chart refresh policy to use?</strong><br/> <small>When set to <b>parallel</b>, visible charts are refreshed in parallel (all queries are sent to netdata server in parallel) and are rendered asynchronously. When set to <b>sequential</b> charts are refreshed one after another. Set it to parallel if your browser can cope with it (most modern browsers do), set it to sequential if you work on an older/slower computer.</small> </td> - </tr> + </tr> <tr class="option-row" id="concurrent_refreshes_row"> - <td class="option-control"></td> - <td class="option-info"> - <table> - <tr class="option-row"> - <td class="option-control"> - <input id="concurrent_refreshes" type="checkbox" checked data-toggle="toggle" data-on="Resync" data-off="Best Effort" data-width="110px"> - </td> - <td class="option-info"> - <strong>Shall we re-sync chart refreshes?</strong><br/> + <td class="option-control"><input id="concurrent_refreshes" type="checkbox" checked data-toggle="toggle" data-on="Resync" data-off="Best Effort" data-width="110px"></td> + <td class="option-info"><strong>Shall we re-sync chart refreshes?</strong><br/> <small>When set to <b>Resync</b>, the dashboard will attempt to re-synchronize all the charts so that they are refreshed concurrently. When set to <b>Best Effort</b>, each chart may be refreshed with a little time difference to the others. Normally, the dashboard starts refreshing them in parallel, but depending on the speed of your computer and the network latencies, charts start having a slight time difference. Setting this to <b>Resync</b> will attempt to re-synchronize the charts on every update. Setting it to <b>Best Effort</b> may lower the pressure on your browser and the network.</small> - </td> - </tr> - </table> </td> - </tr> + </tr> <tr class="option-row"> <td class="option-control"><input id="sync_selection" type="checkbox" checked data-toggle="toggle" data-on="Sync" data-off="Don't Sync" data-onstyle="success" data-offstyle="danger" data-width="110px"></td> <td class="option-info"><strong>Sync hover selection on all charts?</strong><br/> @@ -2983,6 +3148,17 @@ <br/> <div style="padding: 10px;"></div> <div id="versionCheckLog">Not checked yet. Please press the Check Now button.</div> + <div> + <hr/> + </div> + <div> + For progress reports and key netdata updates: <strong><a href="https://twitter.com/linuxnetdata" target="_blank">follow netdata on <i class="fa fa-twitter" aria-hidden="true"></i> twitter</a></strong>. + <br/> + <small> + You can also <a href="https://www.facebook.com/linuxnetdata/" target="_blank">follow netdata on <i class="fa fa-facebook" aria-hidden="true"></i> facebook</a>, + or <a href="https://github.com/firehol/netdata" target="_blank">watch netdata on <i class="fa fa-github" aria-hidden="true"></i> github</a>. + </small> + </div> </div> <div class="modal-footer"> <a href="#" onclick="notifyForUpdate(true); return false;" type="button" class="btn btn-default">Check Now</a> @@ -3033,9 +3209,9 @@ <br/> All the browsers with the same ID will identify <b>you</b>, so please don't share this with others. <p style="text-align: center; padding-top: 10px; padding-bottom: 10px; line-height: 2;"> - <form action="#"> - <input type="text" class="form-control" id="switchRegistryPersonGUID" placeholder="your personal ID" maxlength="36" autocomplete="off" style="text-align: center; font-size: 1.4em;"> - </form> + <form action="#"> + <input type="text" class="form-control" id="switchRegistryPersonGUID" placeholder="your personal ID" maxlength="36" autocomplete="off" style="text-align: center; font-size: 1.4em;"> + </form> </p> Either copy this ID and paste it to another browser, or paste here the ID you have taken from another browser. <p style="padding-top: 10px;"><small> @@ -3083,4 +3259,4 @@ </div> </body> </html> -<script type="text/javascript" src="dashboard.js?v20161004-1"></script> +<script type="text/javascript" src="dashboard.js?v20170118-11"></script> diff --git a/web/lib/jquery.nanoscroller-0.8.7.min.js b/web/lib/jquery.nanoscroller-0.8.7.min.js deleted file mode 100644 index 08802adbd..000000000 --- a/web/lib/jquery.nanoscroller-0.8.7.min.js +++ /dev/null @@ -1,4 +0,0 @@ -/*! nanoScrollerJS - v0.8.7 - (c) 2015 James Florentino; Licensed MIT */ - -!function(a){return"function"==typeof define&&define.amd?define(["jquery"],function(b){return a(b,window,document)}):"object"==typeof exports?module.exports=a(require("jquery"),window,document):a(jQuery,window,document)}(function(a,b,c){"use strict";var d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B,C,D,E,F,G,H;z={paneClass:"nano-pane",sliderClass:"nano-slider",contentClass:"nano-content",enabledClass:"has-scrollbar",flashedClass:"flashed",activeClass:"active",iOSNativeScrolling:!1,preventPageScrolling:!1,disableResize:!1,alwaysVisible:!1,flashDelay:1500,sliderMinHeight:20,sliderMaxHeight:null,documentContext:null,windowContext:null},u="scrollbar",t="scroll",l="mousedown",m="mouseenter",n="mousemove",p="mousewheel",o="mouseup",s="resize",h="drag",i="enter",w="up",r="panedown",f="DOMMouseScroll",g="down",x="wheel",j="keydown",k="keyup",v="touchmove",d="Microsoft Internet Explorer"===b.navigator.appName&&/msie 7./i.test(b.navigator.appVersion)&&b.ActiveXObject,e=null,D=b.requestAnimationFrame,y=b.cancelAnimationFrame,F=c.createElement("div").style,H=function(){var a,b,c,d,e,f;for(d=["t","webkitT","MozT","msT","OT"],a=e=0,f=d.length;f>e;a=++e)if(c=d[a],b=d[a]+"ransform",b in F)return d[a].substr(0,d[a].length-1);return!1}(),G=function(a){return H===!1?!1:""===H?a:H+a.charAt(0).toUpperCase()+a.substr(1)},E=G("transform"),B=E!==!1,A=function(){var a,b,d;return a=c.createElement("div"),b=a.style,b.position="absolute",b.width="100px",b.height="100px",b.overflow=t,b.top="-9999px",c.body.appendChild(a),d=a.offsetWidth-a.clientWidth,c.body.removeChild(a),d},C=function(){var a,c,d;return c=b.navigator.userAgent,(a=/(?=.+Mac OS X)(?=.+Firefox)/.test(c))?(d=/Firefox\/\d{2}\./.exec(c),d&&(d=d[0].replace(/\D+/g,"")),a&&+d>23):!1},q=function(){function j(d,f){this.el=d,this.options=f,e||(e=A()),this.$el=a(this.el),this.doc=a(this.options.documentContext||c),this.win=a(this.options.windowContext||b),this.body=this.doc.find("body"),this.$content=this.$el.children("."+this.options.contentClass),this.$content.attr("tabindex",this.options.tabIndex||0),this.content=this.$content[0],this.previousPosition=0,this.options.iOSNativeScrolling&&null!=this.el.style.WebkitOverflowScrolling?this.nativeScrolling():this.generate(),this.createEvents(),this.addEvents(),this.reset()}return j.prototype.preventScrolling=function(a,b){if(this.isActive)if(a.type===f)(b===g&&a.originalEvent.detail>0||b===w&&a.originalEvent.detail<0)&&a.preventDefault();else if(a.type===p){if(!a.originalEvent||!a.originalEvent.wheelDelta)return;(b===g&&a.originalEvent.wheelDelta<0||b===w&&a.originalEvent.wheelDelta>0)&&a.preventDefault()}},j.prototype.nativeScrolling=function(){this.$content.css({WebkitOverflowScrolling:"touch"}),this.iOSNativeScrolling=!0,this.isActive=!0},j.prototype.updateScrollValues=function(){var a,b;a=this.content,this.maxScrollTop=a.scrollHeight-a.clientHeight,this.prevScrollTop=this.contentScrollTop||0,this.contentScrollTop=a.scrollTop,b=this.contentScrollTop>this.previousPosition?"down":this.contentScrollTop<this.previousPosition?"up":"same",this.previousPosition=this.contentScrollTop,"same"!==b&&this.$el.trigger("update",{position:this.contentScrollTop,maximum:this.maxScrollTop,direction:b}),this.iOSNativeScrolling||(this.maxSliderTop=this.paneHeight-this.sliderHeight,this.sliderTop=0===this.maxScrollTop?0:this.contentScrollTop*this.maxSliderTop/this.maxScrollTop)},j.prototype.setOnScrollStyles=function(){var a;B?(a={},a[E]="translate(0, "+this.sliderTop+"px)"):a={top:this.sliderTop},D?(y&&this.scrollRAF&&y(this.scrollRAF),this.scrollRAF=D(function(b){return function(){return b.scrollRAF=null,b.slider.css(a)}}(this))):this.slider.css(a)},j.prototype.createEvents=function(){this.events={down:function(a){return function(b){return a.isBeingDragged=!0,a.offsetY=b.pageY-a.slider.offset().top,a.slider.is(b.target)||(a.offsetY=0),a.pane.addClass(a.options.activeClass),a.doc.bind(n,a.events[h]).bind(o,a.events[w]),a.body.bind(m,a.events[i]),!1}}(this),drag:function(a){return function(b){return a.sliderY=b.pageY-a.$el.offset().top-a.paneTop-(a.offsetY||.5*a.sliderHeight),a.scroll(),a.contentScrollTop>=a.maxScrollTop&&a.prevScrollTop!==a.maxScrollTop?a.$el.trigger("scrollend"):0===a.contentScrollTop&&0!==a.prevScrollTop&&a.$el.trigger("scrolltop"),!1}}(this),up:function(a){return function(b){return a.isBeingDragged=!1,a.pane.removeClass(a.options.activeClass),a.doc.unbind(n,a.events[h]).unbind(o,a.events[w]),a.body.unbind(m,a.events[i]),!1}}(this),resize:function(a){return function(b){a.reset()}}(this),panedown:function(a){return function(b){return a.sliderY=(b.offsetY||b.originalEvent.layerY)-.5*a.sliderHeight,a.scroll(),a.events.down(b),!1}}(this),scroll:function(a){return function(b){a.updateScrollValues(),a.isBeingDragged||(a.iOSNativeScrolling||(a.sliderY=a.sliderTop,a.setOnScrollStyles()),null!=b&&(a.contentScrollTop>=a.maxScrollTop?(a.options.preventPageScrolling&&a.preventScrolling(b,g),a.prevScrollTop!==a.maxScrollTop&&a.$el.trigger("scrollend")):0===a.contentScrollTop&&(a.options.preventPageScrolling&&a.preventScrolling(b,w),0!==a.prevScrollTop&&a.$el.trigger("scrolltop"))))}}(this),wheel:function(a){return function(b){var c;if(null!=b)return c=b.delta||b.wheelDelta||b.originalEvent&&b.originalEvent.wheelDelta||-b.detail||b.originalEvent&&-b.originalEvent.detail,c&&(a.sliderY+=-c/3),a.scroll(),!1}}(this),enter:function(a){return function(b){var c;if(a.isBeingDragged)return 1!==(b.buttons||b.which)?(c=a.events)[w].apply(c,arguments):void 0}}(this)}},j.prototype.addEvents=function(){var a;this.removeEvents(),a=this.events,this.options.disableResize||this.win.bind(s,a[s]),this.iOSNativeScrolling||(this.slider.bind(l,a[g]),this.pane.bind(l,a[r]).bind(""+p+" "+f,a[x])),this.$content.bind(""+t+" "+p+" "+f+" "+v,a[t])},j.prototype.removeEvents=function(){var a;a=this.events,this.win.unbind(s,a[s]),this.iOSNativeScrolling||(this.slider.unbind(),this.pane.unbind()),this.$content.unbind(""+t+" "+p+" "+f+" "+v,a[t])},j.prototype.generate=function(){var a,c,d,f,g,h,i;return f=this.options,h=f.paneClass,i=f.sliderClass,a=f.contentClass,(g=this.$el.children("."+h)).length||g.children("."+i).length||this.$el.append('<div class="'+h+'"><div class="'+i+'" /></div>'),this.pane=this.$el.children("."+h),this.slider=this.pane.find("."+i),0===e&&C()?(d=b.getComputedStyle(this.content,null).getPropertyValue("padding-right").replace(/[^0-9.]+/g,""),c={right:-14,paddingRight:+d+14}):e&&(c={right:-e},this.$el.addClass(f.enabledClass)),null!=c&&this.$content.css(c),this},j.prototype.restore=function(){this.stopped=!1,this.iOSNativeScrolling||this.pane.show(),this.addEvents()},j.prototype.reset=function(){var a,b,c,f,g,h,i,j,k,l,m,n;return this.iOSNativeScrolling?void(this.contentHeight=this.content.scrollHeight):(this.$el.find("."+this.options.paneClass).length||this.generate().stop(),this.stopped&&this.restore(),a=this.content,f=a.style,g=f.overflowY,d&&this.$content.css({height:this.$content.height()}),b=a.scrollHeight+e,l=parseInt(this.$el.css("max-height"),10),l>0&&(this.$el.height(""),this.$el.height(a.scrollHeight>l?l:a.scrollHeight)),i=this.pane.outerHeight(!1),k=parseInt(this.pane.css("top"),10),h=parseInt(this.pane.css("bottom"),10),j=i+k+h,n=Math.round(j/b*i),n<this.options.sliderMinHeight?n=this.options.sliderMinHeight:null!=this.options.sliderMaxHeight&&n>this.options.sliderMaxHeight&&(n=this.options.sliderMaxHeight),g===t&&f.overflowX!==t&&(n+=e),this.maxSliderTop=j-n,this.contentHeight=b,this.paneHeight=i,this.paneOuterHeight=j,this.sliderHeight=n,this.paneTop=k,this.slider.height(n),this.events.scroll(),this.pane.show(),this.isActive=!0,a.scrollHeight===a.clientHeight||this.pane.outerHeight(!0)>=a.scrollHeight&&g!==t?(this.pane.hide(),this.isActive=!1):this.el.clientHeight===a.scrollHeight&&g===t?this.slider.hide():this.slider.show(),this.pane.css({opacity:this.options.alwaysVisible?1:"",visibility:this.options.alwaysVisible?"visible":""}),c=this.$content.css("position"),("static"===c||"relative"===c)&&(m=parseInt(this.$content.css("right"),10),m&&this.$content.css({right:"",marginRight:m})),this)},j.prototype.scroll=function(){return this.isActive?(this.sliderY=Math.max(0,this.sliderY),this.sliderY=Math.min(this.maxSliderTop,this.sliderY),this.$content.scrollTop(this.maxScrollTop*this.sliderY/this.maxSliderTop),this.iOSNativeScrolling||(this.updateScrollValues(),this.setOnScrollStyles()),this):void 0},j.prototype.scrollBottom=function(a){return this.isActive?(this.$content.scrollTop(this.contentHeight-this.$content.height()-a).trigger(p),this.stop().restore(),this):void 0},j.prototype.scrollTop=function(a){return this.isActive?(this.$content.scrollTop(+a).trigger(p),this.stop().restore(),this):void 0},j.prototype.scrollTo=function(a){return this.isActive?(this.scrollTop(this.$el.find(a).get(0).offsetTop),this):void 0},j.prototype.stop=function(){return y&&this.scrollRAF&&(y(this.scrollRAF),this.scrollRAF=null),this.stopped=!0,this.removeEvents(),this.iOSNativeScrolling||this.pane.hide(),this},j.prototype.destroy=function(){return this.stopped||this.stop(),!this.iOSNativeScrolling&&this.pane.length&&this.pane.remove(),d&&this.$content.height(""),this.$content.removeAttr("tabindex"),this.$el.hasClass(this.options.enabledClass)&&(this.$el.removeClass(this.options.enabledClass),this.$content.css({right:""})),this},j.prototype.flash=function(){return!this.iOSNativeScrolling&&this.isActive?(this.reset(),this.pane.addClass(this.options.flashedClass),setTimeout(function(a){return function(){a.pane.removeClass(a.options.flashedClass)}}(this),this.options.flashDelay),this):void 0},j}(),a.fn.nanoScroller=function(b){return this.each(function(){var c,d;if((d=this.nanoscroller)||(c=a.extend({},z,b),this.nanoscroller=d=new q(this,c)),b&&"object"==typeof b){if(a.extend(d.options,b),null!=b.scrollBottom)return d.scrollBottom(b.scrollBottom);if(null!=b.scrollTop)return d.scrollTop(b.scrollTop);if(b.scrollTo)return d.scrollTo(b.scrollTo);if("bottom"===b.scroll)return d.scrollBottom(0);if("top"===b.scroll)return d.scrollTop(0);if(b.scroll&&b.scroll instanceof a)return d.scrollTo(b.scroll);if(b.stop)return d.stop();if(b.destroy)return d.destroy();if(b.flash)return d.flash()}return d.reset()})},a.fn.nanoScroller.Constructor=q}); -//# sourceMappingURL=jquery.nanoscroller.min.js.map
\ No newline at end of file diff --git a/web/lib/perfect-scrollbar-0.6.15.min.js b/web/lib/perfect-scrollbar-0.6.15.min.js new file mode 100644 index 000000000..1bde608d5 --- /dev/null +++ b/web/lib/perfect-scrollbar-0.6.15.min.js @@ -0,0 +1,2 @@ +/* perfect-scrollbar v0.6.15 */ +!function t(e,n,r){function o(i,s){if(!n[i]){if(!e[i]){var a="function"==typeof require&&require;if(!s&&a)return a(i,!0);if(l)return l(i,!0);var c=new Error("Cannot find module '"+i+"'");throw c.code="MODULE_NOT_FOUND",c}var u=n[i]={exports:{}};e[i][0].call(u.exports,function(t){var n=e[i][1][t];return o(n?n:t)},u,u.exports,t,e,n,r)}return n[i].exports}for(var l="function"==typeof require&&require,i=0;i<r.length;i++)o(r[i]);return o}({1:[function(t,e,n){"use strict";var r=t("../main");"function"==typeof define&&define.amd?define(r):(window.PerfectScrollbar=r,"undefined"==typeof window.Ps&&(window.Ps=r))},{"../main":7}],2:[function(t,e,n){"use strict";function r(t,e){var n=t.className.split(" ");n.indexOf(e)<0&&n.push(e),t.className=n.join(" ")}function o(t,e){var n=t.className.split(" "),r=n.indexOf(e);r>=0&&n.splice(r,1),t.className=n.join(" ")}n.add=function(t,e){t.classList?t.classList.add(e):r(t,e)},n.remove=function(t,e){t.classList?t.classList.remove(e):o(t,e)},n.list=function(t){return t.classList?Array.prototype.slice.apply(t.classList):t.className.split(" ")}},{}],3:[function(t,e,n){"use strict";function r(t,e){return window.getComputedStyle(t)[e]}function o(t,e,n){return"number"==typeof n&&(n=n.toString()+"px"),t.style[e]=n,t}function l(t,e){for(var n in e){var r=e[n];"number"==typeof r&&(r=r.toString()+"px"),t.style[n]=r}return t}var i={};i.e=function(t,e){var n=document.createElement(t);return n.className=e,n},i.appendTo=function(t,e){return e.appendChild(t),t},i.css=function(t,e,n){return"object"==typeof e?l(t,e):"undefined"==typeof n?r(t,e):o(t,e,n)},i.matches=function(t,e){return"undefined"!=typeof t.matches?t.matches(e):"undefined"!=typeof t.matchesSelector?t.matchesSelector(e):"undefined"!=typeof t.webkitMatchesSelector?t.webkitMatchesSelector(e):"undefined"!=typeof t.mozMatchesSelector?t.mozMatchesSelector(e):"undefined"!=typeof t.msMatchesSelector?t.msMatchesSelector(e):void 0},i.remove=function(t){"undefined"!=typeof t.remove?t.remove():t.parentNode&&t.parentNode.removeChild(t)},i.queryChildren=function(t,e){return Array.prototype.filter.call(t.childNodes,function(t){return i.matches(t,e)})},e.exports=i},{}],4:[function(t,e,n){"use strict";var r=function(t){this.element=t,this.events={}};r.prototype.bind=function(t,e){"undefined"==typeof this.events[t]&&(this.events[t]=[]),this.events[t].push(e),this.element.addEventListener(t,e,!1)},r.prototype.unbind=function(t,e){var n="undefined"!=typeof e;this.events[t]=this.events[t].filter(function(r){return!(!n||r===e)||(this.element.removeEventListener(t,r,!1),!1)},this)},r.prototype.unbindAll=function(){for(var t in this.events)this.unbind(t)};var o=function(){this.eventElements=[]};o.prototype.eventElement=function(t){var e=this.eventElements.filter(function(e){return e.element===t})[0];return"undefined"==typeof e&&(e=new r(t),this.eventElements.push(e)),e},o.prototype.bind=function(t,e,n){this.eventElement(t).bind(e,n)},o.prototype.unbind=function(t,e,n){this.eventElement(t).unbind(e,n)},o.prototype.unbindAll=function(){for(var t=0;t<this.eventElements.length;t++)this.eventElements[t].unbindAll()},o.prototype.once=function(t,e,n){var r=this.eventElement(t),o=function(t){r.unbind(e,o),n(t)};r.bind(e,o)},e.exports=o},{}],5:[function(t,e,n){"use strict";e.exports=function(){function t(){return Math.floor(65536*(1+Math.random())).toString(16).substring(1)}return function(){return t()+t()+"-"+t()+"-"+t()+"-"+t()+"-"+t()+t()+t()}}()},{}],6:[function(t,e,n){"use strict";var r=t("./class"),o=t("./dom"),l=n.toInt=function(t){return parseInt(t,10)||0},i=n.clone=function(t){if(t){if(t.constructor===Array)return t.map(i);if("object"==typeof t){var e={};for(var n in t)e[n]=i(t[n]);return e}return t}return null};n.extend=function(t,e){var n=i(t);for(var r in e)n[r]=i(e[r]);return n},n.isEditable=function(t){return o.matches(t,"input,[contenteditable]")||o.matches(t,"select,[contenteditable]")||o.matches(t,"textarea,[contenteditable]")||o.matches(t,"button,[contenteditable]")},n.removePsClasses=function(t){for(var e=r.list(t),n=0;n<e.length;n++){var o=e[n];0===o.indexOf("ps-")&&r.remove(t,o)}},n.outerWidth=function(t){return l(o.css(t,"width"))+l(o.css(t,"paddingLeft"))+l(o.css(t,"paddingRight"))+l(o.css(t,"borderLeftWidth"))+l(o.css(t,"borderRightWidth"))},n.startScrolling=function(t,e){r.add(t,"ps-in-scrolling"),"undefined"!=typeof e?r.add(t,"ps-"+e):(r.add(t,"ps-x"),r.add(t,"ps-y"))},n.stopScrolling=function(t,e){r.remove(t,"ps-in-scrolling"),"undefined"!=typeof e?r.remove(t,"ps-"+e):(r.remove(t,"ps-x"),r.remove(t,"ps-y"))},n.env={isWebKit:"WebkitAppearance"in document.documentElement.style,supportsTouch:"ontouchstart"in window||window.DocumentTouch&&document instanceof window.DocumentTouch,supportsIePointer:null!==window.navigator.msMaxTouchPoints}},{"./class":2,"./dom":3}],7:[function(t,e,n){"use strict";var r=t("./plugin/destroy"),o=t("./plugin/initialize"),l=t("./plugin/update");e.exports={initialize:o,update:l,destroy:r}},{"./plugin/destroy":9,"./plugin/initialize":17,"./plugin/update":21}],8:[function(t,e,n){"use strict";e.exports={handlers:["click-rail","drag-scrollbar","keyboard","wheel","touch"],maxScrollbarLength:null,minScrollbarLength:null,scrollXMarginOffset:0,scrollYMarginOffset:0,suppressScrollX:!1,suppressScrollY:!1,swipePropagation:!0,useBothWheelAxes:!1,wheelPropagation:!1,wheelSpeed:1,theme:"default"}},{}],9:[function(t,e,n){"use strict";var r=t("../lib/helper"),o=t("../lib/dom"),l=t("./instances");e.exports=function(t){var e=l.get(t);e&&(e.event.unbindAll(),o.remove(e.scrollbarX),o.remove(e.scrollbarY),o.remove(e.scrollbarXRail),o.remove(e.scrollbarYRail),r.removePsClasses(t),l.remove(t))}},{"../lib/dom":3,"../lib/helper":6,"./instances":18}],10:[function(t,e,n){"use strict";function r(t,e){function n(t){return t.getBoundingClientRect()}var r=function(t){t.stopPropagation()};e.event.bind(e.scrollbarY,"click",r),e.event.bind(e.scrollbarYRail,"click",function(r){var o=r.pageY-window.pageYOffset-n(e.scrollbarYRail).top,s=o>e.scrollbarYTop?1:-1;i(t,"top",t.scrollTop+s*e.containerHeight),l(t),r.stopPropagation()}),e.event.bind(e.scrollbarX,"click",r),e.event.bind(e.scrollbarXRail,"click",function(r){var o=r.pageX-window.pageXOffset-n(e.scrollbarXRail).left,s=o>e.scrollbarXLeft?1:-1;i(t,"left",t.scrollLeft+s*e.containerWidth),l(t),r.stopPropagation()})}var o=t("../instances"),l=t("../update-geometry"),i=t("../update-scroll");e.exports=function(t){var e=o.get(t);r(t,e)}},{"../instances":18,"../update-geometry":19,"../update-scroll":20}],11:[function(t,e,n){"use strict";function r(t,e){function n(n){var o=r+n*e.railXRatio,i=Math.max(0,e.scrollbarXRail.getBoundingClientRect().left)+e.railXRatio*(e.railXWidth-e.scrollbarXWidth);o<0?e.scrollbarXLeft=0:o>i?e.scrollbarXLeft=i:e.scrollbarXLeft=o;var s=l.toInt(e.scrollbarXLeft*(e.contentWidth-e.containerWidth)/(e.containerWidth-e.railXRatio*e.scrollbarXWidth))-e.negativeScrollAdjustment;c(t,"left",s)}var r=null,o=null,s=function(e){n(e.pageX-o),a(t),e.stopPropagation(),e.preventDefault()},u=function(){l.stopScrolling(t,"x"),e.event.unbind(e.ownerDocument,"mousemove",s)};e.event.bind(e.scrollbarX,"mousedown",function(n){o=n.pageX,r=l.toInt(i.css(e.scrollbarX,"left"))*e.railXRatio,l.startScrolling(t,"x"),e.event.bind(e.ownerDocument,"mousemove",s),e.event.once(e.ownerDocument,"mouseup",u),n.stopPropagation(),n.preventDefault()})}function o(t,e){function n(n){var o=r+n*e.railYRatio,i=Math.max(0,e.scrollbarYRail.getBoundingClientRect().top)+e.railYRatio*(e.railYHeight-e.scrollbarYHeight);o<0?e.scrollbarYTop=0:o>i?e.scrollbarYTop=i:e.scrollbarYTop=o;var s=l.toInt(e.scrollbarYTop*(e.contentHeight-e.containerHeight)/(e.containerHeight-e.railYRatio*e.scrollbarYHeight));c(t,"top",s)}var r=null,o=null,s=function(e){n(e.pageY-o),a(t),e.stopPropagation(),e.preventDefault()},u=function(){l.stopScrolling(t,"y"),e.event.unbind(e.ownerDocument,"mousemove",s)};e.event.bind(e.scrollbarY,"mousedown",function(n){o=n.pageY,r=l.toInt(i.css(e.scrollbarY,"top"))*e.railYRatio,l.startScrolling(t,"y"),e.event.bind(e.ownerDocument,"mousemove",s),e.event.once(e.ownerDocument,"mouseup",u),n.stopPropagation(),n.preventDefault()})}var l=t("../../lib/helper"),i=t("../../lib/dom"),s=t("../instances"),a=t("../update-geometry"),c=t("../update-scroll");e.exports=function(t){var e=s.get(t);r(t,e),o(t,e)}},{"../../lib/dom":3,"../../lib/helper":6,"../instances":18,"../update-geometry":19,"../update-scroll":20}],12:[function(t,e,n){"use strict";function r(t,e){function n(n,r){var o=t.scrollTop;if(0===n){if(!e.scrollbarYActive)return!1;if(0===o&&r>0||o>=e.contentHeight-e.containerHeight&&r<0)return!e.settings.wheelPropagation}var l=t.scrollLeft;if(0===r){if(!e.scrollbarXActive)return!1;if(0===l&&n<0||l>=e.contentWidth-e.containerWidth&&n>0)return!e.settings.wheelPropagation}return!0}var r=!1;e.event.bind(t,"mouseenter",function(){r=!0}),e.event.bind(t,"mouseleave",function(){r=!1});var i=!1;e.event.bind(e.ownerDocument,"keydown",function(c){if(!(c.isDefaultPrevented&&c.isDefaultPrevented()||c.defaultPrevented)){var u=l.matches(e.scrollbarX,":focus")||l.matches(e.scrollbarY,":focus");if(r||u){var d=document.activeElement?document.activeElement:e.ownerDocument.activeElement;if(d){if("IFRAME"===d.tagName)d=d.contentDocument.activeElement;else for(;d.shadowRoot;)d=d.shadowRoot.activeElement;if(o.isEditable(d))return}var p=0,f=0;switch(c.which){case 37:p=c.metaKey?-e.contentWidth:c.altKey?-e.containerWidth:-30;break;case 38:f=c.metaKey?e.contentHeight:c.altKey?e.containerHeight:30;break;case 39:p=c.metaKey?e.contentWidth:c.altKey?e.containerWidth:30;break;case 40:f=c.metaKey?-e.contentHeight:c.altKey?-e.containerHeight:-30;break;case 33:f=90;break;case 32:f=c.shiftKey?90:-90;break;case 34:f=-90;break;case 35:f=c.ctrlKey?-e.contentHeight:-e.containerHeight;break;case 36:f=c.ctrlKey?t.scrollTop:e.containerHeight;break;default:return}a(t,"top",t.scrollTop-f),a(t,"left",t.scrollLeft+p),s(t),i=n(p,f),i&&c.preventDefault()}}})}var o=t("../../lib/helper"),l=t("../../lib/dom"),i=t("../instances"),s=t("../update-geometry"),a=t("../update-scroll");e.exports=function(t){var e=i.get(t);r(t,e)}},{"../../lib/dom":3,"../../lib/helper":6,"../instances":18,"../update-geometry":19,"../update-scroll":20}],13:[function(t,e,n){"use strict";function r(t,e){function n(n,r){var o=t.scrollTop;if(0===n){if(!e.scrollbarYActive)return!1;if(0===o&&r>0||o>=e.contentHeight-e.containerHeight&&r<0)return!e.settings.wheelPropagation}var l=t.scrollLeft;if(0===r){if(!e.scrollbarXActive)return!1;if(0===l&&n<0||l>=e.contentWidth-e.containerWidth&&n>0)return!e.settings.wheelPropagation}return!0}function r(t){var e=t.deltaX,n=-1*t.deltaY;return"undefined"!=typeof e&&"undefined"!=typeof n||(e=-1*t.wheelDeltaX/6,n=t.wheelDeltaY/6),t.deltaMode&&1===t.deltaMode&&(e*=10,n*=10),e!==e&&n!==n&&(e=0,n=t.wheelDelta),t.shiftKey?[-n,-e]:[e,n]}function o(e,n){var r=t.querySelector("textarea:hover, select[multiple]:hover, .ps-child:hover");if(r){if(!window.getComputedStyle(r).overflow.match(/(scroll|auto)/))return!1;var o=r.scrollHeight-r.clientHeight;if(o>0&&!(0===r.scrollTop&&n>0||r.scrollTop===o&&n<0))return!0;var l=r.scrollLeft-r.clientWidth;if(l>0&&!(0===r.scrollLeft&&e<0||r.scrollLeft===l&&e>0))return!0}return!1}function s(s){var c=r(s),u=c[0],d=c[1];o(u,d)||(a=!1,e.settings.useBothWheelAxes?e.scrollbarYActive&&!e.scrollbarXActive?(d?i(t,"top",t.scrollTop-d*e.settings.wheelSpeed):i(t,"top",t.scrollTop+u*e.settings.wheelSpeed),a=!0):e.scrollbarXActive&&!e.scrollbarYActive&&(u?i(t,"left",t.scrollLeft+u*e.settings.wheelSpeed):i(t,"left",t.scrollLeft-d*e.settings.wheelSpeed),a=!0):(i(t,"top",t.scrollTop-d*e.settings.wheelSpeed),i(t,"left",t.scrollLeft+u*e.settings.wheelSpeed)),l(t),a=a||n(u,d),a&&(s.stopPropagation(),s.preventDefault()))}var a=!1;"undefined"!=typeof window.onwheel?e.event.bind(t,"wheel",s):"undefined"!=typeof window.onmousewheel&&e.event.bind(t,"mousewheel",s)}var o=t("../instances"),l=t("../update-geometry"),i=t("../update-scroll");e.exports=function(t){var e=o.get(t);r(t,e)}},{"../instances":18,"../update-geometry":19,"../update-scroll":20}],14:[function(t,e,n){"use strict";function r(t,e){e.event.bind(t,"scroll",function(){l(t)})}var o=t("../instances"),l=t("../update-geometry");e.exports=function(t){var e=o.get(t);r(t,e)}},{"../instances":18,"../update-geometry":19}],15:[function(t,e,n){"use strict";function r(t,e){function n(){var t=window.getSelection?window.getSelection():document.getSelection?document.getSelection():"";return 0===t.toString().length?null:t.getRangeAt(0).commonAncestorContainer}function r(){c||(c=setInterval(function(){return l.get(t)?(s(t,"top",t.scrollTop+u.top),s(t,"left",t.scrollLeft+u.left),void i(t)):void clearInterval(c)},50))}function a(){c&&(clearInterval(c),c=null),o.stopScrolling(t)}var c=null,u={top:0,left:0},d=!1;e.event.bind(e.ownerDocument,"selectionchange",function(){t.contains(n())?d=!0:(d=!1,a())}),e.event.bind(window,"mouseup",function(){d&&(d=!1,a())}),e.event.bind(window,"keyup",function(){d&&(d=!1,a())}),e.event.bind(window,"mousemove",function(e){if(d){var n={x:e.pageX,y:e.pageY},l={left:t.offsetLeft,right:t.offsetLeft+t.offsetWidth,top:t.offsetTop,bottom:t.offsetTop+t.offsetHeight};n.x<l.left+3?(u.left=-5,o.startScrolling(t,"x")):n.x>l.right-3?(u.left=5,o.startScrolling(t,"x")):u.left=0,n.y<l.top+3?(l.top+3-n.y<5?u.top=-5:u.top=-20,o.startScrolling(t,"y")):n.y>l.bottom-3?(n.y-l.bottom+3<5?u.top=5:u.top=20,o.startScrolling(t,"y")):u.top=0,0===u.top&&0===u.left?a():r()}})}var o=t("../../lib/helper"),l=t("../instances"),i=t("../update-geometry"),s=t("../update-scroll");e.exports=function(t){var e=l.get(t);r(t,e)}},{"../../lib/helper":6,"../instances":18,"../update-geometry":19,"../update-scroll":20}],16:[function(t,e,n){"use strict";function r(t,e,n,r){function o(n,r){var o=t.scrollTop,l=t.scrollLeft,i=Math.abs(n),s=Math.abs(r);if(s>i){if(r<0&&o===e.contentHeight-e.containerHeight||r>0&&0===o)return!e.settings.swipePropagation}else if(i>s&&(n<0&&l===e.contentWidth-e.containerWidth||n>0&&0===l))return!e.settings.swipePropagation;return!0}function a(e,n){s(t,"top",t.scrollTop-n),s(t,"left",t.scrollLeft-e),i(t)}function c(){w=!0}function u(){w=!1}function d(t){return t.targetTouches?t.targetTouches[0]:t}function p(t){return!(!t.targetTouches||1!==t.targetTouches.length)||!(!t.pointerType||"mouse"===t.pointerType||t.pointerType===t.MSPOINTER_TYPE_MOUSE)}function f(t){if(p(t)){Y=!0;var e=d(t);g.pageX=e.pageX,g.pageY=e.pageY,v=(new Date).getTime(),null!==y&&clearInterval(y),t.stopPropagation()}}function h(t){if(!Y&&e.settings.swipePropagation&&f(t),!w&&Y&&p(t)){var n=d(t),r={pageX:n.pageX,pageY:n.pageY},l=r.pageX-g.pageX,i=r.pageY-g.pageY;a(l,i),g=r;var s=(new Date).getTime(),c=s-v;c>0&&(m.x=l/c,m.y=i/c,v=s),o(l,i)&&(t.stopPropagation(),t.preventDefault())}}function b(){!w&&Y&&(Y=!1,clearInterval(y),y=setInterval(function(){return l.get(t)&&(m.x||m.y)?Math.abs(m.x)<.01&&Math.abs(m.y)<.01?void clearInterval(y):(a(30*m.x,30*m.y),m.x*=.8,void(m.y*=.8)):void clearInterval(y)},10))}var g={},v=0,m={},y=null,w=!1,Y=!1;n&&(e.event.bind(window,"touchstart",c),e.event.bind(window,"touchend",u),e.event.bind(t,"touchstart",f),e.event.bind(t,"touchmove",h),e.event.bind(t,"touchend",b)),r&&(window.PointerEvent?(e.event.bind(window,"pointerdown",c),e.event.bind(window,"pointerup",u),e.event.bind(t,"pointerdown",f),e.event.bind(t,"pointermove",h),e.event.bind(t,"pointerup",b)):window.MSPointerEvent&&(e.event.bind(window,"MSPointerDown",c),e.event.bind(window,"MSPointerUp",u),e.event.bind(t,"MSPointerDown",f),e.event.bind(t,"MSPointerMove",h),e.event.bind(t,"MSPointerUp",b)))}var o=t("../../lib/helper"),l=t("../instances"),i=t("../update-geometry"),s=t("../update-scroll");e.exports=function(t){if(o.env.supportsTouch||o.env.supportsIePointer){var e=l.get(t);r(t,e,o.env.supportsTouch,o.env.supportsIePointer)}}},{"../../lib/helper":6,"../instances":18,"../update-geometry":19,"../update-scroll":20}],17:[function(t,e,n){"use strict";var r=t("../lib/helper"),o=t("../lib/class"),l=t("./instances"),i=t("./update-geometry"),s={"click-rail":t("./handler/click-rail"),"drag-scrollbar":t("./handler/drag-scrollbar"),keyboard:t("./handler/keyboard"),wheel:t("./handler/mouse-wheel"),touch:t("./handler/touch"),selection:t("./handler/selection")},a=t("./handler/native-scroll");e.exports=function(t,e){e="object"==typeof e?e:{},o.add(t,"ps-container");var n=l.add(t);n.settings=r.extend(n.settings,e),o.add(t,"ps-theme-"+n.settings.theme),n.settings.handlers.forEach(function(e){s[e](t)}),a(t),i(t)}},{"../lib/class":2,"../lib/helper":6,"./handler/click-rail":10,"./handler/drag-scrollbar":11,"./handler/keyboard":12,"./handler/mouse-wheel":13,"./handler/native-scroll":14,"./handler/selection":15,"./handler/touch":16,"./instances":18,"./update-geometry":19}],18:[function(t,e,n){"use strict";function r(t){function e(){a.add(t,"ps-focus")}function n(){a.remove(t,"ps-focus")}var r=this;r.settings=s.clone(c),r.containerWidth=null,r.containerHeight=null,r.contentWidth=null,r.contentHeight=null,r.isRtl="rtl"===u.css(t,"direction"),r.isNegativeScroll=function(){var e=t.scrollLeft,n=null;return t.scrollLeft=-1,n=t.scrollLeft<0,t.scrollLeft=e,n}(),r.negativeScrollAdjustment=r.isNegativeScroll?t.scrollWidth-t.clientWidth:0,r.event=new d,r.ownerDocument=t.ownerDocument||document,r.scrollbarXRail=u.appendTo(u.e("div","ps-scrollbar-x-rail"),t),r.scrollbarX=u.appendTo(u.e("div","ps-scrollbar-x"),r.scrollbarXRail),r.scrollbarX.setAttribute("tabindex",0),r.event.bind(r.scrollbarX,"focus",e),r.event.bind(r.scrollbarX,"blur",n),r.scrollbarXActive=null,r.scrollbarXWidth=null,r.scrollbarXLeft=null,r.scrollbarXBottom=s.toInt(u.css(r.scrollbarXRail,"bottom")),r.isScrollbarXUsingBottom=r.scrollbarXBottom===r.scrollbarXBottom,r.scrollbarXTop=r.isScrollbarXUsingBottom?null:s.toInt(u.css(r.scrollbarXRail,"top")),r.railBorderXWidth=s.toInt(u.css(r.scrollbarXRail,"borderLeftWidth"))+s.toInt(u.css(r.scrollbarXRail,"borderRightWidth")),u.css(r.scrollbarXRail,"display","block"),r.railXMarginWidth=s.toInt(u.css(r.scrollbarXRail,"marginLeft"))+s.toInt(u.css(r.scrollbarXRail,"marginRight")),u.css(r.scrollbarXRail,"display",""),r.railXWidth=null,r.railXRatio=null,r.scrollbarYRail=u.appendTo(u.e("div","ps-scrollbar-y-rail"),t),r.scrollbarY=u.appendTo(u.e("div","ps-scrollbar-y"),r.scrollbarYRail),r.scrollbarY.setAttribute("tabindex",0),r.event.bind(r.scrollbarY,"focus",e),r.event.bind(r.scrollbarY,"blur",n),r.scrollbarYActive=null,r.scrollbarYHeight=null,r.scrollbarYTop=null,r.scrollbarYRight=s.toInt(u.css(r.scrollbarYRail,"right")),r.isScrollbarYUsingRight=r.scrollbarYRight===r.scrollbarYRight,r.scrollbarYLeft=r.isScrollbarYUsingRight?null:s.toInt(u.css(r.scrollbarYRail,"left")),r.scrollbarYOuterWidth=r.isRtl?s.outerWidth(r.scrollbarY):null,r.railBorderYWidth=s.toInt(u.css(r.scrollbarYRail,"borderTopWidth"))+s.toInt(u.css(r.scrollbarYRail,"borderBottomWidth")),u.css(r.scrollbarYRail,"display","block"),r.railYMarginHeight=s.toInt(u.css(r.scrollbarYRail,"marginTop"))+s.toInt(u.css(r.scrollbarYRail,"marginBottom")),u.css(r.scrollbarYRail,"display",""),r.railYHeight=null,r.railYRatio=null}function o(t){return t.getAttribute("data-ps-id")}function l(t,e){t.setAttribute("data-ps-id",e)}function i(t){t.removeAttribute("data-ps-id")}var s=t("../lib/helper"),a=t("../lib/class"),c=t("./default-setting"),u=t("../lib/dom"),d=t("../lib/event-manager"),p=t("../lib/guid"),f={};n.add=function(t){var e=p();return l(t,e),f[e]=new r(t),f[e]},n.remove=function(t){delete f[o(t)],i(t)},n.get=function(t){return f[o(t)]}},{"../lib/class":2,"../lib/dom":3,"../lib/event-manager":4,"../lib/guid":5,"../lib/helper":6,"./default-setting":8}],19:[function(t,e,n){"use strict";function r(t,e){return t.settings.minScrollbarLength&&(e=Math.max(e,t.settings.minScrollbarLength)),t.settings.maxScrollbarLength&&(e=Math.min(e,t.settings.maxScrollbarLength)),e}function o(t,e){var n={width:e.railXWidth};e.isRtl?n.left=e.negativeScrollAdjustment+t.scrollLeft+e.containerWidth-e.contentWidth:n.left=t.scrollLeft,e.isScrollbarXUsingBottom?n.bottom=e.scrollbarXBottom-t.scrollTop:n.top=e.scrollbarXTop+t.scrollTop,s.css(e.scrollbarXRail,n);var r={top:t.scrollTop,height:e.railYHeight};e.isScrollbarYUsingRight?e.isRtl?r.right=e.contentWidth-(e.negativeScrollAdjustment+t.scrollLeft)-e.scrollbarYRight-e.scrollbarYOuterWidth:r.right=e.scrollbarYRight-t.scrollLeft:e.isRtl?r.left=e.negativeScrollAdjustment+t.scrollLeft+2*e.containerWidth-e.contentWidth-e.scrollbarYLeft-e.scrollbarYOuterWidth:r.left=e.scrollbarYLeft+t.scrollLeft,s.css(e.scrollbarYRail,r),s.css(e.scrollbarX,{left:e.scrollbarXLeft,width:e.scrollbarXWidth-e.railBorderXWidth}),s.css(e.scrollbarY,{top:e.scrollbarYTop,height:e.scrollbarYHeight-e.railBorderYWidth})}var l=t("../lib/helper"),i=t("../lib/class"),s=t("../lib/dom"),a=t("./instances"),c=t("./update-scroll");e.exports=function(t){var e=a.get(t);e.containerWidth=t.clientWidth,e.containerHeight=t.clientHeight,e.contentWidth=t.scrollWidth,e.contentHeight=t.scrollHeight;var n;t.contains(e.scrollbarXRail)||(n=s.queryChildren(t,".ps-scrollbar-x-rail"),n.length>0&&n.forEach(function(t){s.remove(t)}),s.appendTo(e.scrollbarXRail,t)),t.contains(e.scrollbarYRail)||(n=s.queryChildren(t,".ps-scrollbar-y-rail"),n.length>0&&n.forEach(function(t){s.remove(t)}),s.appendTo(e.scrollbarYRail,t)),!e.settings.suppressScrollX&&e.containerWidth+e.settings.scrollXMarginOffset<e.contentWidth?(e.scrollbarXActive=!0,e.railXWidth=e.containerWidth-e.railXMarginWidth,e.railXRatio=e.containerWidth/e.railXWidth,e.scrollbarXWidth=r(e,l.toInt(e.railXWidth*e.containerWidth/e.contentWidth)),e.scrollbarXLeft=l.toInt((e.negativeScrollAdjustment+t.scrollLeft)*(e.railXWidth-e.scrollbarXWidth)/(e.contentWidth-e.containerWidth))):e.scrollbarXActive=!1,!e.settings.suppressScrollY&&e.containerHeight+e.settings.scrollYMarginOffset<e.contentHeight?(e.scrollbarYActive=!0,e.railYHeight=e.containerHeight-e.railYMarginHeight,e.railYRatio=e.containerHeight/e.railYHeight,e.scrollbarYHeight=r(e,l.toInt(e.railYHeight*e.containerHeight/e.contentHeight)),e.scrollbarYTop=l.toInt(t.scrollTop*(e.railYHeight-e.scrollbarYHeight)/(e.contentHeight-e.containerHeight))):e.scrollbarYActive=!1,e.scrollbarXLeft>=e.railXWidth-e.scrollbarXWidth&&(e.scrollbarXLeft=e.railXWidth-e.scrollbarXWidth),e.scrollbarYTop>=e.railYHeight-e.scrollbarYHeight&&(e.scrollbarYTop=e.railYHeight-e.scrollbarYHeight),o(t,e),e.scrollbarXActive?i.add(t,"ps-active-x"):(i.remove(t,"ps-active-x"),e.scrollbarXWidth=0,e.scrollbarXLeft=0,c(t,"left",0)),e.scrollbarYActive?i.add(t,"ps-active-y"):(i.remove(t,"ps-active-y"),e.scrollbarYHeight=0,e.scrollbarYTop=0,c(t,"top",0))}},{"../lib/class":2,"../lib/dom":3,"../lib/helper":6,"./instances":18,"./update-scroll":20}],20:[function(t,e,n){"use strict";var r,o,l=t("./instances"),i=function(t){var e=document.createEvent("Event");return e.initEvent(t,!0,!0),e};e.exports=function(t,e,n){if("undefined"==typeof t)throw"You must provide an element to the update-scroll function";if("undefined"==typeof e)throw"You must provide an axis to the update-scroll function";if("undefined"==typeof n)throw"You must provide a value to the update-scroll function";"top"===e&&n<=0&&(t.scrollTop=n=0,t.dispatchEvent(i("ps-y-reach-start"))),"left"===e&&n<=0&&(t.scrollLeft=n=0,t.dispatchEvent(i("ps-x-reach-start")));var s=l.get(t);"top"===e&&n>=s.contentHeight-s.containerHeight&&(n=s.contentHeight-s.containerHeight,n-t.scrollTop<=1?n=t.scrollTop:t.scrollTop=n,t.dispatchEvent(i("ps-y-reach-end"))),"left"===e&&n>=s.contentWidth-s.containerWidth&&(n=s.contentWidth-s.containerWidth,n-t.scrollLeft<=1?n=t.scrollLeft:t.scrollLeft=n,t.dispatchEvent(i("ps-x-reach-end"))),r||(r=t.scrollTop),o||(o=t.scrollLeft),"top"===e&&n<r&&t.dispatchEvent(i("ps-scroll-up")),"top"===e&&n>r&&t.dispatchEvent(i("ps-scroll-down")),"left"===e&&n<o&&t.dispatchEvent(i("ps-scroll-left")),"left"===e&&n>o&&t.dispatchEvent(i("ps-scroll-right")),"top"===e&&(t.scrollTop=r=n,t.dispatchEvent(i("ps-scroll-y"))),"left"===e&&(t.scrollLeft=o=n,t.dispatchEvent(i("ps-scroll-x")))}},{"./instances":18}],21:[function(t,e,n){"use strict";var r=t("../lib/helper"),o=t("../lib/dom"),l=t("./instances"),i=t("./update-geometry"),s=t("./update-scroll");e.exports=function(t){var e=l.get(t);e&&(e.negativeScrollAdjustment=e.isNegativeScroll?t.scrollWidth-t.clientWidth:0,o.css(e.scrollbarXRail,"display","block"),o.css(e.scrollbarYRail,"display","block"),e.railXMarginWidth=r.toInt(o.css(e.scrollbarXRail,"marginLeft"))+r.toInt(o.css(e.scrollbarXRail,"marginRight")),e.railYMarginHeight=r.toInt(o.css(e.scrollbarYRail,"marginTop"))+r.toInt(o.css(e.scrollbarYRail,"marginBottom")),o.css(e.scrollbarXRail,"display","none"),o.css(e.scrollbarYRail,"display","none"),i(t),s(t,"top",t.scrollTop),s(t,"left",t.scrollLeft),o.css(e.scrollbarXRail,"display",""),o.css(e.scrollbarYRail,"display",""))}},{"../lib/dom":3,"../lib/helper":6,"./instances":18,"./update-geometry":19,"./update-scroll":20}]},{},[1]);
\ No newline at end of file diff --git a/web/netdata-swagger.json b/web/netdata-swagger.json index c2ed74d44..ad424abad 100644 --- a/web/netdata-swagger.json +++ b/web/netdata-swagger.json @@ -3,7 +3,7 @@ "info": { "title": "NetData API", "description": "Real time data collection and graphs...", - "version": "1.2.1_master" + "version": "1.4.1_master" }, "host": "registry.my-netdata.io", "schemes": [ @@ -90,7 +90,7 @@ { "name": "after", "in": "query", - "description": "This parameter can either be an absolute timestamp specifying the starting point of the data to be returned, or a relative number of seconds, to the last collected timestamp. Netdata will assume it is a relative number if it is smaller than the duration of the round robin database for this chart. So, if the round robin database is 3600 seconds, any value from -3600 to 3600 will trigger relative arithmetics. Netdata will adapt this parameter to the boundaries of the round robin database.", + "description": "This parameter can either be an absolute timestamp specifying the starting point of the data to be returned, or a relative number of seconds (negative, relative to parameter: before). Netdata will assume it is a relative number if it is less that 3 years (in seconds). Netdata will adapt this parameter to the boundaries of the round robin database. The default is the beginning of the round robin database (i.e. by default netdata will attempt to return data for the entire database).", "required": true, "type": "number", "format": "integer", @@ -100,7 +100,7 @@ { "name": "before", "in": "query", - "description": "This parameter can either be an absolute timestamp specifying the ending point of the data to be returned, or a relative number of seconds, to the last collected timestamp. Netdata will assume it is a relative number if it is smaller than the duration of the round robin database for this chart. So, if the round robin database is 3600 seconds, any value from -3600 to 3600 will trigger relative arithmetics. Netdata will adapt this parameter to the boundaries of the round robin database.", + "description": "This parameter can either be an absolute timestamp specifying the ending point of the data to be returned, or a relative number of seconds (negative), relative to the last collected timestamp. Netdata will assume it is a relative number if it is less than 3 years (in seconds). Netdata will adapt this parameter to the boundaries of the round robin database. The default is zero (i.e. the timestamp of the last value collected).", "required": false, "type": "number", "format": "integer", @@ -109,7 +109,7 @@ { "name": "points", "in": "query", - "description": "The number of points to be returned. If not given, or it is <= 0, or it is bigger than the points stored in the round robin database for this chart for the given duration, all the available collected values for the given duration are returned.", + "description": "The number of points to be returned. If not given, or it is <= 0, or it is bigger than the points stored in the round robin database for this chart for the given duration, all the available collected values for the given duration will be returned.", "required": true, "type": "number", "format": "integer", @@ -119,12 +119,15 @@ { "name": "group", "in": "query", - "description": "The grouping method. If multiple collected values are to be grouped in order to return fewer points, this parameters defines the method of grouping. Two methods are supported, \"max\" and \"average\". \"max\" is actually calculated on the absolute value collected (so it works for both positive and negative dimesions to return the most extreme value in either direction).", + "description": "The grouping method. If multiple collected values are to be grouped in order to return fewer points, this parameters defines the method of grouping. methods supported \"min\", \"max\", \"average\", \"sum\", \"incremental-sum\". \"max\" is actually calculated on the absolute value collected (so it works for both positive and negative dimesions to return the most extreme value in either direction).", "required": true, "type": "string", "enum": [ + "min", "max", - "average" + "average", + "sum", + "incremental-sum" ], "default": "average", "allowEmptyValue": false @@ -173,7 +176,8 @@ "null2zero", "objectrows", "google_json", - "percentage" + "percentage", + "unaligned" ], "collectionFormat": "pipes" }, @@ -243,6 +247,15 @@ "default": "system.cpu" }, { + "name": "alarm", + "in": "query", + "description": "the name of an alarm linked to the chart", + "required": false, + "type": "string", + "format": "any text", + "allowEmptyValue": true + }, + { "name": "dimension", "in": "query", "description": "zero, one or more dimension ids, as returned by the /chart call.", @@ -275,24 +288,17 @@ "default": 0 }, { - "name": "points", - "in": "query", - "description": "The number of points to be returned. If not given, or it is <= 0, or it is bigger than the points stored in the round robin database for this chart for the given duration, all the available collected values for the given duration are returned.", - "required": true, - "type": "number", - "format": "integer", - "allowEmptyValue": false, - "default": 20 - }, - { "name": "group", "in": "query", - "description": "The grouping method. If multiple collected values are to be grouped in order to return fewer points, this parameters defines the method of grouping. Two methods are supported, \"max\" and \"average\". \"max\" is actually calculated on the absolute value collected (so it works for both positive and negative dimesions to return the most extreme value in either direction).", + "description": "The grouping method. If multiple collected values are to be grouped in order to return fewer points, this parameters defines the method of grouping. methods are supported \"min\", \"max\", \"average\", \"sum\", \"incremental-sum\". \"max\" is actually calculated on the absolute value collected (so it works for both positive and negative dimesions to return the most extreme value in either direction).", "required": true, "type": "string", "enum": [ + "min", "max", - "average" + "average", + "sum", + "incremental-sum" ], "default": "average", "allowEmptyValue": false @@ -306,13 +312,12 @@ "items": { "type": "string", "enum": [ - "nonzero", - "flip", "abs", "absolute", "absolute-sum", "null2zero", - "percentage" + "percentage", + "unaligned" ], "collectionFormat": "pipes" }, diff --git a/web/netdata-swagger.yaml b/web/netdata-swagger.yaml index 370ebd246..00a038ce7 100644 --- a/web/netdata-swagger.yaml +++ b/web/netdata-swagger.yaml @@ -2,7 +2,7 @@ swagger: '2.0' info: title: NetData API description: 'Real time data collection and graphs...' - version: 1.2.1_master + version: 1.4.1_master host: registry.my-netdata.io schemes: - http @@ -66,7 +66,7 @@ paths: allowEmptyValue: false - name: after in: query - description: 'This parameter can either be an absolute timestamp specifying the starting point of the data to be returned, or a relative number of seconds (relative to parameter: before). Netdata will assume it is a relative number if it is smaller than the duration of the round robin database for this chart. So, if the round robin database is 3600 seconds, any value from -3600 to 3600 will trigger relative arithmetics. Netdata will adapt this parameter to the boundaries of the round robin database.' + description: 'This parameter can either be an absolute timestamp specifying the starting point of the data to be returned, or a relative number of seconds (negative, relative to parameter: before). Netdata will assume it is a relative number if it is less that 3 years (in seconds). Netdata will adapt this parameter to the boundaries of the round robin database. The default is the beginning of the round robin database (i.e. by default netdata will attempt to return data for the entire database).' required: true type: number format: integer @@ -74,14 +74,14 @@ paths: default: -600 - name: before in: query - description: 'This parameter can either be an absolute timestamp specifying the ending point of the data to be returned, or a relative number of seconds, to the last collected timestamp. Netdata will assume it is a relative number if it is smaller than the duration of the round robin database for this chart. So, if the round robin database is 3600 seconds, any value from -3600 to 3600 will trigger relative arithmetics. Netdata will adapt this parameter to the boundaries of the round robin database.' + description: 'This parameter can either be an absolute timestamp specifying the ending point of the data to be returned, or a relative number of seconds (negative), relative to the last collected timestamp. Netdata will assume it is a relative number if it is less than 3 years (in seconds). Netdata will adapt this parameter to the boundaries of the round robin database. The default is zero (i.e. the timestamp of the last value collected).' required: false type: number format: integer default: 0 - name: points in: query - description: 'The number of points to be returned. If not given, or it is <= 0, or it is bigger than the points stored in the round robin database for this chart for the given duration, all the available collected values for the given duration are returned.' + description: 'The number of points to be returned. If not given, or it is <= 0, or it is bigger than the points stored in the round robin database for this chart for the given duration, all the available collected values for the given duration will be returned.' required: true type: number format: integer @@ -259,6 +259,23 @@ paths: description: 'No chart with the given id is found.' '500': description: 'Internal server error. This usually means the server is out of memory.' + /allmetrics: + get: + summary: 'Get a value of all the metrics maintained by netdata' + description: 'The charts endpoint returns the latest value of all charts and dimensions stored in the netdata server.' + parameters: + - name: format + in: query + description: 'The format of the response to be returned' + required: true + type: string + enum: [ 'shell', 'prometheus' ] + default: 'shell' + responses: + '200': + description: 'All the metrics returned in the format requested' + '400': + description: 'The format requested is not supported' definitions: chart_summary: type: object diff --git a/web/registry.html b/web/registry.html index dde481fb1..3af60a84b 100644 --- a/web/registry.html +++ b/web/registry.html @@ -12,7 +12,7 @@ <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent"> <meta property="og:locale" content="en_US" /> - <meta property="og:image" content="https://my-netdata.io/images/seo-performance-512.png"/> + <meta property="og:image" content="https://cloud.githubusercontent.com/assets/2662304/19168687/f6a567be-8c19-11e6-8561-ce8d589e8346.gif"/> <meta property="og:url" content="http://my-netdata.io/"/> <meta property="og:type" content="website"/> <meta property="og:site_name" content="netdata"/> @@ -169,7 +169,7 @@ var netdataRegistryCallback = function(machines_array) { and that you have chown it to be owned by netdata:netdata --> <!-- <script type="text/javascript" src="http://my.server:19999/dashboard.js"></script> --> -<script type="text/javascript" src="dashboard.js?v20161004-1"></script> +<script type="text/javascript" src="dashboard.js?v20170105-7"></script> <script> // Set options for TV operation diff --git a/web/robots.txt b/web/robots.txt index 1f53798bb..e434d9c2b 100644 --- a/web/robots.txt +++ b/web/robots.txt @@ -1,2 +1,7 @@ User-agent: * +Allow: /$ +Allow: /index.html +Allow: /default.html +Allow: /demosites.html +Allow: /sitemap.xml Disallow: / diff --git a/web/sitemap.xml b/web/sitemap.xml new file mode 100644 index 000000000..ba44033b2 --- /dev/null +++ b/web/sitemap.xml @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="UTF-8"?> +<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"> + <url> + <loc>https://my-netdata.io/</loc> + <lastmod>2017-01-02</lastmod> + <changefreq>always</changefreq> + </url> +</urlset> diff --git a/web/tv.html b/web/tv.html index a73cf747c..bd55e852f 100644 --- a/web/tv.html +++ b/web/tv.html @@ -12,7 +12,7 @@ <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent"> <meta property="og:locale" content="en_US" /> - <meta property="og:image" content="https://my-netdata.io/images/seo-performance-512.png"/> + <meta property="og:image" content="https://cloud.githubusercontent.com/assets/2662304/19168687/f6a567be-8c19-11e6-8561-ce8d589e8346.gif"/> <meta property="og:url" content="http://my-netdata.io/"/> <meta property="og:type" content="website"/> <meta property="og:site_name" content="netdata"/> @@ -50,7 +50,7 @@ var netdataTheme = 'slate'; // this is dark and that you have chown it to be owned by netdata:netdata --> <!-- <script type="text/javascript" src="http://my.server:19999/dashboard.js"></script> --> -<script type="text/javascript" src="dashboard.js?v20161004-1"></script> +<script type="text/javascript" src="dashboard.js?v20170105-7"></script> <script> // Set options for TV operation @@ -99,6 +99,7 @@ setTimeout(function(){ data-width="49%" data-height="100%" data-after="-300" + data-dygraph-valuerange="[0, 100]" ></div> <div data-netdata="system.cpu" data-title="CPU usage of your netdata server" @@ -106,6 +107,7 @@ setTimeout(function(){ data-width="49%" data-height="100%" data-after="-300" + data-dygraph-valuerange="[0, 100]" ></div> </div> </div> @@ -118,6 +120,8 @@ setTimeout(function(){ <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-common-max="io" + data-common-min="io" data-title="I/O on registry.my-netdata.io" data-chart-library="dygraph" data-width="49%" @@ -126,6 +130,8 @@ setTimeout(function(){ ></div> <div data-netdata="system.io" data-title="I/O on your netdata server" + data-common-max="io" + data-common-min="io" data-chart-library="dygraph" data-width="49%" data-height="100%" @@ -142,6 +148,8 @@ setTimeout(function(){ <div style="width: 100%; height: calc(100% - 15px); text-align: center; display: inline-block;"> <div data-netdata="system.ipv4" data-host="http://registry.my-netdata.io" + data-common-max="traffic" + data-common-min="traffic" data-title="IPv4 traffic on registry.my-netdata.io" data-chart-library="dygraph" data-width="49%" @@ -150,6 +158,8 @@ setTimeout(function(){ ></div> <div data-netdata="system.ipv4" data-title="IPv4 traffic on your netdata server" + data-common-max="traffic" + data-common-min="traffic" data-chart-library="dygraph" data-width="49%" data-height="100%" @@ -168,6 +178,8 @@ setTimeout(function(){ <br/> <div data-netdata="netdata.requests" data-host="http://registry.my-netdata.io" + data-common-max="netdata-requests" + data-decimal-digits="0" data-title="Chart Refreshes/s" data-chart-library="gauge" data-width="20%" @@ -177,6 +189,8 @@ setTimeout(function(){ ></div> <div data-netdata="netdata.clients" data-host="http://registry.my-netdata.io" + data-common-max="netdata-clients" + data-decimal-digits="0" data-title="Sockets" data-chart-library="gauge" data-width="20%" @@ -187,6 +201,8 @@ setTimeout(function(){ ></div> <div data-netdata="netdata.net" data-dimensions="in" + data-common-max="netdata-net-in" + data-decimal-digits="0" data-host="http://registry.my-netdata.io" data-title="Requests Traffic" data-chart-library="easypiechart" @@ -197,6 +213,8 @@ setTimeout(function(){ ></div> <div data-netdata="netdata.net" data-dimensions="out" + data-common-max="netdata-net-out" + data-decimal-digits="0" data-host="http://registry.my-netdata.io" data-title="Chart Data Traffic" data-chart-library="easypiechart" @@ -211,6 +229,8 @@ setTimeout(function(){ <br/> <div data-netdata="netdata.requests" data-title="Chart Refreshes/s" + data-common-max="netdata-requests" + data-decimal-digits="0" data-chart-library="gauge" data-width="20%" data-height="100%" @@ -218,6 +238,8 @@ setTimeout(function(){ data-points="300" ></div> <div data-netdata="netdata.clients" + data-common-max="netdata-clients" + data-decimal-digits="0" data-title="Sockets" data-chart-library="gauge" data-width="20%" @@ -228,6 +250,8 @@ setTimeout(function(){ ></div> <div data-netdata="netdata.net" data-dimensions="in" + data-common-max="netdata-net-in" + data-decimal-digits="0" data-title="Requests Traffic" data-chart-library="easypiechart" data-width="15%" @@ -237,6 +261,8 @@ setTimeout(function(){ ></div> <div data-netdata="netdata.net" data-dimensions="out" + data-common-max="netdata-net-out" + data-decimal-digits="0" data-title="Chart Data Traffic" data-chart-library="easypiechart" data-width="15%" diff --git a/web/version.txt b/web/version.txt index 926c9edac..58abac87d 100644 --- a/web/version.txt +++ b/web/version.txt @@ -1 +1 @@ -3028b87ee19e8550df6b9decc49733d595e0bd6e +3bd41a09fccccbc6b095805556d3009b9ebf6213 |